Compare commits

...

56 Commits

Author SHA1 Message Date
Bananymous eb9fe8ffc6 LibC: fflush stdout when reading from stdin
This is the *intended behaviour* per ISO C specification
2025-08-05 03:09:24 +03:00
Bananymous 6ccc727dd0 LibC: Add more reasonable values to stack size constants 2025-08-05 03:09:24 +03:00
Bananymous 40d942ca32 LibC: Make sa_data in sock_addr zero sized 2025-08-05 03:09:24 +03:00
Bananymous 44bfb57953 ports/xash3d-fwgs: Don't disable opengl and patch output
Manually link against libxash.so. My current dlopen does not support
loading ELF files that contain TLS
2025-08-05 03:09:24 +03:00
Bananymous 342682d5d5 ports/mesa: Use llvm pipe instead of softpipe if llvm is compiled 2025-08-05 03:09:24 +03:00
Bananymous b586917930 ports: Add llvm port
This can be compiled before mesa to make mesa use llvmpipe instead of
softpipe. llvmpipe is at least 10x faster :D
2025-08-05 03:09:24 +03:00
Bananymous 45ad6082bc DynamicLoader: Support dlopen(NULL, ...) 2025-08-05 03:09:24 +03:00
Bananymous f27823babe Kernel: Move stacks to the top of userspace address space 2025-08-05 03:09:24 +03:00
Bananymous 95cfac471a Kernel: Rename loopback adapter lo0 -> lo 2025-08-05 03:09:24 +03:00
Bananymous f7c1084c3e Kernel: Expose boot command line in /proc/cmdline 2025-08-05 03:09:24 +03:00
Bananymous cf96bb6cc3 Kernel: Add support for multiboot
I don't know why I did it but it works now :D
2025-08-05 03:09:24 +03:00
Bananymous f1369c8fd6 Kernel/LibC: Implement mprotect
There may be some race conditions with this but i think this is good
enough to start with
2025-08-05 03:09:24 +03:00
Bananymous eb7922ab88 LibC: Implement pthread_cond_* using a futex 2025-08-05 03:09:24 +03:00
Bananymous dfdfb7cdaf Kernel: check all threads in validate_pointer_access 2025-08-05 03:09:24 +03:00
Bananymous 1cc0fb9c01 Kernel: Reschedule if idle after IPI
This allows starting thread execution right after thread is received
from load balancing
2025-08-05 03:09:24 +03:00
Bananymous a51b589bc9 Kernel: Allow any signal flags and support SA_RESETHAND 2025-08-05 03:09:24 +03:00
Bananymous 5940e912b3 Kernel/LibC: Implement simple futex 2025-08-05 03:09:24 +03:00
Bananymous 658a001d91 LibC: Make pthread_barrier safe
It used to deadlock and it was not safe if more threads than the target
were attempting to wait on it.
2025-08-05 03:09:24 +03:00
Bananymous 57c9f5a8a8 LibC: lock mutex when pthread_cond_timedwait times out 2025-08-05 03:09:24 +03:00
Bananymous fa7b58057c LibC: Add MAP_ANON as synonym for MAP_ANONYMOUS
This is part of POSIX issue 8 and some things only check MAP_ANON
2025-08-05 03:09:24 +03:00
Bananymous 0e0f5295cf LibC: Make {,__cxa_}atexit thread safe 2025-08-05 03:09:24 +03:00
Bananymous 284c9e5f61 Kernel: Don't kill process if stack pointer is OOB
This can be valid if process is using green threads or for some other
reason using its own stack
2025-08-05 03:09:24 +03:00
Bananymous 927fbda1e8 Kernel: Make on-demand paging thread safe 2025-08-05 03:09:24 +03:00
Bananymous d25a5034db LibC: Update thread id on fork 2025-08-05 03:09:24 +03:00
Bananymous f197d39aaf LibC: Don't allow pthread_join to return EINTR 2025-08-05 03:09:24 +03:00
Bananymous 4a95343936 LibC: Make _get_uthread a macro
This allows nice inlining :)
2025-08-05 03:09:24 +03:00
Bananymous 4e705a91af LibC: Fix pthread keys
I had misunderstood how these are supposed to work :D
2025-08-05 03:09:24 +03:00
Bananymous 82b351469b DynamicLoader: Setup thread id when initializing TLS
This allows pre-libc code use pthread functions

(__cxa_guard_release calls pthread_cond_broadcast)
2025-08-05 03:09:24 +03:00
Bananymous ea91bdcce7 WindowServer: Make relative mouse no-op with absolute mouse 2025-08-05 03:09:24 +03:00
Bananymous 256c9daefd ports/SDL2: Optimize rendering and fix cursor
Remove the unnecessary framebuffer layer, we can just use the window's
own framebuffer.

Set default cursor on mouse initialization, this fixes cursor hiding in
tuxracer

defined SetRelativeMouseMode instead of SetMouseCapture. This is what
the behaviour was :D
2025-08-05 03:09:24 +03:00
Bananymous af0a46e79c LibGUI/WindowServer: Rename mouse capture -> mouse relative
My terminology was incorrect and this is more correct
2025-08-05 03:09:24 +03:00
Bananymous 4519c48284 WindowServer: Fix custom cursors
Custom cursor is now only set if the cursor is in the client area
instead of anyehere in the window. This makes hidden cursor visible
when it is on top of the title bar!
2025-08-05 03:09:24 +03:00
Bananymous 8ea32c7650 WindowServer: Fix relative mouse vertical flip 2025-08-05 03:09:24 +03:00
Bananymous 5972d40ced ports: Update zstd 1.5.6 -> 1.5.7
Also build using cmake because we can :)
2025-08-05 03:09:24 +03:00
Bananymous f35a6b3922 BuildSystem: Fix meson toolchain file
sed to replace SYSROOT did not work because paths contain /

also add cmake to allow cmake based library detection
2025-08-05 03:09:24 +03:00
Bananymous 21009c37b4 BuildSystem: Remove unnecessary variables from cmake toolchain 2025-08-05 03:09:24 +03:00
Bananymous 11a2d15003 BuildSystem: Optimize disk image creation
disk image is now created with rsync so there is no need for copying the
whole sysroot twice.
2025-08-05 03:09:24 +03:00
Bananymous d8a695a88d Kernel: Don't fail ustar unpack when creation fails 2025-08-05 03:09:24 +03:00
Bananymous f82390424b ports: Add cmake port 2025-08-05 03:09:24 +03:00
Bananymous 08ed405a5b ports: Add libuv port 2025-08-05 03:09:24 +03:00
Bananymous 8c598a6902 LibC: Include stdint.h instead of inttypes.h in network headers 2025-08-05 03:09:24 +03:00
Bananymous 8e9c40caa4 LibC: Pump OPEN_MAX to 128
cmake seems to use around 70 when checking compiler. That seems quite a
lot but could be "normal"
2025-07-31 22:47:40 +03:00
Bananymous 8c29036fbf Kernel: Fix EAGAIN on hungup pipe 2025-07-31 22:47:40 +03:00
Bananymous b46337d376 BuildSystem: Add banan-os cmake platform 2025-07-31 22:47:40 +03:00
Bananymous 56d701492b ports/ncurses: Add c++ bindings 2025-07-31 22:47:40 +03:00
Bananymous 07e4e764a0 LibC: Implement mbtowc 2025-07-31 22:47:40 +03:00
Bananymous 66fe2f2e50 LibC: Fix readv and writev 2025-07-31 22:47:40 +03:00
Bananymous fda0ced72e LibC: Implement getpw{nam,uid}_r 2025-07-31 22:47:40 +03:00
Bananymous 654e8bb7f6 LibC: Implement getgr{gid,nam}_r 2025-07-31 22:47:40 +03:00
Bananymous 80ffde5e1e LibC: Implement aligned_malloc
This is part of POSIX issue 8
2025-07-31 22:47:40 +03:00
Bananymous 52309e0754 LibC: Add in6addr_{any,loopback} definitions 2025-07-31 22:47:40 +03:00
Bananymous 31e411f8f1 LibC: Implement recvmsg and sendmsg
These just wrap recvfrom and sendto so ancillary data is not supported
2025-07-31 22:47:40 +03:00
Bananymous de45b760b5 LibC: Implement alphasort and scandir 2025-07-31 22:47:40 +03:00
Bananymous ff29e9c4d6 LibC: Implement {,f}pathconf
This just return minimum values specified by posix
2025-07-31 22:47:40 +03:00
Bananymous cc04bd0f06 LibC/Kernel: Implement ttyname_r 2025-07-31 22:47:40 +03:00
Bananymous e72e1e4e43 LibC: Add _SC_NPROCESSORS_{CONF,ONLN) 2025-07-31 22:47:40 +03:00
93 changed files with 1974 additions and 682 deletions

View File

@ -14,6 +14,23 @@
# multiboot2 header
.section .multiboot, "aw"
.align 8
multiboot_start:
.long 0x1BADB002
.long (1 << 2) # page align modules
.long -(0x1BADB002 + (1 << 2))
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long FB_WIDTH
.long FB_HEIGHT
.long FB_BPP
multiboot_end:
.align 8
multiboot2_start:
.long 0xE85250D6
.long 0

View File

@ -14,6 +14,23 @@
# multiboot2 header
.section .multiboot, "aw"
.align 8
multiboot_start:
.long 0x1BADB002
.long (1 << 2) # page align modules
.long -(0x1BADB002 + (1 << 2))
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long FB_WIDTH
.long FB_HEIGHT
.long FB_BPP
multiboot_end:
.align 8
multiboot2_start:
.long 0xE85250D6
.long 0

View File

@ -16,7 +16,11 @@ namespace Kernel
virtual bool is_partition() const { return false; }
virtual bool is_storage_device() const { return false; }
virtual BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> mmap_region(PageTable&, off_t offset, size_t len, AddressRange, MemoryRegion::Type, PageTable::flags_t) { (void)offset; (void)len; return BAN::Error::from_errno(EINVAL); }
virtual BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> mmap_region(PageTable&, off_t offset, size_t len, AddressRange, MemoryRegion::Type, PageTable::flags_t, int status_flags)
{
(void)offset; (void)len; (void)status_flags;
return BAN::Error::from_errno(ENOTSUP);
}
virtual dev_t rdev() const override = 0;

View File

@ -27,7 +27,7 @@ namespace Kernel
void sync_pixels_linear(uint32_t first_pixel, uint32_t pixel_count);
void sync_pixels_rectangle(uint32_t top_right_x, uint32_t top_right_y, uint32_t width, uint32_t height);
virtual BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> mmap_region(PageTable&, off_t offset, size_t len, AddressRange, MemoryRegion::Type, PageTable::flags_t) override;
virtual BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> mmap_region(PageTable&, off_t offset, size_t len, AddressRange, MemoryRegion::Type, PageTable::flags_t, int status_flags) override;
virtual dev_t rdev() const override { return m_rdev; }
virtual BAN::StringView name() const override { return m_name.sv(); }

View File

@ -22,7 +22,7 @@ namespace Kernel
class FileBackedRegion;
class FileSystem;
class SharedFileData;
struct SharedFileData;
class Inode : public BAN::RefCounted<Inode>
{

View File

@ -27,18 +27,18 @@ namespace Kernel
BAN_NON_MOVABLE(FileBackedRegion);
public:
static BAN::ErrorOr<BAN::UniqPtr<FileBackedRegion>> create(BAN::RefPtr<Inode>, PageTable&, off_t offset, size_t size, AddressRange address_range, Type, PageTable::flags_t);
static BAN::ErrorOr<BAN::UniqPtr<FileBackedRegion>> create(BAN::RefPtr<Inode>, PageTable&, off_t offset, size_t size, AddressRange address_range, Type, PageTable::flags_t, int status_flags);
~FileBackedRegion();
virtual BAN::ErrorOr<void> msync(vaddr_t, size_t, int) override;
BAN::ErrorOr<void> msync(vaddr_t, size_t, int) override;
virtual BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> clone(PageTable& new_page_table) override;
BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> clone(PageTable& new_page_table) override;
protected:
virtual BAN::ErrorOr<bool> allocate_page_containing_impl(vaddr_t vaddr, bool wants_write) override;
BAN::ErrorOr<bool> allocate_page_containing_impl(vaddr_t vaddr, bool wants_write) override;
private:
FileBackedRegion(BAN::RefPtr<Inode>, PageTable&, off_t offset, ssize_t size, Type flags, PageTable::flags_t page_flags);
FileBackedRegion(BAN::RefPtr<Inode>, PageTable&, off_t offset, ssize_t size, Type type, PageTable::flags_t flags, int status_flags);
private:
BAN::RefPtr<Inode> m_inode;

View File

@ -11,22 +11,22 @@ namespace Kernel
BAN_NON_MOVABLE(MemoryBackedRegion);
public:
static BAN::ErrorOr<BAN::UniqPtr<MemoryBackedRegion>> create(PageTable&, size_t size, AddressRange, Type, PageTable::flags_t);
static BAN::ErrorOr<BAN::UniqPtr<MemoryBackedRegion>> create(PageTable&, size_t size, AddressRange, Type, PageTable::flags_t, int status_flags);
~MemoryBackedRegion();
virtual BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> clone(PageTable& new_page_table) override;
BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> clone(PageTable& new_page_table) override;
virtual BAN::ErrorOr<void> msync(vaddr_t, size_t, int) override { return {}; }
BAN::ErrorOr<void> msync(vaddr_t, size_t, int) override { return {}; }
// Copy data from buffer into this region
// This can fail if no memory is mapped and no free memory was available
BAN::ErrorOr<void> copy_data_to_region(size_t offset_into_region, const uint8_t* buffer, size_t buffer_size);
protected:
virtual BAN::ErrorOr<bool> allocate_page_containing_impl(vaddr_t vaddr, bool wants_write) override;
BAN::ErrorOr<bool> allocate_page_containing_impl(vaddr_t vaddr, bool wants_write) override;
private:
MemoryBackedRegion(PageTable&, size_t size, Type, PageTable::flags_t);
MemoryBackedRegion(PageTable&, size_t size, Type, PageTable::flags_t, int status_flags);
};
}

View File

@ -39,6 +39,10 @@ namespace Kernel
size_t size() const { return m_size; }
vaddr_t vaddr() const { return m_vaddr; }
int status_flags() const { return m_status_flags; }
Type type() const { return m_type; }
PageTable::flags_t flags() const { return m_flags; }
size_t virtual_page_count() const { return BAN::Math::div_round_up<size_t>(m_size, PAGE_SIZE); }
size_t physical_page_count() const { return m_physical_page_count; }
@ -46,6 +50,7 @@ namespace Kernel
void unpin();
void wait_not_pinned();
BAN::ErrorOr<void> mprotect(PageTable::flags_t);
virtual BAN::ErrorOr<void> msync(vaddr_t, size_t, int) = 0;
// Returns error if no memory was available
@ -56,7 +61,7 @@ namespace Kernel
virtual BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> clone(PageTable& new_page_table) = 0;
protected:
MemoryRegion(PageTable&, size_t size, Type type, PageTable::flags_t flags);
MemoryRegion(PageTable&, size_t size, Type type, PageTable::flags_t flags, int status_flags);
BAN::ErrorOr<void> initialize(AddressRange);
virtual BAN::ErrorOr<bool> allocate_page_containing_impl(vaddr_t address, bool wants_write) = 0;
@ -65,7 +70,8 @@ namespace Kernel
PageTable& m_page_table;
const size_t m_size;
const Type m_type;
const PageTable::flags_t m_flags;
PageTable::flags_t m_flags;
const int m_status_flags;
vaddr_t m_vaddr { 0 };
size_t m_physical_page_count { 0 };

View File

@ -6,6 +6,8 @@
#include <kernel/Lock/SpinLock.h>
#include <kernel/Memory/MemoryRegion.h>
#include <fcntl.h>
namespace Kernel
{
@ -55,15 +57,16 @@ namespace Kernel
public:
static BAN::ErrorOr<BAN::UniqPtr<SharedMemoryObject>> create(BAN::RefPtr<SharedMemoryObjectManager::Object>, PageTable&, AddressRange);
virtual BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> clone(PageTable& new_page_table) override;
virtual BAN::ErrorOr<void> msync(vaddr_t, size_t, int) override { return {}; }
BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> clone(PageTable& new_page_table) override;
BAN::ErrorOr<void> msync(vaddr_t, size_t, int) override { return {}; }
protected:
virtual BAN::ErrorOr<bool> allocate_page_containing_impl(vaddr_t vaddr, bool wants_write) override;
BAN::ErrorOr<bool> allocate_page_containing_impl(vaddr_t vaddr, bool wants_write) override;
private:
SharedMemoryObject(BAN::RefPtr<SharedMemoryObjectManager::Object> object, PageTable& page_table)
: MemoryRegion(page_table, object->size, MemoryRegion::Type::SHARED, object->flags)
: MemoryRegion(page_table, object->size, MemoryRegion::Type::SHARED, object->flags, O_EXEC | O_RDWR)
, m_object(object)
{ }

View File

@ -38,7 +38,7 @@ namespace Kernel
bool contains(vaddr_t address) const { return vaddr() <= address && address < vaddr() + size(); }
BAN::ErrorOr<void> allocate_page_for_demand_paging(vaddr_t address);
BAN::ErrorOr<bool> allocate_page_for_demand_paging(vaddr_t address);
private:
VirtualRange(PageTable&, bool preallocated, bool has_guard_pages, vaddr_t, size_t, PageTable::flags_t);

View File

@ -161,6 +161,8 @@ namespace Kernel
BAN::ErrorOr<long> sys_sync(bool should_block);
BAN::ErrorOr<long> sys_get_nprocessor();
static BAN::ErrorOr<long> clean_poweroff(int command);
BAN::ErrorOr<long> sys_poweroff(int command);
@ -168,13 +170,14 @@ namespace Kernel
BAN::ErrorOr<long> sys_mmap(const sys_mmap_t*);
BAN::ErrorOr<long> sys_munmap(void* addr, size_t len);
BAN::ErrorOr<long> sys_mprotect(void* addr, size_t len, int prot);
BAN::ErrorOr<long> sys_msync(void* addr, size_t len, int flags);
BAN::ErrorOr<long> sys_smo_create(size_t len, int prot);
BAN::ErrorOr<long> sys_smo_delete(SharedMemoryObjectManager::Key);
BAN::ErrorOr<long> sys_smo_map(SharedMemoryObjectManager::Key);
BAN::ErrorOr<long> sys_ttyname(int fildes, char* storage);
BAN::ErrorOr<long> sys_ttyname(int fildes, char* name, size_t namesize);
BAN::ErrorOr<long> sys_isatty(int fildes);
BAN::ErrorOr<long> sys_posix_openpt(int flags);
BAN::ErrorOr<long> sys_ptsname(int fildes, char* buffer, size_t buffer_len);
@ -187,6 +190,7 @@ namespace Kernel
BAN::ErrorOr<long> sys_sigpending(sigset_t* set);
BAN::ErrorOr<long> sys_sigprocmask(int how, const sigset_t* set, sigset_t* oset);
BAN::ErrorOr<long> sys_futex(int op, const uint32_t* addr, uint32_t val, const timespec* abstime);
BAN::ErrorOr<long> sys_yield();
BAN::ErrorOr<long> sys_set_tls(void*);
BAN::ErrorOr<long> sys_get_tls();
@ -218,7 +222,7 @@ namespace Kernel
// Returns error if page could not be allocated
// Returns true if the page was allocated successfully
// Return false if access was page violation (segfault)
BAN::ErrorOr<bool> allocate_page_for_demand_paging(vaddr_t addr, bool wants_write);
BAN::ErrorOr<bool> allocate_page_for_demand_paging(vaddr_t addr, bool wants_write, bool wants_exec);
// FIXME: remove this API
BAN::ErrorOr<BAN::String> absolute_path_of(BAN::StringView) const;
@ -335,6 +339,14 @@ namespace Kernel
BAN::UniqPtr<PageTable> m_page_table;
BAN::RefPtr<TTY> m_controlling_terminal;
struct futex_t
{
ThreadBlocker blocker;
uint32_t waiters { 0 };
uint32_t to_wakeup { 0 };
};
BAN::HashMap<paddr_t, BAN::UniqPtr<futex_t>> m_futexes;
friend class Thread;
};

View File

@ -1,5 +1,6 @@
#include <kernel/BootInfo.h>
#include <kernel/BananBootloader.h>
#include <kernel/multiboot.h>
#include <kernel/multiboot2.h>
namespace Kernel
@ -20,6 +21,70 @@ namespace Kernel
return MemoryMapEntry::Type::Reserved;
}
static void parse_boot_info_multiboot(uint32_t info)
{
const auto& multiboot_info = *reinterpret_cast<const multiboot_info_t*>(info);
if (multiboot_info.flags & MULTIBOOT_FLAGS_CMDLINE)
{
MUST(g_boot_info.command_line.append(reinterpret_cast<const char*>(multiboot_info.cmdline)));
}
if (multiboot_info.flags & MULTIBOOT_FLAGS_MODULES)
{
for (size_t i = 0; i < multiboot_info.mods_count; i++)
{
const auto& module = reinterpret_cast<const multiboot_module_t*>(multiboot_info.mods_addr)[i];
MUST(g_boot_info.modules.emplace_back(module.mod_start, module.mod_end - module.mod_start));
}
}
if (multiboot_info.flags & MULTIBOOT_FLAGS_MMAP)
{
uintptr_t address = multiboot_info.mmap_addr;
while (address < multiboot_info.mmap_addr + multiboot_info.mmap_length)
{
const auto& mmap_entry = *reinterpret_cast<const multiboot_mmap_t*>(address);
dprintln("entry {16H} {16H} {8H}",
(uint64_t)mmap_entry.base_addr,
(uint64_t)mmap_entry.length,
(uint64_t)mmap_entry.type
);
MUST(g_boot_info.memory_map_entries.push_back({
.address = mmap_entry.base_addr,
.length = mmap_entry.length,
.type = bios_number_to_memory_type(mmap_entry.type),
}));
address += mmap_entry.size + sizeof(mmap_entry.size);
}
}
if (multiboot_info.flags & MULTIBOOT_FLAGS_FRAMEBUFFER)
{
g_boot_info.framebuffer.address = multiboot_info.framebuffer_addr;
g_boot_info.framebuffer.pitch = multiboot_info.framebuffer_pitch;
g_boot_info.framebuffer.width = multiboot_info.framebuffer_width;
g_boot_info.framebuffer.height = multiboot_info.framebuffer_height;
g_boot_info.framebuffer.bpp = multiboot_info.framebuffer_bpp;
if (multiboot_info.framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB)
g_boot_info.framebuffer.type = FramebufferInfo::Type::RGB;
else if (multiboot_info.framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_TEXT)
g_boot_info.framebuffer.type = FramebufferInfo::Type::Text;
else
g_boot_info.framebuffer.type = FramebufferInfo::Type::Unknown;
}
g_boot_info.kernel_paddr = 0;
}
static BAN::StringView get_early_boot_command_line_multiboot(uint32_t info)
{
const auto& multiboot_info = *reinterpret_cast<const multiboot_info_t*>(info);
if (!(multiboot_info.flags & MULTIBOOT_FLAGS_CMDLINE))
return ""_sv;
return BAN::StringView(reinterpret_cast<const char*>(multiboot_info.cmdline));
}
static void parse_boot_info_multiboot2(uint32_t info)
{
const auto& multiboot2_info = *reinterpret_cast<const multiboot2_info_t*>(info);
@ -149,17 +214,23 @@ namespace Kernel
bool validate_boot_magic(uint32_t magic)
{
if (magic == MULTIBOOT2_MAGIC)
return true;
if (magic == BANAN_BOOTLOADER_MAGIC)
switch (magic)
{
case MULTIBOOT_MAGIC:
case MULTIBOOT2_MAGIC:
case BANAN_BOOTLOADER_MAGIC:
return true;
default:
return false;
}
}
void parse_boot_info(uint32_t magic, uint32_t info)
{
switch (magic)
{
case MULTIBOOT_MAGIC:
return parse_boot_info_multiboot(info);
case MULTIBOOT2_MAGIC:
return parse_boot_info_multiboot2(info);
case BANAN_BOOTLOADER_MAGIC:
@ -172,6 +243,8 @@ namespace Kernel
{
switch (magic)
{
case MULTIBOOT_MAGIC:
return get_early_boot_command_line_multiboot(info);
case MULTIBOOT2_MAGIC:
return get_early_boot_command_line_multiboot2(info);
case BANAN_BOOTLOADER_MAGIC:

View File

@ -241,9 +241,9 @@ namespace Kernel
class FramebufferMemoryRegion : public MemoryRegion
{
public:
static BAN::ErrorOr<BAN::UniqPtr<FramebufferMemoryRegion>> create(PageTable& page_table, size_t size, AddressRange address_range, MemoryRegion::Type region_type, PageTable::flags_t page_flags, BAN::RefPtr<FramebufferDevice> framebuffer)
static BAN::ErrorOr<BAN::UniqPtr<FramebufferMemoryRegion>> create(PageTable& page_table, size_t size, AddressRange address_range, MemoryRegion::Type region_type, PageTable::flags_t page_flags, int status_flags, BAN::RefPtr<FramebufferDevice> framebuffer)
{
auto* region_ptr = new FramebufferMemoryRegion(page_table, size, region_type, page_flags, framebuffer);
auto* region_ptr = new FramebufferMemoryRegion(page_table, size, region_type, page_flags, status_flags, framebuffer);
if (region_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto region = BAN::UniqPtr<FramebufferMemoryRegion>::adopt(region_ptr);
@ -258,7 +258,7 @@ namespace Kernel
m_framebuffer->sync_pixels_full();
}
virtual BAN::ErrorOr<void> msync(vaddr_t vaddr, size_t size, int flags) override
BAN::ErrorOr<void> msync(vaddr_t vaddr, size_t size, int flags) override
{
if (flags != MS_SYNC)
return BAN::Error::from_errno(ENOTSUP);
@ -281,9 +281,9 @@ namespace Kernel
return {};
}
virtual BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> clone(PageTable& new_page_table) override
BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> clone(PageTable& new_page_table) override
{
auto* region_ptr = new FramebufferMemoryRegion(new_page_table, m_size, m_type, m_flags, m_framebuffer);
auto* region_ptr = new FramebufferMemoryRegion(new_page_table, m_size, m_type, m_flags, m_status_flags, m_framebuffer);
if (region_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto region = BAN::UniqPtr<FramebufferMemoryRegion>::adopt(region_ptr);
@ -297,7 +297,7 @@ namespace Kernel
// Returns error if no memory was available
// Returns true if page was succesfully allocated
// Returns false if page was already allocated
virtual BAN::ErrorOr<bool> allocate_page_containing_impl(vaddr_t vaddr, bool wants_write) override
BAN::ErrorOr<bool> allocate_page_containing_impl(vaddr_t vaddr, bool wants_write) override
{
(void)wants_write;
@ -312,8 +312,8 @@ namespace Kernel
}
private:
FramebufferMemoryRegion(PageTable& page_table, size_t size, MemoryRegion::Type region_type, PageTable::flags_t page_flags, BAN::RefPtr<FramebufferDevice> framebuffer)
: MemoryRegion(page_table, size, region_type, page_flags)
FramebufferMemoryRegion(PageTable& page_table, size_t size, MemoryRegion::Type region_type, PageTable::flags_t page_flags, int status_flags, BAN::RefPtr<FramebufferDevice> framebuffer)
: MemoryRegion(page_table, size, region_type, page_flags, status_flags)
, m_framebuffer(framebuffer)
{ }
@ -370,7 +370,7 @@ namespace Kernel
BAN::RefPtr<FramebufferDevice> m_framebuffer;
};
BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> FramebufferDevice::mmap_region(PageTable& page_table, off_t offset, size_t len, AddressRange address_range, MemoryRegion::Type region_type, PageTable::flags_t page_flags)
BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> FramebufferDevice::mmap_region(PageTable& page_table, off_t offset, size_t len, AddressRange address_range, MemoryRegion::Type region_type, PageTable::flags_t page_flags, int status_flags)
{
if (offset != 0)
return BAN::Error::from_errno(EINVAL);
@ -379,7 +379,7 @@ namespace Kernel
if (region_type != MemoryRegion::Type::SHARED)
return BAN::Error::from_errno(EINVAL);
auto region = TRY(FramebufferMemoryRegion::create(page_table, len, address_range, region_type, page_flags, this));
auto region = TRY(FramebufferMemoryRegion::create(page_table, len, address_range, region_type, page_flags, status_flags, this));
return BAN::UniqPtr<MemoryRegion>(BAN::move(region));
}

View File

@ -212,7 +212,7 @@ namespace Kernel::ELF
file_backed_size,
{ .start = pheader_base, .end = pheader_base + file_backed_size },
MemoryRegion::Type::PRIVATE,
flags
flags, O_EXEC | O_RDWR
));
TRY(memory_regions.emplace_back(BAN::move(region)));
}
@ -226,7 +226,7 @@ namespace Kernel::ELF
(pheader_base + program_header.p_memsz) - (aligned_vaddr + file_backed_size),
{ .start = aligned_vaddr + file_backed_size, .end = pheader_base + program_header.p_memsz },
MemoryRegion::Type::PRIVATE,
flags
flags, O_EXEC | O_RDWR
));
if (file_backed_size < program_header.p_filesz)
@ -267,7 +267,8 @@ namespace Kernel::ELF
offset + region_size,
{ .start = last_loaded_address, .end = USERSPACE_END },
MemoryRegion::Type::PRIVATE,
PageTable::Flags::UserSupervisor | PageTable::Flags::Present
PageTable::Flags::UserSupervisor | PageTable::Flags::Present,
O_EXEC | O_RDWR
));
for (vaddr_t vaddr = region->vaddr(); vaddr < region->vaddr() + offset + region->size(); vaddr += PAGE_SIZE)

View File

@ -1,3 +1,4 @@
#include <kernel/BootInfo.h>
#include <kernel/FS/ProcFS/FileSystem.h>
#include <kernel/FS/ProcFS/Inode.h>
@ -33,6 +34,10 @@ namespace Kernel
*s_instance, 0444, 0, 0
));
MUST(static_cast<TmpDirectoryInode*>(s_instance->root_inode().ptr())->link_inode(*meminfo_inode, "meminfo"_sv));
auto cmdline_inode = MUST(TmpFileInode::create_new(*s_instance, 0444, 0, 0));
MUST(cmdline_inode->write(0, { reinterpret_cast<const uint8_t*>(g_boot_info.command_line.data()), g_boot_info.command_line.size() }));
MUST(static_cast<TmpDirectoryInode*>(s_instance->root_inode().ptr())->link_inode(*cmdline_inode, "cmdline"_sv));
}
ProcFileSystem& ProcFileSystem::get()

View File

@ -109,7 +109,8 @@ namespace Kernel
if (file_type == DIRTYPE)
{
TRY(parent_inode->create_directory(file_name_sv, file_mode, file_uid, file_gid));
if (auto ret = parent_inode->create_directory(file_name_sv, file_mode, file_uid, file_gid); ret.is_error())
dwarnln("failed to create directory '{}': {}", file_name_sv, ret.error());
}
else if (file_type == LNKTYPE)
{
@ -117,8 +118,10 @@ namespace Kernel
}
else if (file_type == SYMTYPE)
{
TRY(parent_inode->create_file(file_name_sv, file_mode, file_uid, file_gid));
if (auto ret = parent_inode->create_file(file_name_sv, file_mode, file_uid, file_gid); ret.is_error())
dwarnln("failed to create symlink '{}': {}", file_name_sv, ret.error());
else
{
char link_target[101] {};
const paddr_t paddr = module.start + offset;
PageTable::with_fast_page(paddr & PAGE_ADDR_MASK, [&] {
@ -131,10 +134,13 @@ namespace Kernel
TRY(inode->set_link_target(link_target));
}
}
}
else
{
if (auto ret = parent_inode->create_file(file_name_sv, file_mode, file_uid, file_gid); ret.is_error())
dwarnln("failed to create file '{}': {}", file_name_sv, ret.error());
else
{
TRY(parent_inode->create_file(file_name_sv, file_mode, file_uid, file_gid));
if (file_size)
{
auto inode = TRY(parent_inode->find_inode(file_name_sv));
@ -154,6 +160,7 @@ namespace Kernel
}
}
}
}
offset += 512 + file_size;
if (auto rem = offset % 512)

View File

@ -188,6 +188,26 @@ namespace Kernel
if (isr == ISR::PageFault && Thread::current().is_userspace())
{
if (pid)
{
PageFaultError page_fault_error;
page_fault_error.raw = error;
Processor::set_interrupt_state(InterruptState::Enabled);
auto result = Process::current().allocate_page_for_demand_paging(regs->cr2, page_fault_error.write, page_fault_error.instruction);
Processor::set_interrupt_state(InterruptState::Disabled);
if (!result.is_error() && result.value())
goto done;
if (result.is_error())
{
dwarnln("Demand paging: {}", result.error());
Thread::current().handle_signal(SIGKILL);
goto done;
}
}
// Check if stack is OOB
if (ARCH(i686) && !GDT::is_user_segment(interrupt_stack->cs))
; // 32 bit does not push stack pointer when no CPL change happens
@ -207,26 +227,6 @@ namespace Kernel
Thread::current().handle_signal(SIGKILL);
goto done;
}
if (pid)
{
PageFaultError page_fault_error;
page_fault_error.raw = error;
Processor::set_interrupt_state(InterruptState::Enabled);
auto result = Process::current().allocate_page_for_demand_paging(regs->cr2, page_fault_error.write);
Processor::set_interrupt_state(InterruptState::Disabled);
if (!result.is_error() && result.value())
goto done;
if (result.is_error())
{
dwarnln("Demand paging: {}", result.error());
Thread::current().handle_signal(SIGKILL);
goto done;
}
}
}
}
@ -338,6 +338,7 @@ namespace Kernel
ASSERT(InterruptController::get().is_in_service(IRQ_IPI - IRQ_VECTOR_BASE));
InterruptController::get().eoi(IRQ_IPI - IRQ_VECTOR_BASE);
Processor::handle_ipi();
Processor::scheduler().reschedule_if_idle();
}
extern "C" void cpp_timer_handler()

View File

@ -7,7 +7,7 @@
namespace Kernel
{
BAN::ErrorOr<BAN::UniqPtr<FileBackedRegion>> FileBackedRegion::create(BAN::RefPtr<Inode> inode, PageTable& page_table, off_t offset, size_t size, AddressRange address_range, Type type, PageTable::flags_t flags)
BAN::ErrorOr<BAN::UniqPtr<FileBackedRegion>> FileBackedRegion::create(BAN::RefPtr<Inode> inode, PageTable& page_table, off_t offset, size_t size, AddressRange address_range, Type type, PageTable::flags_t flags, int status_flags)
{
ASSERT(inode->mode().ifreg());
@ -16,14 +16,14 @@ namespace Kernel
if ((size > (size_t)inode->size() || (size_t)offset > (size_t)inode->size() - size))
return BAN::Error::from_errno(EOVERFLOW);
auto* region_ptr = new FileBackedRegion(inode, page_table, offset, size, type, flags);
auto* region_ptr = new FileBackedRegion(inode, page_table, offset, size, type, flags, status_flags);
if (region_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto region = BAN::UniqPtr<FileBackedRegion>::adopt(region_ptr);
TRY(region->initialize(address_range));
if (type == Type::PRIVATE && (flags & PageTable::Flags::ReadWrite))
if (type == Type::PRIVATE)
TRY(region->m_dirty_pages.resize(BAN::Math::div_round_up<size_t>(size, PAGE_SIZE)));
LockGuard _(inode->m_mutex);
@ -39,8 +39,8 @@ namespace Kernel
return region;
}
FileBackedRegion::FileBackedRegion(BAN::RefPtr<Inode> inode, PageTable& page_table, off_t offset, ssize_t size, Type type, PageTable::flags_t flags)
: MemoryRegion(page_table, size, type, flags)
FileBackedRegion::FileBackedRegion(BAN::RefPtr<Inode> inode, PageTable& page_table, off_t offset, ssize_t size, Type type, PageTable::flags_t flags, int status_flags)
: MemoryRegion(page_table, size, type, flags, status_flags)
, m_inode(inode)
, m_offset(offset)
{
@ -200,7 +200,7 @@ namespace Kernel
BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> FileBackedRegion::clone(PageTable& page_table)
{
const size_t aligned_size = (m_size + PAGE_SIZE - 1) & PAGE_ADDR_MASK;
auto result = TRY(FileBackedRegion::create(m_inode, page_table, m_offset, m_size, { .start = m_vaddr, .end = m_vaddr + aligned_size }, m_type, m_flags));
auto result = TRY(FileBackedRegion::create(m_inode, page_table, m_offset, m_size, { .start = m_vaddr, .end = m_vaddr + aligned_size }, m_type, m_flags, m_status_flags));
// non-dirty pages can go through demand paging

View File

@ -4,12 +4,12 @@
namespace Kernel
{
BAN::ErrorOr<BAN::UniqPtr<MemoryBackedRegion>> MemoryBackedRegion::create(PageTable& page_table, size_t size, AddressRange address_range, Type type, PageTable::flags_t flags)
BAN::ErrorOr<BAN::UniqPtr<MemoryBackedRegion>> MemoryBackedRegion::create(PageTable& page_table, size_t size, AddressRange address_range, Type type, PageTable::flags_t flags, int status_flags)
{
if (type != Type::PRIVATE)
return BAN::Error::from_errno(ENOTSUP);
auto* region_ptr = new MemoryBackedRegion(page_table, size, type, flags);
auto* region_ptr = new MemoryBackedRegion(page_table, size, type, flags, status_flags);
if (region_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto region = BAN::UniqPtr<MemoryBackedRegion>::adopt(region_ptr);
@ -19,8 +19,8 @@ namespace Kernel
return region;
}
MemoryBackedRegion::MemoryBackedRegion(PageTable& page_table, size_t size, Type type, PageTable::flags_t flags)
: MemoryRegion(page_table, size, type, flags)
MemoryBackedRegion::MemoryBackedRegion(PageTable& page_table, size_t size, Type type, PageTable::flags_t flags, int status_flags)
: MemoryRegion(page_table, size, type, flags, status_flags)
{
}
@ -68,7 +68,7 @@ namespace Kernel
ASSERT(&PageTable::current() == &m_page_table);
const size_t aligned_size = (m_size + PAGE_SIZE - 1) & PAGE_ADDR_MASK;
auto result = TRY(MemoryBackedRegion::create(new_page_table, m_size, { .start = m_vaddr, .end = m_vaddr + aligned_size }, m_type, m_flags));
auto result = TRY(MemoryBackedRegion::create(new_page_table, m_size, { .start = m_vaddr, .end = m_vaddr + aligned_size }, m_type, m_flags, m_status_flags));
for (size_t offset = 0; offset < m_size; offset += PAGE_SIZE)
{

View File

@ -4,11 +4,12 @@
namespace Kernel
{
MemoryRegion::MemoryRegion(PageTable& page_table, size_t size, Type type, PageTable::flags_t flags)
MemoryRegion::MemoryRegion(PageTable& page_table, size_t size, Type type, PageTable::flags_t flags, int status_flags)
: m_page_table(page_table)
, m_size(size)
, m_type(type)
, m_flags(flags)
, m_status_flags(status_flags)
{
}
@ -49,6 +50,25 @@ namespace Kernel
return true;
}
BAN::ErrorOr<void> MemoryRegion::mprotect(PageTable::flags_t new_page_flags)
{
if (m_flags == new_page_flags)
return {};
const size_t page_count = BAN::Math::div_round_up<size_t>(m_size, PAGE_SIZE);
for (size_t i = 0; i < page_count; i++)
{
const vaddr_t vaddr = m_vaddr + i * PAGE_SIZE;
const paddr_t paddr = m_page_table.physical_address_of(vaddr);
if (paddr == 0)
continue;
m_page_table.map_page_at(paddr, vaddr, new_page_flags);
}
m_flags = new_page_flags;
return {};
}
BAN::ErrorOr<bool> MemoryRegion::allocate_page_containing(vaddr_t address, bool wants_write)
{
ASSERT(contains(address));

View File

@ -99,7 +99,7 @@ namespace Kernel
else
{
const size_t page_count = size() / PAGE_SIZE;
for (size_t i = m_has_guard_pages; i < page_count; i++)
for (size_t i = 0; i < page_count; i++)
{
PageTable::with_fast_page(m_paddrs[i], [&] {
memset(PageTable::fast_page_as_ptr(), 0, PAGE_SIZE);
@ -120,7 +120,7 @@ namespace Kernel
auto result = TRY(create_to_vaddr(page_table, vaddr(), size(), m_flags, m_preallocated, m_has_guard_pages));
const size_t page_count = size() / PAGE_SIZE;
for (size_t i = m_has_guard_pages; i < page_count; i++)
for (size_t i = 0; i < page_count; i++)
{
if (m_paddrs[i] == 0)
continue;
@ -140,15 +140,17 @@ namespace Kernel
return result;
}
BAN::ErrorOr<void> VirtualRange::allocate_page_for_demand_paging(vaddr_t vaddr)
BAN::ErrorOr<bool> VirtualRange::allocate_page_for_demand_paging(vaddr_t vaddr)
{
ASSERT(!m_preallocated);
ASSERT(vaddr % PAGE_SIZE == 0);
ASSERT(contains(vaddr));
ASSERT(&PageTable::current() == &m_page_table);
if (m_preallocated)
return false;
const size_t index = (vaddr - this->vaddr()) / PAGE_SIZE;
ASSERT(m_paddrs[index] == 0);
if (m_paddrs[index])
return false;
SpinLockGuard _(m_lock);
@ -159,7 +161,7 @@ namespace Kernel
m_page_table.map_page_at(m_paddrs[index], vaddr, m_flags);
memset(reinterpret_cast<void*>(vaddr), 0, PAGE_SIZE);
return {};
return true;
}
}

View File

@ -29,17 +29,16 @@ namespace Kernel
, m_type(type)
, m_rdev(get_rdev(type))
{
ASSERT(minor(m_rdev) < 10);
switch (type)
{
case Type::Ethernet:
ASSERT(minor(m_rdev) < 10);
strcpy(m_name, "ethx");
m_name[3] = minor(m_rdev) + '0';
break;
case Type::Loopback:
strcpy(m_name, "lox");
m_name[2] = minor(m_rdev) + '0';
ASSERT(minor(m_rdev) == 0);
strcpy(m_name, "lo");
break;
}
}

View File

@ -429,6 +429,8 @@ namespace Kernel
size_t nread;
{
LockGuard _(inode->m_mutex);
if (!inode->can_read() && inode->has_hungup())
return 0;
if (is_nonblock && !inode->can_read())
return BAN::Error::from_errno(EAGAIN);
nread = TRY(inode->read(offset, buffer));
@ -464,6 +466,11 @@ namespace Kernel
size_t nwrite;
{
LockGuard _(inode->m_mutex);
if (inode->has_error())
{
Thread::current().add_signal(SIGPIPE);
return BAN::Error::from_errno(EPIPE);
}
if (is_nonblock && !inode->can_write())
return BAN::Error::from_errno(EAGAIN);
nwrite = TRY(inode->write(offset, buffer));

View File

@ -26,6 +26,7 @@
#include <pthread.h>
#include <stdio.h>
#include <sys/banan-os.h>
#include <sys/futex.h>
#include <sys/sysmacros.h>
#include <sys/wait.h>
@ -317,7 +318,8 @@ namespace Kernel
tls_size,
{ .start = master_addr, .end = USERSPACE_END },
MemoryRegion::Type::PRIVATE,
PageTable::Flags::UserSupervisor | PageTable::Flags::ReadWrite | PageTable::Flags::Present
PageTable::Flags::UserSupervisor | PageTable::Flags::ReadWrite | PageTable::Flags::Present,
O_EXEC | O_RDWR
));
BAN::Vector<uint8_t> temp_buffer;
@ -981,24 +983,32 @@ namespace Kernel
return {};
}
BAN::ErrorOr<bool> Process::allocate_page_for_demand_paging(vaddr_t address, bool wants_write)
BAN::ErrorOr<bool> Process::allocate_page_for_demand_paging(vaddr_t address, bool wants_write, bool wants_exec)
{
ASSERT(&Process::current() == this);
LockGuard _(m_process_lock);
if (Thread::current().userspace_stack().contains(address))
{
TRY(Thread::current().userspace_stack().allocate_page_for_demand_paging(address));
// NOTE: page can be already allocated by another thread!
PageTable::flags_t wanted_flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present;
if (wants_write)
wanted_flags |= PageTable::Flags::ReadWrite;
if (wants_exec)
wanted_flags |= PageTable::Flags::Execute;
if ((m_page_table->get_page_flags(address & PAGE_ADDR_MASK) & wanted_flags) == wanted_flags)
return true;
}
if (Thread::current().userspace_stack().contains(address))
return Thread::current().userspace_stack().allocate_page_for_demand_paging(address);
for (auto& region : m_mapped_regions)
{
if (!region->contains(address))
continue;
TRY(region->allocate_page_containing(address, wants_write));
return true;
return region->allocate_page_containing(address, wants_write);
}
return false;
@ -2011,6 +2021,11 @@ namespace Kernel
return 0;
}
BAN::ErrorOr<long> Process::sys_get_nprocessor()
{
return Processor::count();
}
[[noreturn]] static void reset_system()
{
(void)ACPI::ACPI::get().reset();
@ -2132,7 +2147,8 @@ namespace Kernel
page_table(),
args.len,
address_range,
region_type, page_flags
region_type, page_flags,
O_EXEC | O_RDWR
));
LockGuard _(m_process_lock);
@ -2159,7 +2175,8 @@ namespace Kernel
page_table(),
args.off, args.len,
address_range,
region_type, page_flags
region_type, page_flags,
status_flags
));
}
else if (inode->is_device())
@ -2168,7 +2185,8 @@ namespace Kernel
page_table(),
args.off, args.len,
address_range,
region_type, page_flags
region_type, page_flags,
status_flags
));
}
@ -2214,6 +2232,70 @@ namespace Kernel
return 0;
}
BAN::ErrorOr<long> Process::sys_mprotect(void* addr, size_t len, int prot)
{
if (len == 0)
return BAN::Error::from_errno(EINVAL);
const vaddr_t vaddr = reinterpret_cast<vaddr_t>(addr);
if (vaddr % PAGE_SIZE != 0)
return BAN::Error::from_errno(EINVAL);
if (auto rem = len % PAGE_SIZE)
len += PAGE_SIZE - rem;
PageTable::flags_t flags = 0;
if (prot & PROT_READ)
flags |= PageTable::Flags::Present;
if (prot & PROT_WRITE)
flags |= PageTable::Flags::ReadWrite | PageTable::Flags::Present;
if (prot & PROT_EXEC)
flags |= PageTable::Flags::Execute | PageTable::Flags::Execute;
if (flags == 0)
flags = PageTable::Flags::Reserved;
else
flags |= PageTable::Flags::UserSupervisor;
LockGuard _(m_process_lock);
// FIXME: We should protect partial regions.
// This is a hack to only protect if the whole mmap region
// is contained within [addr, addr + len]
for (size_t i = 0; i < m_mapped_regions.size(); i++)
{
auto& region = m_mapped_regions[i];
const vaddr_t region_s = region->vaddr();
const vaddr_t region_e = region->vaddr() + region->size();
if (vaddr <= region_s && region_e <= vaddr + len)
{
const bool is_shared = (region->type() == MemoryRegion::Type::SHARED);
const bool is_writable = (region->status_flags() & O_WRONLY);
const bool want_write = (prot & PROT_WRITE);
if (is_shared && want_write && !is_writable)
return BAN::Error::from_errno(EACCES);
// FIXME: if the region is pinned writable, this may
// cause some problems :D
TRY(region->mprotect(flags));
}
else if (region->overlaps(vaddr, len))
{
const bool is_shared = (region->type() == MemoryRegion::Type::SHARED);
const bool is_writable = (region->status_flags() & O_WRONLY);
const bool want_write = (prot & PROT_WRITE);
if (is_shared && want_write && !is_writable)
return BAN::Error::from_errno(EACCES);
dwarnln("TODO: partial region mprotect");
TRY(region->mprotect(flags | region->flags()));
}
}
return 0;
}
BAN::ErrorOr<long> Process::sys_msync(void* addr, size_t len, int flags)
{
if (flags != MS_SYNC && flags != MS_ASYNC && flags != MS_INVALIDATE)
@ -2270,17 +2352,17 @@ namespace Kernel
return m_mapped_regions.back()->vaddr();
}
BAN::ErrorOr<long> Process::sys_ttyname(int fildes, char* storage)
BAN::ErrorOr<long> Process::sys_ttyname(int fildes, char* name, size_t namesize)
{
LockGuard _(m_process_lock);
TRY(validate_pointer_access(storage, TTY_NAME_MAX, true));
TRY(validate_pointer_access(name, namesize, true));
auto inode = TRY(m_open_file_descriptors.inode_of(fildes));
if (!inode->is_tty())
return BAN::Error::from_errno(ENOTTY);
auto path = TRY(m_open_file_descriptors.path_of(fildes));
ASSERT(path.size() < TTY_NAME_MAX);
strncpy(storage, path.data(), path.size());
storage[path.size()] = '\0';
if (namesize < path.size() + 1)
return BAN::Error::from_errno(ERANGE);
strcpy(name, path.data());
return 0;
}
@ -2491,14 +2573,7 @@ namespace Kernel
*oact = m_signal_handlers[signal];
if (act)
{
if (act->sa_flags & ~(SA_RESTART))
{
dwarnln("TODO: sigaction({}, {H})", signal, act->sa_flags);
return BAN::Error::from_errno(ENOTSUP);
}
m_signal_handlers[signal] = *act;
}
return 0;
}
@ -2544,6 +2619,96 @@ namespace Kernel
return 0;
}
BAN::ErrorOr<long> Process::sys_futex(int op, const uint32_t* addr, uint32_t val, const timespec* abstime)
{
const vaddr_t vaddr = reinterpret_cast<vaddr_t>(addr);
if (vaddr % 4)
return BAN::Error::from_errno(EINVAL);
const bool is_private = (op & FUTEX_PRIVATE);
const bool is_realtime = (op & FUTEX_REALTIME);
op &= ~(FUTEX_PRIVATE | FUTEX_REALTIME);
if (!is_private)
{
dwarnln("TODO: shared futex");
return BAN::Error::from_errno(ENOTSUP);
}
LockGuard _(m_process_lock);
auto* buffer_region = TRY(validate_and_pin_pointer_access(addr, sizeof(uint32_t), false));
BAN::ScopeGuard pin_guard([&] { if (buffer_region) buffer_region->unpin(); });
const paddr_t paddr = m_page_table->physical_address_of(vaddr & PAGE_ADDR_MASK) | (vaddr & ~PAGE_ADDR_MASK);
ASSERT(paddr != 0);
switch (op)
{
case FUTEX_WAIT:
{
if (BAN::atomic_load(*addr) != val)
return BAN::Error::from_errno(EAGAIN);
const uint64_t wake_time_ns =
TRY([abstime, is_realtime, this]() -> BAN::ErrorOr<uint64_t>
{
if (abstime == nullptr)
return BAN::numeric_limits<uint64_t>::max();
TRY(validate_pointer_access(abstime, sizeof(*abstime), false));
const uint64_t abs_ns = abstime->tv_sec * 1'000'000'000 + abstime->tv_nsec;
if (!is_realtime)
return abs_ns;
const auto realtime = SystemTimer::get().real_time();
const uint64_t real_ns = realtime.tv_sec * 1'000'000'000 + realtime.tv_nsec;
if (abs_ns <= real_ns)
return BAN::Error::from_errno(ETIMEDOUT);
return SystemTimer::get().ns_since_boot() + (abs_ns - real_ns);
}());
auto it = m_futexes.find(paddr);
if (it == m_futexes.end())
it = TRY(m_futexes.emplace(paddr, TRY(BAN::UniqPtr<futex_t>::create())));
futex_t* const futex = it->value.ptr();
futex->waiters++;
BAN::ScopeGuard _([futex, paddr, this] {
if (--futex->waiters == 0)
m_futexes.remove(paddr);
});
for (;;)
{
TRY(Thread::current().block_or_eintr_or_waketime_ns(futex->blocker, wake_time_ns, true, &m_process_lock));
if (BAN::atomic_load(*addr) == val || futex->to_wakeup == 0)
continue;
futex->to_wakeup--;
return 0;
}
}
case FUTEX_WAKE:
{
auto it = m_futexes.find(paddr);
if (it == m_futexes.end())
return 0;
futex_t* const futex = it->value.ptr();
if (BAN::Math::will_addition_overflow(futex->to_wakeup, val))
futex->to_wakeup = BAN::numeric_limits<uint32_t>::max();
else
futex->to_wakeup += val;
futex->to_wakeup = BAN::Math::min(futex->to_wakeup, futex->waiters);
futex->blocker.unblock();
return 0;
}
default:
return BAN::Error::from_errno(ENOSYS);
}
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<long> Process::sys_yield()
{
Processor::yield();
@ -3055,9 +3220,8 @@ namespace Kernel
BAN::ErrorOr<void> Process::validate_pointer_access_check(const void* ptr, size_t size, bool needs_write)
{
ASSERT(&Process::current() == this);
auto& thread = Thread::current();
vaddr_t vaddr = (vaddr_t)ptr;
const vaddr_t vaddr = reinterpret_cast<vaddr_t>(ptr);
// NOTE: detect overflow
if (vaddr + size < vaddr)
@ -3067,14 +3231,12 @@ namespace Kernel
if (vaddr + size > USERSPACE_END)
goto unauthorized_access;
if (vaddr == 0)
return {};
if (vaddr >= thread.userspace_stack_bottom() && vaddr + size <= thread.userspace_stack_top())
for (const auto* thread : m_threads)
if (vaddr >= thread->userspace_stack_bottom() && vaddr + size <= thread->userspace_stack_top())
return {};
// FIXME: should we allow cross mapping access?
for (auto& mapped_region : m_mapped_regions)
for (const auto& mapped_region : m_mapped_regions)
{
if (!mapped_region->contains_fully(vaddr, size))
continue;
@ -3107,7 +3269,7 @@ unauthorized_access:
const vaddr_t current = page_start + i * PAGE_SIZE;
if (page_table().get_page_flags(current) & PageTable::Flags::Present)
continue;
TRY(Process::allocate_page_for_demand_paging(current, needs_write));
TRY(Process::allocate_page_for_demand_paging(current, needs_write, false));
}
return {};

View File

@ -119,9 +119,15 @@ namespace Kernel
thread->m_is_userspace = true;
#if ARCH(x86_64)
static constexpr vaddr_t stack_addr_start = 0x0000700000000000;
#elif ARCH(i686)
static constexpr vaddr_t stack_addr_start = 0xB0000000;
#endif
thread->m_kernel_stack = TRY(VirtualRange::create_to_vaddr_range(
page_table,
0x200000, USERSPACE_END,
stack_addr_start, USERSPACE_END,
kernel_stack_size,
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
true, true
@ -129,7 +135,7 @@ namespace Kernel
thread->m_userspace_stack = TRY(VirtualRange::create_to_vaddr_range(
page_table,
0x200000, USERSPACE_END,
stack_addr_start, USERSPACE_END,
userspace_stack_size,
PageTable::Flags::UserSupervisor | PageTable::Flags::ReadWrite | PageTable::Flags::Present,
true, true
@ -507,9 +513,16 @@ namespace Kernel
bool has_sa_restart;
{
SpinLockGuard _(m_process->m_signal_lock);
ASSERT(!(m_process->m_signal_handlers[signal].sa_flags & SA_SIGINFO));
signal_handler = (vaddr_t)m_process->m_signal_handlers[signal].sa_handler;
has_sa_restart = !!(m_process->m_signal_handlers[signal].sa_flags & SA_RESTART);
auto& handler = m_process->m_signal_handlers[signal];
ASSERT(!(handler.sa_flags & SA_SIGINFO));
signal_handler = reinterpret_cast<vaddr_t>(handler.sa_handler);
if (handler.sa_flags & SA_RESETHAND)
handler.sa_handler = SIG_DFL;
has_sa_restart = !!(handler.sa_flags & SA_RESTART);
}
m_signal_pending_mask &= ~(1ull << signal);

View File

@ -1,6 +1,6 @@
diff -ruN SDL-release-2.30.11/CMakeLists.txt SDL-release-2.30.11-banan_os/CMakeLists.txt
diff -ruN SDL-release-2.30.11/CMakeLists.txt SDL2-2.30.11-banan_os/CMakeLists.txt
--- SDL-release-2.30.11/CMakeLists.txt 2025-01-01 19:09:38.000000000 +0200
+++ SDL-release-2.30.11-banan_os/CMakeLists.txt 2025-06-25 16:53:26.163363407 +0300
+++ SDL2-2.30.11-banan_os/CMakeLists.txt 2025-08-03 14:04:09.894244781 +0300
@@ -1452,7 +1452,7 @@
CheckPTHREAD()
CheckLibUnwind()
@ -60,9 +60,9 @@ diff -ruN SDL-release-2.30.11/CMakeLists.txt SDL-release-2.30.11-banan_os/CMakeL
elseif(RISCOS)
if(SDL_MISC)
file(GLOB MISC_SOURCES ${SDL2_SOURCE_DIR}/src/misc/riscos/*.c)
diff -ruN SDL-release-2.30.11/include/SDL_config.h.cmake SDL-release-2.30.11-banan_os/include/SDL_config.h.cmake
diff -ruN SDL-release-2.30.11/include/SDL_config.h.cmake SDL2-2.30.11-banan_os/include/SDL_config.h.cmake
--- SDL-release-2.30.11/include/SDL_config.h.cmake 2025-01-01 19:09:38.000000000 +0200
+++ SDL-release-2.30.11-banan_os/include/SDL_config.h.cmake 2025-06-24 19:53:27.963249565 +0300
+++ SDL2-2.30.11-banan_os/include/SDL_config.h.cmake 2025-08-03 14:04:09.894803585 +0300
@@ -406,6 +406,7 @@
#cmakedefine SDL_VIDEO_DRIVER_ANDROID @SDL_VIDEO_DRIVER_ANDROID@
#cmakedefine SDL_VIDEO_DRIVER_EMSCRIPTEN @SDL_VIDEO_DRIVER_EMSCRIPTEN@
@ -71,9 +71,9 @@ diff -ruN SDL-release-2.30.11/include/SDL_config.h.cmake SDL-release-2.30.11-ban
#cmakedefine SDL_VIDEO_DRIVER_COCOA @SDL_VIDEO_DRIVER_COCOA@
#cmakedefine SDL_VIDEO_DRIVER_UIKIT @SDL_VIDEO_DRIVER_UIKIT@
#cmakedefine SDL_VIDEO_DRIVER_DIRECTFB @SDL_VIDEO_DRIVER_DIRECTFB@
diff -ruN SDL-release-2.30.11/include/SDL_platform.h SDL-release-2.30.11-banan_os/include/SDL_platform.h
diff -ruN SDL-release-2.30.11/include/SDL_platform.h SDL2-2.30.11-banan_os/include/SDL_platform.h
--- SDL-release-2.30.11/include/SDL_platform.h 2025-01-01 19:09:38.000000000 +0200
+++ SDL-release-2.30.11-banan_os/include/SDL_platform.h 2025-06-24 17:54:20.094530618 +0300
+++ SDL2-2.30.11-banan_os/include/SDL_platform.h 2025-08-03 14:04:09.895022748 +0300
@@ -36,6 +36,10 @@
#undef __HAIKU__
#define __HAIKU__ 1
@ -85,9 +85,9 @@ diff -ruN SDL-release-2.30.11/include/SDL_platform.h SDL-release-2.30.11-banan_o
#if defined(bsdi) || defined(__bsdi) || defined(__bsdi__)
#undef __BSDI__
#define __BSDI__ 1
diff -ruN SDL-release-2.30.11/src/misc/banan_os/SDL_sysurl.cpp SDL-release-2.30.11-banan_os/src/misc/banan_os/SDL_sysurl.cpp
diff -ruN SDL-release-2.30.11/src/misc/banan_os/SDL_sysurl.cpp SDL2-2.30.11-banan_os/src/misc/banan_os/SDL_sysurl.cpp
--- SDL-release-2.30.11/src/misc/banan_os/SDL_sysurl.cpp 1970-01-01 02:00:00.000000000 +0200
+++ SDL-release-2.30.11-banan_os/src/misc/banan_os/SDL_sysurl.cpp 2025-06-24 18:51:56.695953622 +0300
+++ SDL2-2.30.11-banan_os/src/misc/banan_os/SDL_sysurl.cpp 2025-08-03 14:04:09.895198889 +0300
@@ -0,0 +1,30 @@
+/*
+ Simple DirectMedia Layer
@ -119,9 +119,9 @@ diff -ruN SDL-release-2.30.11/src/misc/banan_os/SDL_sysurl.cpp SDL-release-2.30.
+
+/* vi: set ts=4 sw=4 expandtab: */
+
diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_message_box.cpp SDL-release-2.30.11-banan_os/src/video/banan_os/SDL_banan_os_message_box.cpp
diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_message_box.cpp SDL2-2.30.11-banan_os/src/video/banan_os/SDL_banan_os_message_box.cpp
--- SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_message_box.cpp 1970-01-01 02:00:00.000000000 +0200
+++ SDL-release-2.30.11-banan_os/src/video/banan_os/SDL_banan_os_message_box.cpp 2025-06-27 16:29:59.012277213 +0300
+++ SDL2-2.30.11-banan_os/src/video/banan_os/SDL_banan_os_message_box.cpp 2025-08-03 14:04:09.895299391 +0300
@@ -0,0 +1,60 @@
+/*
+ Simple DirectMedia Layer
@ -183,9 +183,9 @@ diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_message_box.cpp SD
+#endif /* SDL_VIDEO_DRIVER_BANAN_OS */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_message_box.h SDL-release-2.30.11-banan_os/src/video/banan_os/SDL_banan_os_message_box.h
diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_message_box.h SDL2-2.30.11-banan_os/src/video/banan_os/SDL_banan_os_message_box.h
--- SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_message_box.h 1970-01-01 02:00:00.000000000 +0200
+++ SDL-release-2.30.11-banan_os/src/video/banan_os/SDL_banan_os_message_box.h 2025-06-24 19:49:44.390183027 +0300
+++ SDL2-2.30.11-banan_os/src/video/banan_os/SDL_banan_os_message_box.h 2025-08-03 14:04:09.895347931 +0300
@@ -0,0 +1,45 @@
+/*
+ Simple DirectMedia Layer
@ -232,10 +232,10 @@ diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_message_box.h SDL-
+#endif
+
+/* vi: set ts=4 sw=4 expandtab: */
diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_video.cpp SDL-release-2.30.11-banan_os/src/video/banan_os/SDL_banan_os_video.cpp
diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_video.cpp SDL2-2.30.11-banan_os/src/video/banan_os/SDL_banan_os_video.cpp
--- SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_video.cpp 1970-01-01 02:00:00.000000000 +0200
+++ SDL-release-2.30.11-banan_os/src/video/banan_os/SDL_banan_os_video.cpp 2025-06-28 20:49:41.452498550 +0300
@@ -0,0 +1,770 @@
+++ SDL2-2.30.11-banan_os/src/video/banan_os/SDL_banan_os_video.cpp 2025-08-04 14:57:50.844360579 +0300
@@ -0,0 +1,719 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
@ -291,8 +291,8 @@ diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_video.cpp SDL-rele
+struct banan_os_window
+{
+ BAN::UniqPtr<LibGUI::Window> window;
+ LibGUI::Texture framebuffer;
+ OSMesaContext gl_context;
+ OSMesaContext gl_context { nullptr };
+ SDL_bool relative { SDL_FALSE };
+};
+
+struct banan_os_video_device_data
@ -442,6 +442,8 @@ diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_video.cpp SDL-rele
+{
+ DUMP_FUNCTION();
+
+ // FIXME: support system cursors :)
+
+ auto* cursor = static_cast<SDL_Cursor*>(SDL_calloc(1, sizeof(SDL_Cursor)));
+ if (cursor == nullptr) {
+ SDL_OutOfMemory();
@ -473,12 +475,17 @@ diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_video.cpp SDL-rele
+ return 0;
+}
+
+static int BANAN_OS_CaptureMouse(SDL_Window* window)
+static int BANAN_OS_SetRelativeMouseMode(SDL_bool enabled)
+{
+ DUMP_FUNCTION();
+
+ auto* window = SDL_GetMouseFocus();
+ if (window == nullptr)
+ return 0;
+
+ auto& ban_window = *static_cast<banan_os_window*>(window->driverdata);
+ ban_window.window->set_mouse_capture(SDL_GetMouse()->capture_window == window);
+ ban_window.window->set_mouse_relative(enabled);
+ ban_window.relative = enabled;
+
+ return 0;
+}
@ -489,9 +496,11 @@ diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_video.cpp SDL-rele
+
+ auto* mouse = SDL_GetMouse();
+ mouse->ShowCursor = BANAN_OS_ShowCursor;
+ mouse->CaptureMouse = BANAN_OS_CaptureMouse;
+ mouse->SetRelativeMouseMode = BANAN_OS_SetRelativeMouseMode;
+ mouse->CreateSystemCursor = BANAN_OS_CreateSystemCursor;
+ mouse->FreeCursor = BANAN_OS_FreeCursor;
+
+ SDL_SetDefaultCursor(BANAN_OS_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW));
+}
+
+static int BANAN_OS_VideoInit(_THIS)
@ -588,16 +597,16 @@ diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_video.cpp SDL-rele
+ );
+
+ ban_window->window->set_mouse_button_event_callback(
+ [window](LibGUI::EventPacket::MouseButtonEvent::event_t event) {
+ [window, ban_window](LibGUI::EventPacket::MouseButtonEvent::event_t event) {
+ const int state = event.pressed ? SDL_PRESSED : SDL_RELEASED;
+ SDL_SendMouseMotion(window, 0, SDL_FALSE, event.x, event.y);
+ SDL_SendMouseMotion(window, 0, ban_window->relative, event.x, event.y);
+ SDL_SendMouseButton(window, 0, state, BANAN_OS_mouse_button_to_sdl(event.button));
+ }
+ );
+
+ ban_window->window->set_mouse_move_event_callback(
+ [window](LibGUI::EventPacket::MouseMoveEvent::event_t event) {
+ SDL_SendMouseMotion(window, 0, SDL_FALSE, event.x, event.y);
+ [window, ban_window](LibGUI::EventPacket::MouseMoveEvent::event_t event) {
+ SDL_SendMouseMotion(window, 0, ban_window->relative, event.x, event.y);
+ }
+ );
+
@ -613,13 +622,13 @@ diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_video.cpp SDL-rele
+ const size_t height = ban_window->window->height();
+
+ if (ban_window->gl_context) {
+ ban_window->framebuffer = MUST(LibGUI::Texture::create(width, height, 0x000000));
+ OSMesaMakeCurrent(
+ ban_window->gl_context,
+ ban_window->framebuffer.pixels().data(),
+ ban_window->window->texture().pixels().data(),
+ GL_UNSIGNED_BYTE,
+ width, height
+ );
+ OSMesaPixelStore(OSMESA_Y_UP, 0);
+ }
+
+ SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, width, height);
@ -767,11 +776,9 @@ diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_video.cpp SDL-rele
+ return -1;
+ }
+
+ ban_window.framebuffer = framebuffer_or_error.release_value();
+
+ *format = SDL_PIXELFORMAT_BGR888;
+ *pixels = ban_window.framebuffer.pixels().data();
+ *pitch = ban_window.framebuffer.width() * sizeof(uint32_t);
+ *pixels = ban_window.window->texture().pixels().data();
+ *pitch = ban_window.window->texture().width() * sizeof(uint32_t);
+
+ return 0;
+}
@ -782,25 +789,8 @@ diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_video.cpp SDL-rele
+
+ auto& ban_window = *static_cast<banan_os_window*>(window->driverdata);
+
+ int min_x = ban_window.window->width();
+ int min_y = ban_window.window->height();
+ int max_x = 0;
+ int max_y = 0;
+ for (int i = 0; i < numrects; i++) {
+ ban_window.window->texture().copy_texture(ban_window.framebuffer,
+ rects[i].x, rects[i].y,
+ rects[i].x, rects[i].y,
+ rects[i].w, rects[i].h
+ );
+
+ min_x = BAN::Math::min(min_x, rects[i].x);
+ min_y = BAN::Math::min(min_y, rects[i].y);
+ max_x = BAN::Math::max(max_x, rects[i].x + rects[i].w);
+ max_y = BAN::Math::max(max_y, rects[i].y + rects[i].h);
+ }
+
+ if (min_x < max_x && min_y < max_y)
+ ban_window.window->invalidate(min_x, min_y, max_x - min_x, max_y - min_y);
+ for (int i = 0; i < numrects; i++)
+ ban_window.window->invalidate(rects[i].x, rects[i].y, rects[i].w, rects[i].h);
+
+ return 0;
+}
@ -808,32 +798,6 @@ diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_video.cpp SDL-rele
+static void BANAN_OS_DestroyWindowFramebuffer(_THIS, SDL_Window* window)
+{
+ DUMP_FUNCTION();
+
+ auto& ban_window = *static_cast<banan_os_window*>(window->driverdata);
+ ban_window.framebuffer = {};
+}
+
+static int BANAN_OS_WaitEventTimeout(_THIS, int timeout)
+{
+ DUMP_FUNCTION();
+
+ auto& ban_video_device_data = *static_cast<banan_os_video_device_data*>(_this->driverdata);
+
+ fd_set fds;
+ FD_ZERO(&fds);
+
+ int max_fd = 0;
+ for (auto* window : ban_video_device_data.windows)
+ {
+ max_fd = BAN::Math::max(max_fd, window->window->server_fd());
+ FD_SET(window->window->server_fd(), &fds);
+ }
+
+ timeval tv {
+ .tv_sec = static_cast<time_t>(timeout / 1000),
+ .tv_usec = static_cast<suseconds_t>((timeout % 1000) * 1000),
+ };
+ return select(max_fd + 1, &fds, nullptr, nullptr, &tv);
+}
+
+static void BANAN_OS_PumpEvents(_THIS)
@ -871,25 +835,19 @@ diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_video.cpp SDL-rele
+
+ auto& ban_window = *static_cast<banan_os_window*>(window->driverdata);
+
+ auto framebuffer_or_error = LibGUI::Texture::create(ban_window.window->width(), ban_window.window->width(), 0x000000);
+ if (framebuffer_or_error.is_error()) {
+ dwarnln("LibGUI::Texture::create: {}", framebuffer_or_error.error());
+ return nullptr;
+ }
+
+ auto gl_context = OSMesaCreateContext(OSMESA_BGRA, NULL);
+ if (gl_context == nullptr) {
+ dwarnln("OSMesaCreateContext");
+ return nullptr;
+ }
+
+ auto fb = framebuffer_or_error.release_value();
+ auto& fb = ban_window.window->texture();
+ if (!OSMesaMakeCurrent(gl_context, fb.pixels().data(), GL_UNSIGNED_BYTE, fb.width(), fb.height())) {
+ OSMesaDestroyContext(gl_context);
+ return nullptr;
+ }
+ OSMesaPixelStore(OSMESA_Y_UP, 0);
+
+ ban_window.framebuffer = BAN::move(fb);
+ ban_window.gl_context = gl_context;
+
+ return gl_context;
@ -914,9 +872,10 @@ diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_video.cpp SDL-rele
+
+ auto gl_context = static_cast<OSMesaContext>(context);
+
+ auto& fb = ban_window.framebuffer;
+ auto& fb = ban_window.window->texture();
+ if (!OSMesaMakeCurrent(gl_context, fb.pixels().data(), GL_UNSIGNED_BYTE, fb.width(), fb.height()))
+ return -1;
+ OSMesaPixelStore(OSMESA_Y_UP, 0);
+
+ return 0;
+}
@ -931,15 +890,6 @@ diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_video.cpp SDL-rele
+ glFinish();
+
+ auto& ban_window = *static_cast<banan_os_window*>(window->driverdata);
+
+ auto* src = ban_window.framebuffer.pixels().data();
+ auto* dst = ban_window.window->texture().pixels().data();
+
+ const size_t width = ban_window.window->width();
+ const size_t height = ban_window.window->height();
+ for (size_t y = 0; y < height; y++)
+ memcpy(&dst[(height - y - 1) * width], &src[y * width], width * 4);
+
+ ban_window.window->invalidate();
+
+ return 0;
@ -982,7 +932,6 @@ diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_video.cpp SDL-rele
+ device->UpdateWindowFramebuffer = BANAN_OS_UpdateWindowFramebuffer;
+ device->DestroyWindowFramebuffer = BANAN_OS_DestroyWindowFramebuffer;
+
+ device->WaitEventTimeout = BANAN_OS_WaitEventTimeout;
+ device->PumpEvents = BANAN_OS_PumpEvents;
+
+ device->GL_LoadLibrary = BANAN_OS_GL_LoadLibrary;
@ -1006,9 +955,9 @@ diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_video.cpp SDL-rele
+#endif /* SDL_VIDEO_DRIVER_BANAN_OS */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff -ruN SDL-release-2.30.11/src/video/SDL_sysvideo.h SDL-release-2.30.11-banan_os/src/video/SDL_sysvideo.h
diff -ruN SDL-release-2.30.11/src/video/SDL_sysvideo.h SDL2-2.30.11-banan_os/src/video/SDL_sysvideo.h
--- SDL-release-2.30.11/src/video/SDL_sysvideo.h 2025-01-01 19:09:38.000000000 +0200
+++ SDL-release-2.30.11-banan_os/src/video/SDL_sysvideo.h 2025-06-24 18:55:08.546768590 +0300
+++ SDL2-2.30.11-banan_os/src/video/SDL_sysvideo.h 2025-08-03 14:04:09.895655165 +0300
@@ -462,6 +462,7 @@
extern VideoBootStrap WINDOWS_bootstrap;
extern VideoBootStrap WINRT_bootstrap;
@ -1017,9 +966,9 @@ diff -ruN SDL-release-2.30.11/src/video/SDL_sysvideo.h SDL-release-2.30.11-banan
extern VideoBootStrap PND_bootstrap;
extern VideoBootStrap UIKIT_bootstrap;
extern VideoBootStrap Android_bootstrap;
diff -ruN SDL-release-2.30.11/src/video/SDL_video.c SDL-release-2.30.11-banan_os/src/video/SDL_video.c
diff -ruN SDL-release-2.30.11/src/video/SDL_video.c SDL2-2.30.11-banan_os/src/video/SDL_video.c
--- SDL-release-2.30.11/src/video/SDL_video.c 2025-01-01 19:09:38.000000000 +0200
+++ SDL-release-2.30.11-banan_os/src/video/SDL_video.c 2025-06-24 19:37:36.342677687 +0300
+++ SDL2-2.30.11-banan_os/src/video/SDL_video.c 2025-08-03 14:04:09.896007237 +0300
@@ -94,6 +94,9 @@
#ifdef SDL_VIDEO_DRIVER_HAIKU
&HAIKU_bootstrap,

27
ports/cmake/build.sh Executable file
View File

@ -0,0 +1,27 @@
#!/bin/bash ../install.sh
NAME='cmake'
VERSION='3.26.6' # NOTE: same version as used by our toolchain
DOWNLOAD_URL="https://github.com/Kitware/CMake/releases/download/v$VERSION/cmake-$VERSION.tar.gz#070b9a2422e666d2c1437e2dab239a236e8a63622d0a8d0ffe9e389613d2b76a"
DEPENDENCIES=('openssl' 'libuv' 'make' 'bash')
configure() {
$BANAN_CMAKE \
--toolchain=$BANAN_TOOLCHAIN_DIR/Toolchain.txt \
-B build -GNinja --fresh \
-DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_USE_OPENSSL=ON \
-DCMAKE_USE_SYSTEM_LIBUV=ON \
-DOPENSSL_ROOT_DIR=/usr \
-DBUILD_TESTING=OFF \
. || exit 1
}
build() {
$BANAN_CMAKE --build build || exit 1
}
install() {
$BANAN_CMAKE --install build || exit 1
cp $BANAN_TOOLCHAIN_DIR/cmake-platform/* $BANAN_SYSROOT/usr/share/cmake-3.26/Modules/Platform/
}

View File

@ -0,0 +1,12 @@
diff -ruN cmake-3.26.6/Source/kwsys/ProcessUNIX.c cmake-3.26.6-x86_64/Source/kwsys/ProcessUNIX.c
--- cmake-3.26.6/Source/kwsys/ProcessUNIX.c 2023-11-27 20:48:12.000000000 +0200
+++ cmake-3.26.6-x86_64/Source/kwsys/ProcessUNIX.c 2025-07-30 21:49:08.783313124 +0300
@@ -115,7 +115,7 @@
#endif
/* Some platforms do not have siginfo on their signal handlers. */
-#if defined(SA_SIGINFO) && !defined(__BEOS__)
+#if defined(SA_SIGINFO) && !defined(__BEOS__) && !defined(__banan_os__)
# define KWSYSPE_USE_SIGINFO 1
#endif

View File

@ -42,8 +42,9 @@ fi
export MESON_CROSS_FILE="$BANAN_PORT_DIR/$BANAN_ARCH-banan_os-meson.txt"
if [ ! -f "$MESON_CROSS_FILE" ] || [ "$MESON_CROSS_FILE" -ot "$BANAN_TOOLCHAIN_DIR/meson-cross-file.in" ]; then
cp "$BANAN_TOOLCHAIN_DIR/meson-cross-file.in" "$MESON_CROSS_FILE"
sed -i "s/ARCH/$BANAN_ARCH/" "$MESON_CROSS_FILE"
sed -i "s/SYSROOT/$BANAN_SYSROOT/" "$MESON_CROSS_FILE"
sed -i "s|ARCH|$BANAN_ARCH|" "$MESON_CROSS_FILE"
sed -i "s|CMAKE|$BANAN_CMAKE|" "$MESON_CROSS_FILE"
sed -i "s|SYSROOT|$BANAN_SYSROOT|" "$MESON_CROSS_FILE"
fi
MAKE_BUILD_TARGETS=('all')

23
ports/libuv/build.sh Executable file
View File

@ -0,0 +1,23 @@
#!/bin/bash ../install.sh
NAME='libuv'
VERSION='1.51.0'
DOWNLOAD_URL="https://dist.libuv.org/dist/v$VERSION/libuv-v$VERSION.tar.gz#5f0557b90b1106de71951a3c3931de5e0430d78da1d9a10287ebc7a3f78ef8eb"
TAR_CONTENT="libuv-v$VERSION"
configure() {
$BANAN_CMAKE \
--toolchain="$BANAN_TOOLCHAIN_DIR/Toolchain.txt" \
-B build -GNinja --fresh \
-DCMAKE_INSTALL_PREFIX=/usr \
-DBUILD_TESTING=OFF \
. || exit 1
}
build() {
$BANAN_CMAKE --build build || exit 1
}
install() {
$BANAN_CMAKE --install build || exit 1
}

View File

@ -0,0 +1,21 @@
diff -ruN libuv-v1.51.0/src/unix/tty.c libuv-1.51.0-x86_64/src/unix/tty.c
--- libuv-v1.51.0/src/unix/tty.c 2025-04-25 12:50:27.000000000 +0300
+++ libuv-1.51.0-x86_64/src/unix/tty.c 2025-07-23 17:12:21.857957334 +0300
@@ -30,7 +30,7 @@
#include <errno.h>
#include <sys/ioctl.h>
-#if defined(__MVS__) && !defined(IMAXBEL)
+#if (defined(__MVS__) || defined(__banan_os__)) && !defined(IMAXBEL)
#define IMAXBEL 0
#endif
@@ -244,7 +244,7 @@
static void uv__tty_make_raw(struct termios* tio) {
assert(tio != NULL);
-#if defined __sun || defined __MVS__
+#if defined __sun || defined __MVS__ || defined(__banan_os__)
/*
* This implementation of cfmakeraw for Solaris and derivatives is taken from
* http://www.perkin.org.uk/posts/solaris-portability-cfmakeraw.html.

View File

@ -0,0 +1,12 @@
diff -ruN libuv-v1.51.0/src/unix/core.c libuv-1.51.0-x86_64/src/unix/core.c
--- libuv-v1.51.0/src/unix/core.c 2025-04-25 12:50:27.000000000 +0300
+++ libuv-1.51.0-x86_64/src/unix/core.c 2025-07-23 17:11:06.844349261 +0300
@@ -1036,7 +1036,7 @@
rusage->ru_stime.tv_sec = usage.ru_stime.tv_sec;
rusage->ru_stime.tv_usec = usage.ru_stime.tv_usec;
-#if !defined(__MVS__) && !defined(__HAIKU__)
+#if !defined(__MVS__) && !defined(__HAIKU__) && !defined(__banan_os__)
rusage->ru_maxrss = usage.ru_maxrss;
rusage->ru_ixrss = usage.ru_ixrss;
rusage->ru_idrss = usage.ru_idrss;

View File

@ -0,0 +1,33 @@
diff -ruN libuv-v1.51.0/src/unix/fs.c libuv-1.51.0-x86_64/src/unix/fs.c
--- libuv-v1.51.0/src/unix/fs.c 2025-04-25 12:50:27.000000000 +0300
+++ libuv-1.51.0-x86_64/src/unix/fs.c 2025-07-23 17:14:28.118310214 +0300
@@ -77,7 +77,8 @@
defined(__MVS__) || \
defined(__NetBSD__) || \
defined(__HAIKU__) || \
- defined(__QNX__)
+ defined(__QNX__) || \
+ defined(__banan_os__)
# include <sys/statvfs.h>
#else
# include <sys/statfs.h>
@@ -683,7 +684,8 @@
defined(__MVS__) || \
defined(__NetBSD__) || \
defined(__HAIKU__) || \
- defined(__QNX__)
+ defined(__QNX__) || \
+ defined(__banan_os__)
struct statvfs buf;
if (0 != statvfs(req->path, &buf))
@@ -705,7 +707,8 @@
defined(__OpenBSD__) || \
defined(__NetBSD__) || \
defined(__HAIKU__) || \
- defined(__QNX__)
+ defined(__QNX__) || \
+ defined(__banan_os__)
stat_fs->f_type = 0; /* f_type is not supported. */
#else
stat_fs->f_type = buf.f_type;

View File

@ -0,0 +1,13 @@
diff -ruN libuv-v1.51.0/src/unix/process.c libuv-1.51.0-x86_64/src/unix/process.c
--- libuv-v1.51.0/src/unix/process.c 2025-04-25 12:50:27.000000000 +0300
+++ libuv-1.51.0-x86_64/src/unix/process.c 2025-07-23 17:16:04.548824726 +0300
@@ -390,7 +390,9 @@
* aren't root, so don't bother checking the return value, this
* is just done as an optimistic privilege dropping function.
*/
+ #if !defined(__banan_os__)
SAVE_ERRNO(setgroups(0, NULL));
+ #endif
}
if ((options->flags & UV_PROCESS_SETGID) && setgid(options->gid))

View File

@ -0,0 +1,22 @@
diff -ruN libuv-v1.51.0/src/unix/thread.c libuv-1.51.0-x86_64/src/unix/thread.c
--- libuv-v1.51.0/src/unix/thread.c 2025-04-25 12:50:27.000000000 +0300
+++ libuv-1.51.0-x86_64/src/unix/thread.c 2025-07-23 17:16:19.619749435 +0300
@@ -897,7 +897,7 @@
abort();
}
-#if defined(_AIX) || defined(__MVS__) || defined(__PASE__)
+#if defined(_AIX) || defined(__MVS__) || defined(__PASE__) || defined(__banan_os__)
int uv__thread_setname(const char* name) {
return UV_ENOSYS;
}
@@ -938,7 +938,8 @@
#if (defined(__ANDROID_API__) && __ANDROID_API__ < 26) || \
defined(_AIX) || \
defined(__MVS__) || \
- defined(__PASE__)
+ defined(__PASE__) || \
+ defined(__banan_os__)
int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) {
return UV_ENOSYS;
}

View File

@ -0,0 +1,23 @@
diff -ruN libuv-v1.51.0/src/unix/udp.c libuv-1.51.0-x86_64/src/unix/udp.c
--- libuv-v1.51.0/src/unix/udp.c 2025-04-25 12:50:27.000000000 +0300
+++ libuv-1.51.0-x86_64/src/unix/udp.c 2025-07-23 17:15:38.986952773 +0300
@@ -767,7 +767,8 @@
!defined(__ANDROID__) && \
!defined(__DragonFly__) && \
!defined(__QNX__) && \
- !defined(__GNU__)
+ !defined(__GNU__) && \
+ !defined(__banan_os__)
static int uv__udp_set_source_membership4(uv_udp_t* handle,
const struct sockaddr_in* multicast_addr,
const char* interface_addr,
@@ -958,7 +959,8 @@
!defined(__ANDROID__) && \
!defined(__DragonFly__) && \
!defined(__QNX__) && \
- !defined(__GNU__)
+ !defined(__GNU__) && \
+ !defined(__banan_os__)
int err;
union uv__sockaddr mcast_addr;
union uv__sockaddr src_addr;

View File

@ -0,0 +1,32 @@
diff -ruN libuv-v1.51.0/include/uv/unix.h libuv-1.51.0-x86_64/include/uv/unix.h
--- libuv-v1.51.0/include/uv/unix.h 2025-04-25 12:50:27.000000000 +0300
+++ libuv-1.51.0-x86_64/include/uv/unix.h 2025-07-28 14:06:18.220856588 +0300
@@ -66,7 +66,8 @@
defined(__MSYS__) || \
defined(__HAIKU__) || \
defined(__QNX__) || \
- defined(__GNU__)
+ defined(__GNU__) || \
+ defined(__banan_os__)
# include "uv/posix.h"
#endif
diff -ruN libuv-v1.51.0/CMakeLists.txt libuv-1.51.0-x86_64/CMakeLists.txt
--- libuv-v1.51.0/CMakeLists.txt 2025-04-25 12:50:27.000000000 +0300
+++ libuv-1.51.0-x86_64/CMakeLists.txt 2025-07-28 13:57:21.518563402 +0300
@@ -283,6 +283,15 @@
list(APPEND uv_sources src/unix/proctitle.c)
endif()
+if(CMAKE_SYSTEM_NAME STREQUAL "banan-os")
+ list(APPEND uv_sources
+ src/unix/no-fsevents.c
+ src/unix/no-proctitle.c
+ src/unix/posix-hrtime.c
+ src/unix/posix-poll.c
+ )
+endif()
+
if(CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD")
list(APPEND uv_sources src/unix/freebsd.c)
endif()

34
ports/llvm/build.sh Executable file
View File

@ -0,0 +1,34 @@
#!/bin/bash ../install.sh
NAME='llvm'
VERSION='20.1.8'
DOWNLOAD_URL="https://github.com/llvm/llvm-project/releases/download/llvmorg-$VERSION/llvm-project-$VERSION.src.tar.xz#6898f963c8e938981e6c4a302e83ec5beb4630147c7311183cf61069af16333d"
TAR_CONTENT="llvm-project-$VERSION.src"
DEPENDENCIES=('zlib' 'zstd')
configure() {
unset CC CXX LD
$BANAN_CMAKE -B build -S llvm -G Ninja \
--toolchain="$BANAN_TOOLCHAIN_DIR/Toolchain.txt" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr \
-DLLVM_ENABLE_PROJECTS= \
-DLLVM_ENABLE_RTTI=ON \
-DLLVM_TARGETS_TO_BUILD=X86 \
-DLLVM_INCLUDE_BENCHMARKS=OFF \
-DLLVM_INCLUDE_TESTS=OFF \
-DLLVM_HOST_TRIPLE=x86_64-pc-banan_os \
-DLLVM_PARALLEL_LINK_JOBS=1 \
|| exit 1
}
build() {
$BANAN_CMAKE --build build || exit 1
}
install() {
# This port only contains llvm libraries used optionally by
# mesa port. There is no need to install and fill the disk :D
:
}

View File

@ -0,0 +1,107 @@
diff -ruN llvm-project-20.1.8.src/llvm/include/llvm/ADT/bit.h llvm-project-20.1.8.src-banan_os/llvm/include/llvm/ADT/bit.h
--- llvm-project-20.1.8.src/llvm/include/llvm/ADT/bit.h 2025-07-09 02:06:32.000000000 +0300
+++ llvm-project-20.1.8.src-banan_os/llvm/include/llvm/ADT/bit.h 2025-08-03 17:05:07.723852159 +0300
@@ -29,7 +29,7 @@
#if defined(__linux__) || defined(__GNU__) || defined(__HAIKU__) || \
defined(__Fuchsia__) || defined(__EMSCRIPTEN__) || defined(__NetBSD__) || \
- defined(__OpenBSD__) || defined(__DragonFly__)
+ defined(__OpenBSD__) || defined(__DragonFly__) || defined(__banan_os__)
#include <endian.h>
#elif defined(_AIX)
#include <sys/machine.h>
diff -ruN llvm-project-20.1.8.src/llvm/include/llvm/Support/ExitCodes.h llvm-project-20.1.8.src-banan_os/llvm/include/llvm/Support/ExitCodes.h
--- llvm-project-20.1.8.src/llvm/include/llvm/Support/ExitCodes.h 2025-07-09 02:06:32.000000000 +0300
+++ llvm-project-20.1.8.src-banan_os/llvm/include/llvm/Support/ExitCodes.h 2025-08-03 17:05:35.659696821 +0300
@@ -20,6 +20,8 @@
#if HAVE_SYSEXITS_H
#include <sysexits.h>
+#elif defined(__banan_os__)
+#define EX_IOERR 69
#elif __MVS__ || defined(_WIN32)
// <sysexits.h> does not exist on z/OS and Windows. The only value used in LLVM
// is EX_IOERR, which is used to signal a special error condition (broken pipe).
diff -ruN llvm-project-20.1.8.src/llvm/lib/Support/Unix/Path.inc llvm-project-20.1.8.src-banan_os/llvm/lib/Support/Unix/Path.inc
--- llvm-project-20.1.8.src/llvm/lib/Support/Unix/Path.inc 2025-07-09 02:06:32.000000000 +0300
+++ llvm-project-20.1.8.src-banan_os/llvm/lib/Support/Unix/Path.inc 2025-08-03 17:06:42.941322766 +0300
@@ -107,7 +107,7 @@
#endif
#if defined(__NetBSD__) || defined(__DragonFly__) || defined(__GNU__) || \
- defined(__MVS__)
+ defined(__MVS__) || defined(__banan_os__)
#define STATVFS_F_FLAG(vfs) (vfs).f_flag
#else
#define STATVFS_F_FLAG(vfs) (vfs).f_flags
@@ -317,6 +317,9 @@
return std::string(real_path);
break; // Found entry, but realpath failed.
}
+#elif defined(__banan_os__)
+ fprintf(stddbg, "TODO: getMainExecutable\n");
+ return "";
#elif defined(HAVE_DLOPEN)
// Use dladdr to get executable path if available.
Dl_info DLInfo;
@@ -506,6 +509,9 @@
#elif defined(__HAIKU__)
// Haiku doesn't expose this information.
return false;
+#elif defined(__banan_os__)
+ // banan-os doesn't currently support remote filesystem mounts.
+ return true;
#elif defined(__sun)
// statvfs::f_basetype contains a null-terminated FSType name of the mounted
// target
diff -ruN llvm-project-20.1.8.src/llvm/lib/Support/Unix/Program.inc llvm-project-20.1.8.src-banan_os/llvm/lib/Support/Unix/Program.inc
--- llvm-project-20.1.8.src/llvm/lib/Support/Unix/Program.inc 2025-07-09 02:06:32.000000000 +0300
+++ llvm-project-20.1.8.src-banan_os/llvm/lib/Support/Unix/Program.inc 2025-08-03 17:04:50.500947936 +0300
@@ -342,7 +342,7 @@
namespace llvm {
namespace sys {
-#if defined(_AIX)
+#if defined(_AIX) || defined(__banan_os__)
static pid_t(wait4)(pid_t pid, int *status, int options, struct rusage *usage);
#elif !defined(__Fuchsia__)
using ::wait4;
@@ -383,6 +383,17 @@
// would cause the call here to fail with ECHILD).
return ::wait4(pid, status, options & ~WNOHANG, usage);
}
+#elif defined(__banan_os__)
+
+#ifndef _ALL_SOURCE
+extern "C" pid_t(wait4)(pid_t pid, int *status, int options,
+ struct rusage *usage);
+#endif
+pid_t(llvm::sys::wait4)(pid_t pid, int *status, int options,
+ struct rusage *usage) {
+ memset(usage, 0, sizeof(struct rusage));
+ return waitpid(pid, status, options);
+}
#endif
ProcessInfo llvm::sys::Wait(const ProcessInfo &PI,
@@ -468,7 +479,7 @@
std::chrono::microseconds UserT = toDuration(Info.ru_utime);
std::chrono::microseconds KernelT = toDuration(Info.ru_stime);
uint64_t PeakMemory = 0;
-#if !defined(__HAIKU__) && !defined(__MVS__)
+#if !defined(__HAIKU__) && !defined(__MVS__) && !defined(__banan_os__)
PeakMemory = static_cast<uint64_t>(Info.ru_maxrss);
#endif
*ProcStat = ProcessStatistics{UserT + KernelT, UserT, PeakMemory};
diff -ruN llvm-project-20.1.8.src/llvm/lib/Support/Unix/Signals.inc llvm-project-20.1.8.src-banan_os/llvm/lib/Support/Unix/Signals.inc
--- llvm-project-20.1.8.src/llvm/lib/Support/Unix/Signals.inc 2025-07-09 02:06:32.000000000 +0300
+++ llvm-project-20.1.8.src-banan_os/llvm/lib/Support/Unix/Signals.inc 2025-08-03 17:03:20.739447219 +0300
@@ -817,7 +817,7 @@
OS << "Stack dump without symbol names (ensure you have llvm-symbolizer in "
"your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point "
"to it):\n";
-#if HAVE_DLOPEN && !defined(_AIX)
+#if HAVE_DLOPEN && !defined(_AIX) && !defined(__banan_os__)
int width = 0;
for (int i = 0; i < depth; ++i) {
Dl_info dlinfo;

View File

@ -7,7 +7,6 @@ DEPENDENCIES=('zlib' 'zstd' 'expat')
CONFIGURE_OPTIONS=(
'-Dprefix=/usr'
'-Dosmesa=true'
'-Dgallium-drivers=softpipe'
'-Dvulkan-drivers=[]'
'-Dplatforms=[]'
'-Dglx=disabled'
@ -15,7 +14,50 @@ CONFIGURE_OPTIONS=(
)
configure() {
meson setup --reconfigure --cross-file "$MESON_CROSS_FILE" "${CONFIGURE_OPTIONS[@]}" build || exit 1
llvm_version='20.1.9'
llvm_root="../../llvm/llvm-$llvm_version-$BANAN_ARCH"
gallium_driver=softpipe
if [ -d "$llvm_root" ]; then
llvm_lib=$(realpath "$llvm_root/build/lib")
mkdir -p subprojects/llvm
wrap_file='subprojects/llvm/meson.build'
echo "project('llvm', ['cpp'])" >$wrap_file
echo "" >>$wrap_file
echo "cpp = meson.get_compiler('cpp')" >>$wrap_file
echo "" >>$wrap_file
echo "_deps = []" >>$wrap_file
echo "_search = '$llvm_lib'" >>$wrap_file
echo "foreach d : [" >>$wrap_file
for path in $llvm_lib/libLLVM*.a; do
name=$(basename $path)
echo " '${name:3:-2}'," >>$wrap_file
done
echo " ]" >>$wrap_file
echo " _deps += cpp.find_library(d, dirs : _search)" >>$wrap_file
echo "endforeach" >>$wrap_file
echo "" >>$wrap_file
echo "dep_llvm = declare_dependency(" >>$wrap_file
echo " include_directories : include_directories(" >>$wrap_file
echo " '$(realpath $llvm_root/llvm/include)'," >>$wrap_file
echo " '$(realpath $llvm_root/build/include)'," >>$wrap_file
echo " )," >>$wrap_file
echo " dependencies : _deps," >>$wrap_file
echo " version : '$llvm_version'," >>$wrap_file
echo ")" >>$wrap_file
gallium_driver=llvmpipe
fi
meson setup \
--reconfigure \
--cross-file "$MESON_CROSS_FILE" \
"${CONFIGURE_OPTIONS[@]}" \
"-Dgallium-drivers=$gallium_driver" \
build || exit 1
}
build() {

View File

@ -0,0 +1,21 @@
diff -ru mesa-25.0.7-banan_os/src/gallium/drivers/llvmpipe/lp_texture.c mesa-25.0.7-x86_64/src/gallium/drivers/llvmpipe/lp_texture.c
--- mesa-25.0.7-banan_os/src/gallium/drivers/llvmpipe/lp_texture.c 2025-05-28 18:20:23.000000000 +0300
+++ mesa-25.0.7-x86_64/src/gallium/drivers/llvmpipe/lp_texture.c 2025-08-03 19:16:20.254971098 +0300
@@ -59,7 +59,7 @@
#include "frontend/sw_winsys.h"
#include "git_sha1.h"
-#ifndef _WIN32
+#if !defined(_WIN32) && !defined(__banan_os__)
#include "drm-uapi/drm_fourcc.h"
#endif
@@ -1713,7 +1713,7 @@
case PIPE_RESOURCE_PARAM_LAYER_STRIDE:
*value = lpr->img_stride[level];
return true;
-#ifndef _WIN32
+#if !defined(_WIN32) && !defined(__banan_os__)
case PIPE_RESOURCE_PARAM_MODIFIER:
*value = lpr->dmabuf ? DRM_FORMAT_MOD_LINEAR : DRM_FORMAT_MOD_INVALID;
return true;

View File

@ -13,8 +13,7 @@ CONFIGURE_OPTIONS=(
'--with-shared'
'--without-ada'
'--without-manpages'
'--without-dlsym'
'--without-cxx-binding'
CFLAGS='-std=c17'
)
post_install() {

View File

@ -20,7 +20,7 @@ configure() {
done
popd
./waf configure -T release --disable-gl || exit 1
./waf configure -T release || exit 1
}
build() {
@ -29,6 +29,7 @@ build() {
install() {
./waf install --destdir=$BANAN_SYSROOT/home/user/halflife || exit 1
patchelf --add-needed libxash.so $BANAN_SYSROOT/home/user/halflife/xash3d
cat > $BANAN_SYSROOT/home/user/halflife/start.sh << EOF
#!/bin/Shell

View File

@ -1,17 +1,20 @@
#!/bin/bash ../install.sh
NAME='zstd'
VERSION='1.5.6'
DOWNLOAD_URL="https://github.com/facebook/zstd/releases/download/v$VERSION/zstd-$VERSION.tar.gz#8c29e06cf42aacc1eafc4077ae2ec6c6fcb96a626157e0593d5e82a34fd403c1"
VERSION='1.5.7'
DOWNLOAD_URL="https://github.com/facebook/zstd/releases/download/v$VERSION/zstd-$VERSION.tar.gz#eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3"
configure() {
:
$BANAN_CMAKE -B _build -S build/cmake -G Ninja \
--toolchain $BANAN_TOOLCHAIN_DIR/Toolchain.txt \
-DCMAKE_INSTALL_PREFIX=/usr \
|| exit 1
}
build() {
make -C lib -j$(nproc) lib-nomt || exit 1
$BANAN_CMAKE --build _build ||exit 1
}
install() {
make -C lib install "DESTDIR=$BANAN_SYSROOT" PREFIX=/usr || exit 1
$BANAN_CMAKE --install _build ||exit 1
}

View File

@ -4,13 +4,11 @@ set -e
export BANAN_SCRIPT_DIR=$(dirname $(realpath $0))
source $BANAN_SCRIPT_DIR/config.sh
FAKEROOT_FILE="$BANAN_BUILD_DIR/fakeroot-context"
run_fakeroot() {
if [ ! -f $FAKEROOT_FILE ]; then
touch $FAKEROOT_FILE
if [ ! -f $BANAN_FAKEROOT ]; then
touch $BANAN_FAKEROOT
fi
fakeroot -i $FAKEROOT_FILE -s $FAKEROOT_FILE -- /bin/bash -c '$@' bash $@
fakeroot -i $BANAN_FAKEROOT -s $BANAN_FAKEROOT -- /bin/bash -c '$@' bash $@
}
make_build_dir () {
@ -47,11 +45,6 @@ build_toolchain () {
create_image () {
build_target all
build_target install
pushd $BANAN_SYSROOT >/dev/null
run_fakeroot tar cf ${BANAN_SYSROOT_TAR} *
popd >/dev/null
$BANAN_SCRIPT_DIR/image.sh "$1"
}

View File

@ -20,7 +20,8 @@ export BANAN_BUILD_DIR="$BANAN_ROOT_DIR/build"
export BANAN_PORT_DIR="$BANAN_ROOT_DIR/ports"
export BANAN_SYSROOT="$BANAN_BUILD_DIR/sysroot"
export BANAN_SYSROOT_TAR="$BANAN_SYSROOT.tar"
export BANAN_FAKEROOT="$BANAN_BUILD_DIR/fakeroot-context"
export BANAN_DISK_IMAGE_PATH="$BANAN_BUILD_DIR/banan-os.img"

View File

@ -5,8 +5,13 @@ if [ -z $BANAN_DISK_IMAGE_PATH ]; then
exit 1
fi
if [ -z $BANAN_SYSROOT_TAR ]; then
echo "You must set the BANAN_SYSROOT_TAR environment variable" >&2
if [ -z $BANAN_SYSROOT ]; then
echo "You must set the BANAN_SYSROOT environment variable" >&2
exit 1
fi
if [ -z $BANAN_FAKEROOT ]; then
echo "You must set the BANAN_FAKEROOT environment variable" >&2
exit 1
fi
@ -34,13 +39,33 @@ fi
if sudo mount $ROOT_PARTITION $MOUNT_DIR; then
if (($BANAN_INITRD)); then
fakeroot -i $BANAN_FAKEROOT tar -C $BANAN_SYSROOT -cf $BANAN_SYSROOT.initrd .
sudo mkdir -p $MOUNT_DIR/boot
sudo cp $BANAN_BUILD_DIR/kernel/banan-os.kernel $MOUNT_DIR/boot/banan-os.kernel
sudo cp $BANAN_SYSROOT_TAR $MOUNT_DIR/boot/banan-os.initrd
sudo mv $BANAN_SYSROOT.initrd $MOUNT_DIR/boot/banan-os.initrd
else
cd $MOUNT_DIR
sudo tar xf $BANAN_SYSROOT_TAR
cd
sudo rsync -rulHpt $BANAN_SYSROOT/ $MOUNT_DIR
fakeroot -i $BANAN_FAKEROOT find $BANAN_SYSROOT -printf './%P|%U|%G\n' >$BANAN_BUILD_DIR/sysroot-perms.txt
sudo bash -c "
if enable stat &>/dev/null; then
while IFS='|' read -r path uid gid; do
full=\"$MOUNT_DIR/\$path\"
stat \"\$full\"
if [[ \${STAT[uid]} != \$uid ]] || [[ \${STAT[gid]} != \$gid ]]; then
chown -h \"\$uid:\$gid\" \"\$full\"
fi
done <$BANAN_BUILD_DIR/sysroot-perms.txt
else
while IFS='|' read -r path uid gid; do
full=\"$MOUNT_DIR/\$path\"
if [[ \$(stat -c '%u %g' \"\$full\") != \"\$uid \$gid\" ]]; then
chown -h \"\$uid:\$gid\" \"\$full\"
fi
done <$BANAN_BUILD_DIR/sysroot-perms.txt
fi
"
fi
sudo umount $MOUNT_DIR

View File

@ -1 +1 @@
*/
local/

View File

@ -8,15 +8,10 @@ if (NOT DEFINED ENV{BANAN_SYSROOT})
endif ()
set(BANAN_SYSROOT $ENV{BANAN_SYSROOT})
if (NOT DEFINED ENV{BANAN_SYSROOT_TAR})
message(FATAL_ERROR "environment variable BANAN_SYSROOT_TAR not defined")
if (NOT DEFINED ENV{BANAN_TOOLCHAIN_PREFIX})
message(FATAL_ERROR "environment variable BANAN_TOOLCHAIN_PREFIX not defined")
endif ()
set(BANAN_SYSROOT_TAR $ENV{BANAN_SYSROOT_TAR})
if (NOT DEFINED ENV{BANAN_ROOT_DIR})
message(FATAL_ERROR "environment variable BANAN_ROOT_DIR not defined")
endif ()
set(TOOLCHAIN_PREFIX $ENV{BANAN_ROOT_DIR}/toolchain/local)
set(TOOLCHAIN_PREFIX $ENV{BANAN_TOOLCHAIN_PREFIX})
set(CMAKE_SYSTEM_NAME banan-os)
set(CMAKE_SYSTEM_PROCESSOR ${BANAN_ARCH})

View File

@ -193,6 +193,8 @@ build_cmake() {
cp -r ./bin/* $BANAN_TOOLCHAIN_PREFIX/bin/
cp -r ./share/* $BANAN_TOOLCHAIN_PREFIX/share/
cp $BANAN_TOOLCHAIN_DIR/cmake-platform/* $BANAN_TOOLCHAIN_PREFIX/share/cmake-3.26/Modules/Platform/
}
BUILD_BINUTILS=1

View File

@ -0,0 +1,2 @@
include(Platform/banan-os-GNU)
__banan_os_compiler_gnu(ASM)

View File

@ -0,0 +1,2 @@
include(Platform/banan-os-GNU)
__banan_os_compiler_gnu(C)

View File

@ -0,0 +1,2 @@
include(Platform/banan-os-GNU)
__banan_os_compiler_gnu(CXX)

View File

@ -0,0 +1,20 @@
# This module is shared by multiple languages; use include blocker.
include_guard()
macro(__banan_os_compiler_gnu lang)
set(CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG "-Wl,-rpath,")
set(CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG_SEP ":")
set(CMAKE_SHARED_LIBRARY_RPATH_LINK_${lang}_FLAG "-Wl,-rpath-link,")
set(CMAKE_SHARED_LIBRARY_SONAME_${lang}_FLAG "-Wl,-soname,")
set(CMAKE_EXE_EXPORTS_${lang}_FLAG "-Wl,--export-dynamic")
# Initialize link type selection flags. These flags are used when
# building a shared library, shared module, or executable that links
# to other libraries to select whether to use the static or shared
# versions of the libraries.
foreach(type SHARED_LIBRARY SHARED_MODULE EXE)
set(CMAKE_${type}_LINK_STATIC_${lang}_FLAGS "-Wl,-Bstatic")
set(CMAKE_${type}_LINK_DYNAMIC_${lang}_FLAGS "-Wl,-Bdynamic")
endforeach()
endmacro()

View File

@ -0,0 +1,2 @@
set(BANAN_OS 1)
set(UNIX 1)

View File

@ -0,0 +1,9 @@
set(CMAKE_DL_LIBS "")
set(CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN "\$ORIGIN")
set(CMAKE_SHARED_LIBRARY_SUFFIX ".so")
# Shared libraries with no builtin soname may not be linked safely by
# specifying the file path.
set(CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME 1)
include(Platform/UnixPaths)

View File

@ -11,7 +11,8 @@ ar = 'ARCH-pc-banan_os-ar'
ld = 'ARCH-pc-banan_os-ld'
objcopy = 'ARCH-pc-banan_os-objcopy'
strip = 'ARCH-pc-banan_os-strip'
pkgconfig = 'pkg-config'
pkg-config = 'pkg-config'
cmake = 'CMAKE'
[properties]
sys_root='SYSROOT'

View File

@ -20,6 +20,7 @@ set(LIBC_SOURCES
malloc.cpp
math.cpp
netdb.cpp
netinet/in.cpp
poll.cpp
printf_impl.cpp
pthread.cpp
@ -35,6 +36,7 @@ set(LIBC_SOURCES
sys/banan-os.cpp
sys/epoll.cpp
sys/file.cpp
sys/futex.cpp
sys/ioctl.cpp
sys/mman.cpp
sys/resource.cpp

View File

@ -2,6 +2,7 @@
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>
@ -109,3 +110,54 @@ void rewinddir(DIR* dirp)
dirp->entry_index = 0;
lseek(dirp->fd, 0, SEEK_SET);
}
int alphasort(const struct dirent** d1, const struct dirent** d2)
{
return strcoll((*d1)->d_name, (*d2)->d_name);
}
int scandir(const char* dir, struct dirent*** namelist, int (*sel)(const struct dirent*), int (*compar)(const struct dirent**, const struct dirent**))
{
DIR* dirp = opendir(dir);
if (dirp == nullptr)
return -1;
size_t count = 0;
dirent** list = nullptr;
dirent* dent;
while ((dent = readdir(dirp)))
{
if (sel && sel(dent) == 0)
continue;
void* new_list = realloc(list, (count + 1) * sizeof(dirent*));
if (new_list == nullptr)
goto scandir_error;
list = static_cast<dirent**>(new_list);
list[count] = static_cast<dirent*>(malloc(sizeof(dirent)));
if (list[count] == nullptr)
goto scandir_error;
memcpy(list[count], dent, sizeof(dirent));
count++;
}
closedir(dirp);
qsort(list, count, sizeof(dirent*), reinterpret_cast<int(*)(const void*, const void*)>(compar));
*namelist = list;
return count;
scandir_error:
closedir(dirp);
for (size_t i = 0; i < count; i++)
free(list[i]);
free(list);
*namelist = nullptr;
return -1;
}

View File

@ -1,8 +1,6 @@
#include <errno.h>
#include <pthread.h>
extern uthread* _get_uthread();
int* __errno_location()
{
return &_get_uthread()->errno_;

View File

@ -1,162 +1,213 @@
#include <BAN/StringView.h>
#include <BAN/Vector.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
static struct stat s_group_st;
static char* s_group_mmap = nullptr;
static int s_group_fd = -1;
static FILE* s_grent_fp = nullptr;
static group s_grent_struct;
static struct group s_group;
static char* s_grent_buffer = nullptr;
static size_t s_grent_buffer_size = 0;
id_t parse_id(BAN::StringView string)
void endgrent(void)
{
id_t id = 0;
for (char c : string)
{
if (!isdigit(c))
return -1;
id = (id * 10) + (c - '0');
}
return id;
if (s_grent_fp)
fclose(s_grent_fp);
s_grent_fp = nullptr;
if (s_grent_buffer)
free(s_grent_buffer);
s_grent_buffer = nullptr;
}
static bool open_group_file()
void setgrent(void)
{
if (s_group_fd == -1)
s_group_fd = open("/etc/group", O_RDONLY);
if (s_group_fd == -1)
return false;
if (fstat(s_group_fd, &s_group_st) == -1)
return false;
if (s_group_mmap == nullptr || s_group_mmap == MAP_FAILED)
s_group_mmap = (char*)mmap(nullptr, s_group_st.st_size, PROT_READ, MAP_PRIVATE, s_group_fd, 0);
if (s_group_mmap == MAP_FAILED)
return false;
s_group.gr_name = nullptr;
s_group.gr_mem = nullptr;
return true;
if (!s_grent_fp)
return;
fseek(s_grent_fp, 0, SEEK_SET);
}
struct group* fill_group(const BAN::Vector<BAN::StringView>& parts)
static int getgrent_impl(FILE* fp, struct group* grp, char* buffer, size_t bufsize, struct group** result)
{
if (parts.size() != 4)
return nullptr;
if (s_group.gr_name)
for (;;)
{
free(s_group.gr_name);
s_group.gr_name = nullptr;
if (fgets(buffer, bufsize, fp) == nullptr)
{
if (ferror(fp))
return errno;
*result = nullptr;
return 0;
}
if (s_group.gr_mem)
const size_t line_len = strlen(buffer);
if (line_len == 0)
continue;
if (buffer[line_len - 1] == '\n')
buffer[line_len - 1] = '\0';
else if (!feof(fp))
return (errno = ERANGE);
#define GET_STRING() ({ \
ptr = strchr(ptr, ':'); \
if (ptr == nullptr) \
continue; \
*ptr++ = '\0'; \
})
#define GET_INT() ({ \
if (!isdigit(*ptr)) \
continue; \
long val = 0; \
while (isdigit(*ptr)) \
val = (val * 10) + (*ptr++ - '0'); \
if (*ptr != ':') \
continue; \
*ptr++ = '\0'; \
val; \
})
char* ptr = buffer;
grp->gr_name = ptr;
GET_STRING();
grp->gr_passwd = ptr;
GET_STRING();
grp->gr_gid = GET_INT();
size_t offset = line_len + 1;
if (auto rem = offset % alignof(char*))
offset += alignof(char*) - rem;
grp->gr_mem = reinterpret_cast<char**>(buffer + offset);
const size_t mem_max = (bufsize - offset) / sizeof(char*);
size_t mem_idx = 0;
while (*ptr && mem_idx + 1 <= mem_max)
{
for (size_t i = 0; s_group.gr_mem && s_group.gr_mem[i]; i++)
free(s_group.gr_mem[i]);
free(s_group.gr_mem);
s_group.gr_mem = nullptr;
grp->gr_mem[mem_idx++] = ptr;
ptr = strchrnul(ptr, ',');
if (*ptr == ',')
*ptr++ = '\0';
}
auto groups_or_error = parts[3].split(',');
if (groups_or_error.is_error())
return nullptr;
auto groups = groups_or_error.release_value();
if (mem_idx + 1 > mem_max)
return (errno = ERANGE);
s_group.gr_gid = parse_id(parts[2]);
if (s_group.gr_gid == -1)
return nullptr;
s_group.gr_name = (char*)malloc(parts[0].size() + 1);
if (s_group.gr_name == nullptr)
return nullptr;
memcpy(s_group.gr_name, parts[0].data(), parts[0].size());
s_group.gr_name[parts[0].size()] = '\0';
s_group.gr_mem = (char**)malloc((groups.size() + 1) * sizeof(char*));
if (s_group.gr_mem == nullptr)
return nullptr;
for (size_t i = 0; i < groups.size(); i++)
{
s_group.gr_mem[i] = (char*)malloc(groups[i].size() + 1);
if (s_group.gr_mem[i] == nullptr)
{
for (size_t j = 0; j < i; j++)
free(s_group.gr_mem[j]);
free(s_group.gr_mem);
s_group.gr_mem = nullptr;
return nullptr;
grp->gr_mem[mem_idx] = nullptr;
*result = grp;
return 0;
}
memcpy(s_group.gr_mem[i], groups[i].data(), groups[i].size());
s_group.gr_mem[i][groups[i].size()] = '\0';
}
s_group.gr_mem[groups.size()] = nullptr;
return &s_group;
}
struct group* getgrnam(const char* name)
struct group* getgrent(void)
{
if (s_group_mmap == nullptr || s_group_mmap == MAP_FAILED)
if (!open_group_file())
return nullptr;
off_t start = 0;
off_t end = 0;
while (start < s_group_st.st_size)
if (s_grent_fp == nullptr)
{
while (end < s_group_st.st_size && s_group_mmap[end] != '\n')
end++;
BAN::StringView line(s_group_mmap + start, end - start);
start = ++end;
auto parts_or_error = line.split(':', true);
if (parts_or_error.is_error())
s_grent_fp = fopen("/etc/group", "r");
if (s_grent_fp == nullptr)
return nullptr;
auto parts = parts_or_error.release_value();
if (parts.size() == 4 && parts[0] == name)
return fill_group(parts);
}
if (s_grent_buffer == nullptr)
{
long size = sysconf(_SC_GETGR_R_SIZE_MAX);
if (size == -1)
size = 512;
s_grent_buffer = static_cast<char*>(malloc(size));
if (s_grent_buffer == nullptr)
return nullptr;
s_grent_buffer_size = size;
}
const off_t old_offset = ftello(s_grent_fp);
group* result;
for (;;)
{
const int error = getgrent_impl(s_grent_fp, &s_grent_struct, s_grent_buffer, s_grent_buffer_size, &result);
if (error == 0)
break;
fseeko(s_grent_fp, old_offset, SEEK_SET);
if (error != ERANGE)
return nullptr;
const size_t new_size = s_grent_buffer_size * 2;
char* new_buffer = static_cast<char*>(realloc(s_grent_buffer, new_size));
if (new_buffer == nullptr)
return nullptr;
s_grent_buffer = new_buffer;
s_grent_buffer_size = new_size;
}
return result;
}
struct group* getgrgid(gid_t gid)
{
if (s_group_mmap == nullptr || s_group_mmap == MAP_FAILED)
if (!open_group_file())
group* grp;
setgrent();
while ((grp = getgrent()))
if (grp->gr_gid == gid)
return grp;
return nullptr;
}
off_t start = 0;
off_t end = 0;
while (start < s_group_st.st_size)
struct group* getgrnam(const char* name)
{
while (end < s_group_st.st_size && s_group_mmap[end] != '\n')
end++;
BAN::StringView line(s_group_mmap + start, end - start);
start = ++end;
auto parts_or_error = line.split(':', true);
if (parts_or_error.is_error())
return nullptr;
auto parts = parts_or_error.release_value();
if (parts.size() == 4 && parse_id(parts[2]) == gid)
return fill_group(parts);
}
group* grp;
setgrent();
while ((grp = getgrent()))
if (strcmp(grp->gr_name, name) == 0)
return grp;
return nullptr;
}
int getgrgid_r(gid_t gid, struct group* grp, char* buffer, size_t bufsize, struct group** result)
{
FILE* fp = fopen("/etc/group", "r");
if (fp == nullptr)
return errno;
int ret = 0;
for (;;)
{
if ((ret = getgrent_impl(fp, grp, buffer, bufsize, result)))
break;
if (*result == nullptr)
break;
if (grp->gr_gid == gid)
break;
}
fclose(fp);
return ret;
}
int getgrnam_r(const char* name, struct group* grp, char* buffer, size_t bufsize, struct group** result)
{
FILE* fp = fopen("/etc/group", "r");
if (fp == nullptr)
return errno;
int ret = 0;
for (;;)
{
if ((ret = getgrent_impl(fp, grp, buffer, bufsize, result)))
break;
if (*result == nullptr)
break;
if (strcmp(grp->gr_name, name) == 0)
break;
}
fclose(fp);
return ret;
}

View File

@ -1,5 +1,7 @@
#include <stddef.h>
#include <limits.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
struct atexit_func_entry_t
{
@ -8,19 +10,52 @@ struct atexit_func_entry_t
void* dso_handle;
};
static atexit_func_entry_t s_atexit_funcs[ATEXIT_MAX];
static atexit_func_entry_t s_atexit_funcs_storage[ATEXIT_MAX];
static atexit_func_entry_t* s_atexit_funcs = s_atexit_funcs_storage;
static size_t s_atexit_funcs_capacity = ATEXIT_MAX;
static size_t s_atexit_func_count = 0;
static pthread_mutex_t s_atexit_mutex = PTHREAD_MUTEX_INITIALIZER;
extern "C" int __cxa_atexit(void(*func)(void*), void* arg, void* dso_handle)
{
if (s_atexit_func_count >= ATEXIT_MAX)
return -1;
pthread_mutex_lock(&s_atexit_mutex);
if (s_atexit_func_count >= s_atexit_funcs_capacity)
{
const size_t new_capacity = s_atexit_funcs_capacity * 2;
void* new_funcs = nullptr;
if (s_atexit_funcs == s_atexit_funcs_storage)
{
new_funcs = malloc(new_capacity * sizeof(atexit_func_entry_t));
if (new_funcs == nullptr)
goto __cxa_atexit_error;
memcpy(new_funcs, s_atexit_funcs, s_atexit_func_count * sizeof(atexit_func_entry_t));
}
else
{
new_funcs = realloc(s_atexit_funcs, new_capacity * sizeof(atexit_func_entry_t));
if (new_funcs == nullptr)
goto __cxa_atexit_error;
}
s_atexit_funcs = reinterpret_cast<atexit_func_entry_t*>(new_funcs);
s_atexit_funcs_capacity = new_capacity;
}
s_atexit_funcs[s_atexit_func_count++] = {
.func = func,
.arg = arg,
.dso_handle = dso_handle,
};
pthread_mutex_unlock(&s_atexit_mutex);
return 0;
__cxa_atexit_error:
pthread_mutex_unlock(&s_atexit_mutex);
return -1;
};
extern "C" void __cxa_finalize(void* dso_handle)

View File

@ -5,7 +5,7 @@
__BEGIN_DECLS
#include <inttypes.h>
#include <stdint.h>
#define INET_ADDRSTRLEN 16
#define INET6_ADDRSTRLEN 46

View File

@ -10,6 +10,8 @@ __BEGIN_DECLS
#include <bits/types/pthread_attr_t.h>
#include <bits/types/pthread_t.h>
#include <stdint.h>
typedef int pthread_once_t;
typedef unsigned pthread_key_t;
@ -28,17 +30,6 @@ typedef struct
unsigned lock_depth;
} pthread_mutex_t;
typedef struct
{
int shared;
} pthread_barrierattr_t;
typedef struct
{
pthread_barrierattr_t attr;
unsigned target;
unsigned waiting;
} pthread_barrier_t;
typedef struct
{
int clock;
@ -47,7 +38,7 @@ typedef struct
struct _pthread_cond_block
{
struct _pthread_cond_block* next;
int signaled;
uint32_t futex;
};
typedef struct
{
@ -56,6 +47,20 @@ typedef struct
struct _pthread_cond_block* block_list;
} pthread_cond_t;
typedef struct
{
int shared;
} pthread_barrierattr_t;
typedef struct
{
pthread_barrierattr_t attr;
pthread_mutex_t lock;
pthread_cond_t cond;
unsigned target;
unsigned waiting;
unsigned generation;
} pthread_barrier_t;
typedef struct
{
int shared;

View File

@ -14,6 +14,7 @@ __BEGIN_DECLS
struct group
{
char* gr_name; /* The name of the group. */
char* gr_passwd;/* The password of the group */
gid_t gr_gid; /* Numerical group ID. */
char** gr_mem; /* Pointer to a null-terminated array of character pointers to member names. */
};
@ -21,7 +22,7 @@ struct group
void endgrent(void);
struct group* getgrent(void);
struct group* getgrgid(gid_t gid);
int getgrgit_r(gid_t gid, struct group* grp, char* buffer, size_t bufsize, struct group** result);
int getgrgid_r(gid_t gid, struct group* grp, char* buffer, size_t bufsize, struct group** result);
struct group* getgrnam(const char* name);
int getgrnam_r(const char* name, struct group* grp, char* buffer, size_t bufsize, struct group** result);
void setgrent(void);

View File

@ -79,7 +79,7 @@ __BEGIN_DECLS
#define LOGIN_NAME_MAX 256
#define MQ_OPEN_MAX _POSIX_MQ_OPEN_MAX
#define MQ_PRIO_MAX _POSIX_MQ_PRIO_MAX
#define OPEN_MAX 64
#define OPEN_MAX 128
#define PAGESIZE PAGE_SIZE
#define PTHREAD_DESTRUCTOR_ITERATIONS _POSIX_THREAD_DESTRUCTOR_ITERATIONS
#define PTHREAD_KEYS_MAX _POSIX_THREAD_KEYS_MAX

View File

@ -9,7 +9,6 @@ __BEGIN_DECLS
#include <netinet/in.h>
#include <sys/socket.h>
#include <inttypes.h>
#define IPPORT_RESERVED 1024

View File

@ -87,6 +87,20 @@ struct uthread
#define _PTHREAD_ATFORK_CHILD 2
void _pthread_call_atfork(int state);
#if defined(__x86_64__)
#define _get_uthread() ({ \
struct uthread* __tmp; \
asm volatile("movq %%fs:0, %0" : "=r"(__tmp)); \
__tmp; \
})
#elif defined(__i686__)
#define _get_uthread() ({ \
struct uthread* __tmp; \
asm volatile("movl %%gs:0, %0" : "=r"(__tmp)); \
__tmp; \
})
#endif
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void(*child)(void));
int pthread_attr_destroy(pthread_attr_t* attr);
int pthread_attr_getdetachstate(const pthread_attr_t* attr, int* detachstate);

View File

@ -134,8 +134,9 @@ struct sigevent
#define SA_NODEFER 0x040
#define SS_ONSTACK 0x080
#define SS_DISABLE 0x100
#define MINSIGSTKSZ 0x200
#define SIGSTKSZ 0x400
#define MINSIGSTKSZ 4096
#define SIGSTKSZ 4096
typedef struct
{

View File

@ -44,6 +44,7 @@ void _Exit(int status) __attribute__((__noreturn__));
long a64l(const char* s);
void abort(void) __attribute__((__noreturn__));
int abs(int i);
void* aligned_alloc(size_t alignment, size_t size);
int atexit(void (*func)(void));
double atof(const char* str);
int atoi(const char* str);

View File

@ -0,0 +1,45 @@
#ifndef _SYS_FUTEX_H
#define _SYS_FUTEX_H 1
#include <sys/cdefs.h>
__BEGIN_DECLS
#include <stdint.h>
#include <time.h>
#define FUTEX_WAIT 0
#define FUTEX_WAKE 1
#define FUTEX_PRIVATE 0x10
#define FUTEX_REALTIME 0x20
#define FUTEX_WAIT_PRIVATE (FUTEX_WAIT | FUTEX_PRIVATE)
#define FUTEX_WAKE_PRIVATE (FUTEX_WAKE | FUTEX_PRIVATE)
// op is one of FUTEX_WAIT or FUTEX_WAKE optionally or'ed with FUTEX_PRIVATE and/or FUTEX_REALTIME
//
// FUTEX_WAIT
// put current thread to sleep until *addr != value or until timeout occurs
// timeout is specified as a absolute time or NULL for indefinite wait
//
// FUTEX_WAKE
// signals waiting futexes to recheck *addr. at most value threads are woken up
//
// FUTEX_PRIVATE
// limit futex wait/wake events to the current process
//
// FUTEX_REALTIME
// abstime corresponds to CLOCK_REALTIME instead of the default CLOCK_MONOTONIC
//
// ERRORS
// ETIMEDOUT timeout occured
// EINVAL addr is not aligned on 4 byte boundary
// ENOSYS op contains unrecognized value
// EINTR function was interrupted
// ENOMEM not enough memory to allocate futex object
// EAGAIN *addr != value before thread was put to sleep
int futex(int op, const uint32_t* addr, uint32_t value, const struct timespec* abstime);
__END_DECLS
#endif

View File

@ -16,6 +16,7 @@ __BEGIN_DECLS
#define MAP_PRIVATE 0x02
#define MAP_SHARED 0x04
#define MAP_ANONYMOUS 0x08
#define MAP_ANON MAP_ANONYMOUS
#define MS_ASYNC 0x01
#define MS_INVALIDATE 0x02

View File

@ -25,7 +25,7 @@ __BEGIN_DECLS
struct sockaddr
{
sa_family_t sa_family; /* Address family. */
char sa_data[]; /* Socket address (variable-length data). */
char sa_data[0]; /* Socket address (variable-length data). */
};
struct sockaddr_storage

View File

@ -52,6 +52,7 @@ __BEGIN_DECLS
O(SYS_SYNC, sync) \
O(SYS_MMAP, mmap) \
O(SYS_MUNMAP, munmap) \
O(SYS_MPROTECT, mprotect) \
O(SYS_TTY_CTRL, tty_ctrl) \
O(SYS_POWEROFF, poweroff) \
O(SYS_FCHMODAT, fchmodat) \
@ -108,6 +109,8 @@ __BEGIN_DECLS
O(SYS_EPOLL_CTL, epoll_ctl) \
O(SYS_EPOLL_PWAIT2, epoll_pwait2) \
O(SYS_FLOCK, flock) \
O(SYS_GET_NPROCESSOR, get_nprocessor) \
O(SYS_FUTEX, futex) \
enum Syscall
{

View File

@ -342,6 +342,10 @@ enum
#define _SC_MQ_PRIO_MAX _SC_MQ_PRIO_MAX
_SC_NGROUPS_MAX,
#define _SC_NGROUPS_MAX _SC_NGROUPS_MAX
_SC_NPROCESSORS_CONF,
#define _SC_NPROCESSORS_CONF _SC_NPROCESSORS_CONF
_SC_NPROCESSORS_ONLN,
#define _SC_NPROCESSORS_ONLN _SC_NPROCESSORS_ONLN
_SC_OPEN_MAX,
#define _SC_OPEN_MAX _SC_OPEN_MAX
_SC_PAGE_SIZE,

View File

@ -338,14 +338,14 @@ void* calloc(size_t nmemb, size_t size)
return ptr;
}
int posix_memalign(void** memptr, size_t alignment, size_t size)
void* aligned_alloc(size_t alignment, size_t size)
{
dprintln_if(DEBUG_MALLOC, "posix_memalign({}, {})", alignment, size);
dprintln_if(DEBUG_MALLOC, "aligned_alloc({}, {})", alignment, size);
if (alignment < sizeof(void*) || alignment % sizeof(void*) || !BAN::Math::is_power_of_two(alignment / sizeof(void*)))
{
errno = EINVAL;
return -1;
return nullptr;
}
if (alignment < s_malloc_default_align)
@ -353,7 +353,7 @@ int posix_memalign(void** memptr, size_t alignment, size_t size)
void* unaligned = malloc(size + alignment + sizeof(malloc_node_t));
if (unaligned == nullptr)
return -1;
return nullptr;
pthread_mutex_lock(&s_malloc_mutex);
@ -395,6 +395,11 @@ int posix_memalign(void** memptr, size_t alignment, size_t size)
pthread_mutex_unlock(&s_malloc_mutex);
assert(((uintptr_t)node->data & (alignment - 1)) == 0);
*memptr = node->data;
return 0;
return node->data;
}
int posix_memalign(void** memptr, size_t alignment, size_t size)
{
dprintln_if(DEBUG_MALLOC, "posix_memalign({}, {})", alignment, size);
return (*memptr = aligned_alloc(alignment, size)) ? 0 : -1;
}

View File

@ -0,0 +1,4 @@
#include <netinet/in.h>
const struct in6_addr in6addr_any = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
const struct in6_addr in6addr_loopback = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };

View File

@ -10,6 +10,7 @@
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/futex.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>
@ -54,17 +55,6 @@ extern "C" void _pthread_trampoline_cpp(void* arg)
ASSERT_NOT_REACHED();
}
uthread* _get_uthread()
{
uthread* result;
#if ARCH(x86_64)
asm volatile("movq %%fs:0, %0" : "=r"(result));
#elif ARCH(i686)
asm volatile("movl %%gs:0, %0" : "=r"(result));
#endif
return result;
}
static void free_uthread(uthread* uthread)
{
if (uthread->dtv[0] == 0)
@ -105,58 +95,95 @@ void pthread_cleanup_push(void (*routine)(void*), void* arg)
uthread->cleanup_stack = cleanup;
}
static thread_local struct {
void* value;
void (*destructor)(void*);
} s_pthread_keys[PTHREAD_KEYS_MAX] {};
static thread_local uint8_t s_pthread_keys_allocated[(PTHREAD_KEYS_MAX + 7) / 8];
static inline bool is_pthread_key_allocated(pthread_key_t key)
static thread_local struct
{
if (key >= PTHREAD_KEYS_MAX)
return false;
return s_pthread_keys_allocated[key / 8] & (1 << (key % 8));
}
void* value;
pthread_key_t key;
} s_pthread_key_values[PTHREAD_KEYS_MAX] {};
static pthread_key_t s_pthread_key_current = 1;
static pthread_key_t s_pthread_key_map[PTHREAD_KEYS_MAX] {};
static void (*s_pthread_key_destructors[PTHREAD_KEYS_MAX])(void*) {};
static pthread_spinlock_t s_pthread_key_lock = PTHREAD_SPIN_INITIALIZER;
int pthread_key_create(pthread_key_t* key, void (*destructor)(void*))
{
for (pthread_key_t i = 0; i < PTHREAD_KEYS_MAX; i++)
{
if (is_pthread_key_allocated(i))
continue;
s_pthread_keys[i].value = nullptr;
s_pthread_keys[i].destructor = destructor;
s_pthread_keys_allocated[i / 8] |= 1 << (i % 8);
*key = i;
return 0;
}
int ret = EAGAIN;
return EAGAIN;
pthread_spin_lock(&s_pthread_key_lock);
for (size_t i = 0; i < PTHREAD_KEYS_MAX; i++)
{
if (s_pthread_key_map[i])
continue;
s_pthread_key_destructors[i] = destructor;
s_pthread_key_map[i] = *key = s_pthread_key_current++;
ret = 0;
break;
}
pthread_spin_unlock(&s_pthread_key_lock);
return ret;
}
int pthread_key_delete(pthread_key_t key)
{
if (!is_pthread_key_allocated(key))
return EINVAL;
s_pthread_keys[key].value = nullptr;
s_pthread_keys[key].destructor = nullptr;
s_pthread_keys_allocated[key / 8] &= ~(1 << (key % 8));
return 0;
int ret = EINVAL;
pthread_spin_lock(&s_pthread_key_lock);
for (size_t i = 0; i < PTHREAD_KEYS_MAX; i++)
{
if (s_pthread_key_map[i] != key)
continue;
s_pthread_key_destructors[i] = nullptr;
s_pthread_key_map[i] = 0;
ret = 0;
break;
}
pthread_spin_unlock(&s_pthread_key_lock);
return ret;
}
void* pthread_getspecific(pthread_key_t key)
{
if (!is_pthread_key_allocated(key))
return nullptr;
return s_pthread_keys[key].value;
void* ret = nullptr;
pthread_spin_lock(&s_pthread_key_lock);
for (size_t i = 0; i < PTHREAD_KEYS_MAX; i++)
{
if (s_pthread_key_map[i] != key)
continue;
if (s_pthread_key_values[i].key != key)
{
s_pthread_key_values[i].key = key;
s_pthread_key_values[i].value = nullptr;
}
ret = s_pthread_key_values[i].value;
break;
}
pthread_spin_unlock(&s_pthread_key_lock);
return ret;
}
int pthread_setspecific(pthread_key_t key, const void* value)
{
if (!is_pthread_key_allocated(key))
return EINVAL;
s_pthread_keys[key].value = const_cast<void*>(value);
return 0;
int ret = EINVAL;
pthread_spin_lock(&s_pthread_key_lock);
for (size_t i = 0; i < PTHREAD_KEYS_MAX; i++)
{
if (s_pthread_key_map[i] != key)
continue;
if (s_pthread_key_values[i].key != key)
s_pthread_key_values[i].key = key;
s_pthread_key_values[i].value = const_cast<void*>(value);
ret = 0;
break;
}
pthread_spin_unlock(&s_pthread_key_lock);
return ret;
}
int pthread_attr_destroy(pthread_attr_t* attr)
@ -414,15 +441,22 @@ void pthread_exit(void* value_ptr)
for (size_t iteration = 0; iteration < PTHREAD_DESTRUCTOR_ITERATIONS; iteration++)
{
bool called = false;
for (pthread_key_t i = 0; i < PTHREAD_KEYS_MAX; i++)
for (size_t i = 0; i < PTHREAD_KEYS_MAX; i++)
{
if (!is_pthread_key_allocated(i))
void (*destructor)(void*) = nullptr;
void* value = nullptr;
pthread_spin_lock(&s_pthread_key_lock);
if (s_pthread_key_map[i] && s_pthread_key_values[i].key == s_pthread_key_map[i])
{
destructor = s_pthread_key_destructors[i];
value = s_pthread_key_values[i].value;
}
pthread_spin_unlock(&s_pthread_key_lock);
if (!value || !destructor)
continue;
if (!s_pthread_keys[i].value || !s_pthread_keys[i].destructor)
continue;
void* old_value = s_pthread_keys[i].value;
s_pthread_keys[i].value = nullptr;
s_pthread_keys[i].destructor(old_value);
destructor(value);
called = true;
}
if (!called)
@ -442,7 +476,14 @@ int pthread_equal(pthread_t t1, pthread_t t2)
int pthread_join(pthread_t thread, void** value_ptr)
{
pthread_testcancel();
return syscall(SYS_PTHREAD_JOIN, thread, value_ptr);
errno = 0;
while (syscall(SYS_PTHREAD_JOIN, thread, value_ptr) == -1 && errno == EINTR)
{
pthread_testcancel();
errno = 0;
}
return errno;
}
pthread_t pthread_self(void)
@ -478,6 +519,9 @@ static pthread_mutex_t s_atfork_mutex = PTHREAD_MUTEX_INITIALIZER;
void _pthread_call_atfork(int state)
{
if (state == _PTHREAD_ATFORK_CHILD)
_get_uthread()->id = syscall(SYS_PTHREAD_SELF);
pthread_mutex_lock(&s_atfork_mutex);
pthread_atfork_t* list = nullptr;
@ -1070,7 +1114,12 @@ int pthread_cond_broadcast(pthread_cond_t* cond)
{
pthread_spin_lock(&cond->lock);
for (auto* block = cond->block_list; block; block = block->next)
BAN::atomic_store(block->signaled, 1);
{
BAN::atomic_store(block->futex, 1);
const int op = FUTEX_WAKE | (cond->attr.shared ? 0 : FUTEX_PRIVATE);
futex(op, &block->futex, 1, nullptr);
}
pthread_spin_unlock(&cond->lock);
return 0;
}
@ -1078,8 +1127,13 @@ int pthread_cond_broadcast(pthread_cond_t* cond)
int pthread_cond_signal(pthread_cond_t* cond)
{
pthread_spin_lock(&cond->lock);
if (cond->block_list)
BAN::atomic_store(cond->block_list->signaled, 1);
if (auto* block = cond->block_list)
{
BAN::atomic_store(block->futex, 1);
const int op = FUTEX_WAKE | (cond->attr.shared ? 0 : FUTEX_PRIVATE);
futex(op, &block->futex, 1, nullptr);
}
pthread_spin_unlock(&cond->lock);
return 0;
}
@ -1094,35 +1148,26 @@ int pthread_cond_timedwait(pthread_cond_t* __restrict cond, pthread_mutex_t* __r
{
pthread_testcancel();
constexpr auto has_timed_out =
[](const struct timespec* abstime, clockid_t clock_id) -> bool
{
if (abstime == nullptr)
return false;
struct timespec curtime;
clock_gettime(clock_id, &curtime);
if (curtime.tv_sec < abstime->tv_sec)
return false;
if (curtime.tv_sec > abstime->tv_sec)
return true;
return curtime.tv_nsec >= abstime->tv_nsec;
};
pthread_spin_lock(&cond->lock);
_pthread_cond_block block = {
.next = cond->block_list,
.signaled = 0,
.futex = 0,
};
cond->block_list = &block;
pthread_spin_unlock(&cond->lock);
pthread_mutex_unlock(mutex);
while (BAN::atomic_load(block.signaled) == 0)
while (BAN::atomic_load(block.futex) == 0)
{
if (has_timed_out(abstime, cond->attr.clock))
const int op = FUTEX_WAIT
| (cond->attr.shared ? 0 : FUTEX_PRIVATE)
| (cond->attr.clock == CLOCK_REALTIME ? FUTEX_REALTIME : 0);
if (futex(op, &block.futex, 0, abstime) == -1 && errno == ETIMEDOUT)
{
pthread_mutex_lock(mutex);
return ETIMEDOUT;
sched_yield();
}
}
pthread_spin_lock(&cond->lock);
@ -1192,28 +1237,34 @@ int pthread_barrier_init(pthread_barrier_t* __restrict barrier, const pthread_ba
attr = &default_attr;
*barrier = {
.attr = *attr,
.lock = PTHREAD_MUTEX_INITIALIZER,
.cond = PTHREAD_COND_INITIALIZER,
.target = count,
.waiting = 0,
.generation = 0,
};
return 0;
}
int pthread_barrier_wait(pthread_barrier_t* barrier)
{
const unsigned index = BAN::atomic_add_fetch(barrier->waiting, 1);
pthread_mutex_lock(&barrier->lock);
// FIXME: this case should be handled, but should be relatively uncommon
// so i'll just roll with the easy implementation
ASSERT(index <= barrier->target);
const auto gen = barrier->generation;
barrier->waiting++;
if (index == barrier->target)
if (barrier->waiting == barrier->target)
{
BAN::atomic_store(barrier->waiting, 0);
barrier->waiting = 0;
barrier->generation++;
pthread_cond_broadcast(&barrier->cond);
pthread_mutex_unlock(&barrier->lock);
return PTHREAD_BARRIER_SERIAL_THREAD;
}
while (BAN::atomic_load(barrier->waiting))
sched_yield();
while (barrier->generation == gen)
pthread_cond_wait(&barrier->cond, &barrier->lock);
pthread_mutex_unlock(&barrier->lock);
return 0;
}

View File

@ -1,118 +1,142 @@
#include <BAN/Assert.h>
#include <ctype.h>
#include <errno.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static FILE* s_pwent_fp = nullptr;
static passwd s_pwent_struct;
static bool open_pwent()
{
if (s_pwent_fp)
return true;
s_pwent_fp = fopen("/etc/passwd", "r");
return s_pwent_fp;
}
static void clear_pwent(passwd& passwd)
{
if (passwd.pw_name)
free(passwd.pw_name);
passwd.pw_name = nullptr;
if (passwd.pw_dir)
free(passwd.pw_dir);
passwd.pw_dir = nullptr;
if (passwd.pw_shell)
free(passwd.pw_shell);
passwd.pw_shell = nullptr;
}
static char* s_pwent_buffer = nullptr;
static size_t s_pwent_buffer_size = 0;
void endpwent(void)
{
if (!s_pwent_fp)
return;
if (s_pwent_fp)
fclose(s_pwent_fp);
s_pwent_fp = nullptr;
clear_pwent(s_pwent_struct);
if (s_pwent_buffer)
free(s_pwent_buffer);
s_pwent_buffer = nullptr;
}
void setpwent(void)
{
if (!s_pwent_fp)
return;
fseek(s_pwent_fp, 0, SEEK_SET);
}
static int getpwent_impl(FILE* fp, struct passwd* pwd, char* buffer, size_t bufsize, struct passwd** result)
{
for (;;)
{
if (fgets(buffer, bufsize, fp) == nullptr)
{
if (ferror(fp))
return errno;
*result = nullptr;
return 0;
}
const size_t line_len = strlen(buffer);
if (line_len == 0)
continue;
if (buffer[line_len - 1] == '\n')
buffer[line_len - 1] = '\0';
else if (!feof(fp))
return (errno = ERANGE);
#define GET_STRING() ({ \
ptr = strchr(ptr, ':'); \
if (ptr == nullptr) \
continue; \
*ptr++ = '\0'; \
})
#define GET_INT() ({ \
if (!isdigit(*ptr)) \
continue; \
long val = 0; \
while (isdigit(*ptr)) \
val = (val * 10) + (*ptr++ - '0'); \
if (*ptr != ':') \
continue; \
*ptr++ = '\0'; \
val; \
})
char* ptr = buffer;
pwd->pw_name = ptr;
GET_STRING();
pwd->pw_passwd = ptr;
GET_STRING();
pwd->pw_uid = GET_INT();
pwd->pw_gid = GET_INT();
pwd->pw_gecos = ptr;
GET_STRING();
pwd->pw_dir = ptr;
GET_STRING();
pwd->pw_shell = ptr;
*result = pwd;
return 0;
}
}
struct passwd* getpwent(void)
{
if (!s_pwent_fp)
if (!open_pwent())
return nullptr;
clear_pwent(s_pwent_struct);
static char buffer[4096];
if (!fgets(buffer, sizeof(buffer), s_pwent_fp))
return nullptr;
size_t buffer_len = strlen(buffer);
ASSERT(buffer[buffer_len - 1] == '\n');
buffer[buffer_len - 1] = '\0';
buffer_len--;
const char* ptr = buffer;
for (int i = 0; i < 7; i++)
if (s_pwent_fp == nullptr)
{
char* end = strchr(ptr, ':');
ASSERT((i < 6) ? end != nullptr : end == nullptr);
if (!end)
end = buffer + buffer_len;
*end = '\0';
const size_t field_len = end - ptr;
switch (i)
{
case 0:
s_pwent_struct.pw_name = strndup(ptr, field_len + 1);
if (!s_pwent_struct.pw_name)
s_pwent_fp = fopen("/etc/passwd", "r");
if (s_pwent_fp == nullptr)
return nullptr;
break;
case 1:
s_pwent_struct.pw_passwd = strndup(ptr, field_len + 1);
if (!s_pwent_struct.pw_passwd)
return nullptr;
break;
case 2:
ASSERT(1 <= field_len && field_len <= 9);
for (size_t j = 0; j < field_len; j++)
ASSERT(isdigit(ptr[j]));
s_pwent_struct.pw_uid = atoi(ptr);
break;
case 3:
ASSERT(1 <= field_len && field_len <= 9);
for (size_t j = 0; j < field_len; j++)
ASSERT(isdigit(ptr[j]));
s_pwent_struct.pw_gid = atoi(ptr);
break;
case 4:
s_pwent_struct.pw_gecos = strndup(ptr, field_len + 1);
if (!s_pwent_struct.pw_gecos)
return nullptr;
break;
case 5:
s_pwent_struct.pw_dir = strndup(ptr, field_len + 1);
if (!s_pwent_struct.pw_dir)
return nullptr;
break;
case 6:
s_pwent_struct.pw_shell = strndup(ptr, field_len + 1);
if (!s_pwent_struct.pw_shell)
return nullptr;
break;
}
ptr = end + 1;
if (s_pwent_buffer == nullptr)
{
long size = sysconf(_SC_GETPW_R_SIZE_MAX);
if (size == -1)
size = 512;
s_pwent_buffer = static_cast<char*>(malloc(size));
if (s_pwent_buffer == nullptr)
return nullptr;
s_pwent_buffer_size = size;
}
return &s_pwent_struct;
const off_t old_offset = ftello(s_pwent_fp);
passwd* result;
for (;;)
{
const int error = getpwent_impl(s_pwent_fp, &s_pwent_struct, s_pwent_buffer, s_pwent_buffer_size, &result);
if (error == 0)
break;
fseeko(s_pwent_fp, old_offset, SEEK_SET);
if (error != ERANGE)
return nullptr;
const size_t new_size = s_pwent_buffer_size * 2;
char* new_buffer = static_cast<char*>(realloc(s_pwent_buffer, new_size));
if (new_buffer == nullptr)
return nullptr;
s_pwent_buffer = new_buffer;
s_pwent_buffer_size = new_size;
}
return result;
}
struct passwd* getpwnam(const char* name)
@ -135,9 +159,44 @@ struct passwd* getpwuid(uid_t uid)
return nullptr;
}
void setpwent(void)
int getpwuid_r(uid_t uid, struct passwd* pwd, char* buffer, size_t bufsize, struct passwd** result)
{
if (!s_pwent_fp)
return;
fseek(s_pwent_fp, 0, SEEK_SET);
FILE* fp = fopen("/etc/passwd", "r");
if (fp == nullptr)
return errno;
int ret = 0;
for (;;)
{
if ((ret = getpwent_impl(fp, pwd, buffer, bufsize, result)))
break;
if (*result == nullptr)
break;
if (pwd->pw_uid == uid)
break;
}
fclose(fp);
return ret;
}
int getpwnam_r(const char* name, struct passwd* pwd, char* buffer, size_t bufsize, struct passwd** result)
{
FILE* fp = fopen("/etc/passwd", "r");
if (fp == nullptr)
return errno;
int ret = 0;
for (;;)
{
if ((ret = getpwent_impl(fp, pwd, buffer, bufsize, result)))
break;
if (*result == nullptr)
break;
if (strcmp(pwd->pw_name, name) == 0)
break;
}
fclose(fp);
return ret;
}

View File

@ -542,6 +542,9 @@ int getc_unlocked(FILE* file)
if (file->eof)
return EOF;
if (file == stdin && file->buffer_type == _IOLBF && stdout->buffer_type == _IOLBF && stdout->buffer_idx)
fflush(stdout);
// read characters from ungetc
if (file->unget_buf_idx)
{

View File

@ -570,6 +570,45 @@ int mblen(const char* s, size_t n)
ASSERT_NOT_REACHED();
}
int mbtowc(wchar_t* __restrict pwc, const char* __restrict s, size_t n)
{
// no state-dependent encodings
if (s == nullptr)
return 0;
switch (__getlocale(LC_CTYPE))
{
case LOCALE_INVALID:
ASSERT_NOT_REACHED();
case LOCALE_POSIX:
if (pwc != nullptr)
*pwc = *s;
return *s ? 1 : 0;
case LOCALE_UTF8:
const auto* us = reinterpret_cast<const unsigned char*>(s);
const uint32_t length = BAN::UTF8::byte_length(*us);
if (length == BAN::UTF8::invalid || n < length)
{
errno = EILSEQ;
return -1;
}
const auto wch = BAN::UTF8::to_codepoint(us);
if (wch == BAN::UTF8::invalid)
{
errno = EILSEQ;
return -1;
}
if (pwc)
*pwc = wch;
return wch ? length : 0;
}
ASSERT_NOT_REACHED();
}
size_t mbstowcs(wchar_t* __restrict pwcs, const char* __restrict s, size_t n)
{
size_t written = 0;

View File

@ -0,0 +1,12 @@
#include <errno.h>
#include <sys/futex.h>
#include <sys/syscall.h>
#include <unistd.h>
int futex(int op, const uint32_t* addr, uint32_t value, const struct timespec* abstime)
{
errno = 0;
while (syscall(SYS_FUTEX, op, addr, value, abstime) == -1 && errno == EINTR)
errno = 0;
return errno;
}

View File

@ -24,6 +24,11 @@ int munmap(void* addr, size_t len)
return syscall(SYS_MUNMAP, addr, len);
}
int mprotect(void* addr, size_t len, int prot)
{
return syscall(SYS_MPROTECT, addr, len, prot);
}
int msync(void* addr, size_t len, int flags)
{
pthread_testcancel();
@ -45,8 +50,3 @@ int mlock(const void*, size_t)
{
ASSERT_NOT_REACHED();
}
int mprotect(void*, size_t, int)
{
ASSERT_NOT_REACHED();
}

View File

@ -1,3 +1,6 @@
#include <BAN/Debug.h>
#include <errno.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/syscall.h>
@ -33,7 +36,7 @@ int listen(int socket, int backlog)
ssize_t recv(int socket, void* __restrict buffer, size_t length, int flags)
{
pthread_testcancel();
// cancellation point in recvfrom
return recvfrom(socket, buffer, length, flags, nullptr, nullptr);
}
@ -53,7 +56,7 @@ ssize_t recvfrom(int socket, void* __restrict buffer, size_t length, int flags,
ssize_t send(int socket, const void* message, size_t length, int flags)
{
pthread_testcancel();
// cancellation point in sendto
return sendto(socket, message, length, flags, nullptr, 0);
}
@ -71,6 +74,74 @@ ssize_t sendto(int socket, const void* message, size_t length, int flags, const
return syscall(SYS_SENDTO, &arguments);
}
ssize_t recvmsg(int socket, struct msghdr* message, int flags)
{
if (CMSG_FIRSTHDR(message))
{
dwarnln("TODO: recvmsg ancillary data");
errno = ENOTSUP;
return -1;
}
size_t total_recv = 0;
for (int i = 0; i < message->msg_iovlen; i++)
{
const ssize_t nrecv = recvfrom(
socket,
message->msg_iov[i].iov_base,
message->msg_iov[i].iov_len,
flags,
static_cast<sockaddr*>(message->msg_name),
&message->msg_namelen
);
if (nrecv < 0)
return -1;
total_recv += nrecv;
if (static_cast<size_t>(nrecv) < message->msg_iov[i].iov_len)
break;
}
return total_recv;
}
ssize_t sendmsg(int socket, const struct msghdr* message, int flags)
{
if (CMSG_FIRSTHDR(message))
{
dwarnln("TODO: sendmsg ancillary data");
errno = ENOTSUP;
return -1;
}
size_t total_sent = 0;
for (int i = 0; i < message->msg_iovlen; i++)
{
const ssize_t nsend = sendto(
socket,
message->msg_iov[i].iov_base,
message->msg_iov[i].iov_len,
flags,
static_cast<sockaddr*>(message->msg_name),
message->msg_namelen
);
if (nsend < 0)
return -1;
total_sent += nsend;
if (static_cast<size_t>(nsend) < message->msg_iov[i].iov_len)
break;
}
return total_sent;
}
int socket(int domain, int type, int protocol)
{
return syscall(SYS_SOCKET, domain, type, protocol);

View File

@ -9,7 +9,7 @@ ssize_t readv(int fildes, const struct iovec* iov, int iovcnt)
size_t result = 0;
for (int i = 0; i < iovcnt; i++)
{
uint8_t* base = static_cast<uint8_t*>(iov->iov_base);
uint8_t* base = static_cast<uint8_t*>(iov[i].iov_base);
size_t nread = 0;
while (nread < iov[i].iov_len)
@ -33,7 +33,7 @@ ssize_t writev(int fildes, const struct iovec* iov, int iovcnt)
size_t result = 0;
for (int i = 0; i < iovcnt; i++)
{
const uint8_t* base = static_cast<const uint8_t*>(iov->iov_base);
const uint8_t* base = static_cast<const uint8_t*>(iov[i].iov_base);
size_t nwrite = 0;
while (nwrite < iov[i].iov_len)

View File

@ -787,12 +787,19 @@ char* getlogin(void)
char* ttyname(int fildes)
{
static char storage[_POSIX_TTY_NAME_MAX];
if (syscall(SYS_TTYNAME, fildes, storage) == -1)
static char storage[TTY_NAME_MAX];
if (ttyname_r(fildes, storage, sizeof(storage)) != 0)
return nullptr;
return storage;
}
int ttyname_r(int fildes, char* name, size_t namesize)
{
if (syscall(SYS_TTYNAME, fildes, name, namesize))
return errno;
return 0;
}
int access(const char* path, int amode)
{
return syscall(SYS_ACCESS, path, amode);
@ -875,7 +882,35 @@ size_t confstr(int name, char* buf, size_t len)
return 0;
}
long pathconf(const char* path, int name);
long fpathconf(int fd, int name)
{
(void)fd;
switch (name)
{
#define LIMIT_CASE(name) case _PC_##name: return name;
LIMIT_CASE(LINK_MAX)
LIMIT_CASE(MAX_CANON)
LIMIT_CASE(MAX_INPUT)
LIMIT_CASE(NAME_MAX)
LIMIT_CASE(PATH_MAX)
LIMIT_CASE(PIPE_BUF)
#undef LIMIT_CASE
#define POSIX_CASE(name) case _PC_##name: return _POSIX_##name;
POSIX_CASE(CHOWN_RESTRICTED)
POSIX_CASE(NO_TRUNC)
POSIX_CASE(VDISABLE)
#undef POSIX_CASE
}
errno = EINVAL;
return 0;
}
long pathconf(const char* path, int name)
{
(void)path;
return fpathconf(0, name);
}
long sysconf(int name)
{
@ -1015,6 +1050,9 @@ long sysconf(int name)
case _SC_PAGE_SIZE:
case _SC_PAGESIZE: return getpagesize();
case _SC_NPROCESSORS_ONLN: return syscall(SYS_GET_NPROCESSOR);
case _SC_NPROCESSORS_CONF: return syscall(SYS_GET_NPROCESSOR);
case _SC_CLK_TCK: return 100;
case _SC_GETGR_R_SIZE_MAX: return 1024;
case _SC_GETPW_R_SIZE_MAX: return 1024;

View File

@ -142,10 +142,10 @@ namespace LibGUI
return on_socket_error(__FUNCTION__);
}
void Window::set_mouse_capture(bool captured)
void Window::set_mouse_relative(bool enabled)
{
WindowPacket::WindowSetMouseCapture packet;
packet.captured = captured;
WindowPacket::WindowSetMouseRelative packet;
packet.enabled = enabled;
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
return on_socket_error(__FUNCTION__);

View File

@ -205,7 +205,7 @@ namespace LibGUI
WindowInvalidate,
WindowSetPosition,
WindowSetAttributes,
WindowSetMouseCapture,
WindowSetMouseRelative,
WindowSetSize,
WindowSetMinSize,
WindowSetMaxSize,
@ -266,8 +266,8 @@ namespace LibGUI
);
DEFINE_PACKET(
WindowSetMouseCapture,
bool, captured
WindowSetMouseRelative,
bool, enabled
);
DEFINE_PACKET(

View File

@ -43,7 +43,7 @@ namespace LibGUI
void invalidate(int32_t x, int32_t y, uint32_t width, uint32_t height);
void invalidate() { return invalidate(0, 0, width(), height()); }
void set_mouse_capture(bool captured);
void set_mouse_relative(bool enabled);
void set_fullscreen(bool fullscreen);
void set_title(BAN::StringView title);

View File

@ -1200,21 +1200,32 @@ static void initialize_tls(MasterTLS master_tls)
memcpy(tls_addr, master_tls.addr, master_tls.size);
uthread* uthread = reinterpret_cast<struct uthread*>(tls_addr + master_tls.size);
uthread->self = uthread;
uthread->master_tls_addr = master_tls.addr;
uthread->master_tls_size = master_tls.size;
uthread& uthread = *reinterpret_cast<struct uthread*>(tls_addr + master_tls.size);
uthread->dtv[0] = master_tls.module_count;
// uthread is prepared in libc init, but some other stuff may be calling pthread functions
// for example __cxa_guard_release calls pthread_cond_broadcast
uthread = {
.self = &uthread,
.master_tls_addr = master_tls.addr,
.master_tls_size = master_tls.size,
.cleanup_stack = nullptr,
.id = static_cast<pthread_t>(syscall<>(SYS_PTHREAD_SELF)),
.errno_ = 0,
.cancel_type = PTHREAD_CANCEL_DEFERRED,
.cancel_state = PTHREAD_CANCEL_ENABLE,
.canceled = false,
};
uthread.dtv[0] = master_tls.module_count;
for (size_t i = 0; i < s_loaded_file_count; i++)
{
const auto& elf = s_loaded_files[i];
if (elf.tls_addr == nullptr)
continue;
uthread->dtv[elf.tls_module] = reinterpret_cast<uintptr_t>(tls_addr) + uthread->master_tls_size - elf.tls_offset;
uthread.dtv[elf.tls_module] = reinterpret_cast<uintptr_t>(tls_addr) + uthread.master_tls_size - elf.tls_offset;
}
syscall(SYS_SET_TLS, uthread);
syscall(SYS_SET_TLS, &uthread);
}
static void initialize_environ(char** envp)
@ -1352,6 +1363,9 @@ void* __dlopen(const char* file, int mode)
// FIXME: RTLD_{LOCAL,GLOBAL}
if (file == nullptr)
return &s_loaded_files[0];
char path_buffer[PATH_MAX];
if (!find_library(file, path_buffer))
{

View File

@ -199,13 +199,13 @@ void WindowServer::on_window_set_attributes(int fd, const LibGUI::WindowPacket::
dwarnln("could not send window shown event: {}", ret.error());
}
void WindowServer::on_window_set_mouse_capture(int fd, const LibGUI::WindowPacket::WindowSetMouseCapture& packet)
void WindowServer::on_window_set_mouse_relative(int fd, const LibGUI::WindowPacket::WindowSetMouseRelative& packet)
{
if (m_is_mouse_captured && packet.captured)
if (m_is_mouse_relative && packet.enabled)
{
ASSERT(m_focused_window);
if (fd != m_focused_window->client_fd())
dwarnln("client tried to set mouse capture while other window has it already captured");
dwarnln("client tried to set mouse relative while other window has it already");
return;
}
@ -221,11 +221,11 @@ void WindowServer::on_window_set_mouse_capture(int fd, const LibGUI::WindowPacke
return;
}
if (packet.captured == m_is_mouse_captured)
if (packet.enabled == m_is_mouse_relative)
return;
set_focused_window(target_window);
m_is_mouse_captured = packet.captured;
m_is_mouse_relative = packet.enabled;
invalidate(cursor_area());
}
@ -490,7 +490,7 @@ void WindowServer::on_key_event(LibInput::KeyEvent event)
void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event)
{
if (m_is_mouse_captured)
if (m_is_mouse_relative)
{
ASSERT(m_focused_window);
@ -681,7 +681,7 @@ void WindowServer::on_mouse_move_impl(int32_t new_x, int32_t new_y)
void WindowServer::on_mouse_move(LibInput::MouseMoveEvent event)
{
if (m_is_mouse_captured)
if (m_is_mouse_relative)
{
ASSERT(m_focused_window);
@ -712,7 +712,7 @@ void WindowServer::on_mouse_move(LibInput::MouseMoveEvent event)
}
const int32_t new_x = BAN::Math::clamp(m_cursor.x + event.rel_x, min_x, max_x);
const int32_t new_y = BAN::Math::clamp(m_cursor.y + event.rel_y, min_y, max_y);
const int32_t new_y = BAN::Math::clamp(m_cursor.y - event.rel_y, min_y, max_y);
return on_mouse_move_impl(new_x, new_y);
}
@ -724,6 +724,12 @@ void WindowServer::on_mouse_move_abs(LibInput::MouseMoveAbsEvent event)
return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
};
if (m_is_mouse_relative)
{
dwarnln("relative mouse not supported with absolute mouse");
return;
}
int32_t out_min_x, out_max_x;
int32_t out_min_y, out_max_y;
@ -767,9 +773,9 @@ void WindowServer::set_focused_window(BAN::RefPtr<Window> window)
if (m_focused_window == window)
return;
if (m_is_mouse_captured)
if (m_is_mouse_relative)
{
m_is_mouse_captured = false;
m_is_mouse_relative = false;
invalidate(cursor_area());
}
@ -903,7 +909,7 @@ void WindowServer::invalidate(Rectangle area)
mark_pending_sync(dst_area);
}
if (!m_is_mouse_captured)
if (!m_is_mouse_relative)
{
auto cursor_area = this->cursor_area();
cursor_area.x -= m_focused_window->client_x();
@ -1140,7 +1146,7 @@ void WindowServer::invalidate(Rectangle area)
}
}
if (!m_is_mouse_captured)
if (!m_is_mouse_relative)
{
if (const auto overlap = cursor_area().get_overlap(area); overlap.has_value())
{
@ -1355,8 +1361,8 @@ BAN::RefPtr<Window> WindowServer::find_window_with_fd(int fd) const
BAN::RefPtr<Window> WindowServer::find_hovered_window() const
{
for (auto window : m_client_windows)
if (window->full_area().contains(m_cursor))
for (size_t i = m_client_windows.size(); i > 0; i--)
if (auto window = m_client_windows[i - 1]; window->client_area().contains(m_cursor))
return window;
return {};
}

View File

@ -35,7 +35,7 @@ public:
void on_window_invalidate(int fd, const LibGUI::WindowPacket::WindowInvalidate&);
void on_window_set_position(int fd, const LibGUI::WindowPacket::WindowSetPosition&);
void on_window_set_attributes(int fd, const LibGUI::WindowPacket::WindowSetAttributes&);
void on_window_set_mouse_capture(int fd, const LibGUI::WindowPacket::WindowSetMouseCapture&);
void on_window_set_mouse_relative(int fd, const LibGUI::WindowPacket::WindowSetMouseRelative&);
void on_window_set_size(int fd, const LibGUI::WindowPacket::WindowSetSize&);
void on_window_set_min_size(int fd, const LibGUI::WindowPacket::WindowSetMinSize&);
void on_window_set_max_size(int fd, const LibGUI::WindowPacket::WindowSetMaxSize&);
@ -112,7 +112,7 @@ private:
uint8_t m_resize_quadrant { 0 };
Position m_resize_start;
bool m_is_mouse_captured { false };
bool m_is_mouse_relative { false };
bool m_deleted_window { false };
bool m_is_stopped { false };

View File

@ -368,7 +368,7 @@ int main()
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(WindowSetMouseCapture, on_window_set_mouse_capture);
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);