forked from Bananymous/banan-os
Kernel: Implement MAP_PRIVATE file mappings
mmap() now supports mapping files with MAP_PRIVATE.
This commit is contained in:
parent
4a92f44cf6
commit
db5d6a7f80
|
@ -32,6 +32,7 @@ set(KERNEL_SOURCES
|
|||
kernel/Input/PS2Keymap.cpp
|
||||
kernel/InterruptController.cpp
|
||||
kernel/kernel.cpp
|
||||
kernel/Memory/FileBackedRegion.cpp
|
||||
kernel/Memory/GeneralAllocator.cpp
|
||||
kernel/Memory/Heap.cpp
|
||||
kernel/Memory/kmalloc.cpp
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/FS/Inode.h>
|
||||
#include <kernel/Memory/MemoryRegion.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class FileBackedRegion final : public MemoryRegion
|
||||
{
|
||||
BAN_NON_COPYABLE(FileBackedRegion);
|
||||
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);
|
||||
~FileBackedRegion();
|
||||
|
||||
virtual BAN::ErrorOr<bool> allocate_page_containing(vaddr_t vaddr) override;
|
||||
|
||||
virtual BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> clone(PageTable& new_page_table) override;
|
||||
|
||||
private:
|
||||
FileBackedRegion(BAN::RefPtr<Inode>, PageTable&, off_t offset, ssize_t size, Type flags, PageTable::flags_t page_flags);
|
||||
|
||||
private:
|
||||
BAN::RefPtr<Inode> m_inode;
|
||||
const off_t m_offset;
|
||||
};
|
||||
|
||||
}
|
|
@ -21,7 +21,7 @@ namespace Kernel
|
|||
BAN_NON_MOVABLE(MemoryRegion);
|
||||
|
||||
public:
|
||||
enum Type : uint8_t
|
||||
enum class Type : uint8_t
|
||||
{
|
||||
PRIVATE,
|
||||
SHARED
|
||||
|
|
|
@ -48,6 +48,7 @@ namespace Kernel
|
|||
|
||||
BAN::ErrorOr<BAN::StringView> path_of(int) const;
|
||||
BAN::ErrorOr<BAN::RefPtr<Inode>> inode_of(int);
|
||||
BAN::ErrorOr<int> flags_of(int) const;
|
||||
|
||||
private:
|
||||
struct OpenFileDescription : public BAN::RefCounted<OpenFileDescription>
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
#include <kernel/LockGuard.h>
|
||||
#include <kernel/Memory/FileBackedRegion.h>
|
||||
#include <kernel/Memory/Heap.h>
|
||||
|
||||
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)
|
||||
{
|
||||
ASSERT(inode->mode().ifreg());
|
||||
|
||||
if (type != Type::PRIVATE)
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
|
||||
if (offset < 0 || offset % PAGE_SIZE || size == 0)
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
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);
|
||||
if (region_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
auto region = BAN::UniqPtr<FileBackedRegion>::adopt(region_ptr);
|
||||
|
||||
TRY(region->initialize(address_range));
|
||||
|
||||
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)
|
||||
, m_inode(inode)
|
||||
, m_offset(offset)
|
||||
{
|
||||
}
|
||||
|
||||
FileBackedRegion::~FileBackedRegion()
|
||||
{
|
||||
if (m_vaddr == 0)
|
||||
return;
|
||||
|
||||
ASSERT(m_type == Type::PRIVATE);
|
||||
|
||||
size_t needed_pages = BAN::Math::div_round_up<size_t>(m_size, PAGE_SIZE);
|
||||
for (size_t i = 0; i < needed_pages; i++)
|
||||
{
|
||||
paddr_t paddr = m_page_table.physical_address_of(m_vaddr + i * PAGE_SIZE);
|
||||
if (paddr != 0)
|
||||
Heap::get().release_page(paddr);
|
||||
}
|
||||
}
|
||||
|
||||
BAN::ErrorOr<bool> FileBackedRegion::allocate_page_containing(vaddr_t address)
|
||||
{
|
||||
ASSERT(m_type == Type::PRIVATE);
|
||||
|
||||
ASSERT(contains(address));
|
||||
|
||||
// Check if address is already mapped
|
||||
vaddr_t vaddr = address & PAGE_ADDR_MASK;
|
||||
if (m_page_table.physical_address_of(vaddr) != 0)
|
||||
return false;
|
||||
|
||||
// Map new physcial page to address
|
||||
paddr_t paddr = Heap::get().take_free_page();
|
||||
if (paddr == 0)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
m_page_table.map_page_at(paddr, vaddr, m_flags);
|
||||
|
||||
size_t file_offset = m_offset + (vaddr - m_vaddr);
|
||||
size_t bytes = BAN::Math::min<size_t>(m_size - file_offset, PAGE_SIZE);
|
||||
|
||||
BAN::ErrorOr<size_t> read_ret = 0;
|
||||
|
||||
// Zero out the new page
|
||||
if (&PageTable::current() == &m_page_table)
|
||||
read_ret = m_inode->read(file_offset, (void*)vaddr, bytes);
|
||||
else
|
||||
{
|
||||
LockGuard _(PageTable::current());
|
||||
ASSERT(PageTable::current().is_page_free(0));
|
||||
|
||||
PageTable::current().map_page_at(paddr, 0, PageTable::Flags::ReadWrite | PageTable::Flags::Present);
|
||||
read_ret = m_inode->read(file_offset, (void*)0, bytes);
|
||||
memset((void*)0, 0x00, PAGE_SIZE);
|
||||
PageTable::current().unmap_page(0);
|
||||
}
|
||||
|
||||
if (read_ret.is_error())
|
||||
{
|
||||
Heap::get().release_page(paddr);
|
||||
m_page_table.unmap_page(vaddr);
|
||||
return read_ret.release_error();
|
||||
}
|
||||
|
||||
if (read_ret.value() < bytes)
|
||||
{
|
||||
dwarnln("Only {}/{} bytes read", read_ret.value(), bytes);
|
||||
Heap::get().release_page(paddr);
|
||||
m_page_table.unmap_page(vaddr);
|
||||
return BAN::Error::from_errno(EIO);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> FileBackedRegion::clone(PageTable& new_page_table)
|
||||
{
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,13 +1,14 @@
|
|||
#include <kernel/LockGuard.h>
|
||||
#include <kernel/Memory/Heap.h>
|
||||
#include <kernel/Memory/MemoryBackedRegion.h>
|
||||
#include <kernel/LockGuard.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<MemoryBackedRegion>> MemoryBackedRegion::create(PageTable& page_table, size_t size, AddressRange address_range, Type type, PageTable::flags_t flags)
|
||||
{
|
||||
ASSERT(type == Type::PRIVATE);
|
||||
if (type != Type::PRIVATE)
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
|
||||
auto* region_ptr = new MemoryBackedRegion(page_table, size, type, flags);
|
||||
if (region_ptr == nullptr)
|
||||
|
|
|
@ -309,6 +309,12 @@ namespace Kernel
|
|||
return m_open_files[fd]->inode;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<int> OpenFileDescriptorSet::flags_of(int fd) const
|
||||
{
|
||||
TRY(validate_fd(fd));
|
||||
return m_open_files[fd]->flags;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> OpenFileDescriptorSet::validate_fd(int fd) const
|
||||
{
|
||||
if (fd < 0 || fd >= (int)m_open_files.size())
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <kernel/IDT.h>
|
||||
#include <kernel/InterruptController.h>
|
||||
#include <kernel/LockGuard.h>
|
||||
#include <kernel/Memory/FileBackedRegion.h>
|
||||
#include <kernel/Memory/Heap.h>
|
||||
#include <kernel/Memory/MemoryBackedRegion.h>
|
||||
#include <kernel/Memory/PageTableScope.h>
|
||||
|
@ -810,29 +811,64 @@ namespace Kernel
|
|||
if (args->prot != PROT_NONE && args->prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
|
||||
PageTable::flags_t flags = PageTable::Flags::UserSupervisor;
|
||||
if (args->prot & PROT_READ)
|
||||
flags |= PageTable::Flags::Present;
|
||||
if (args->prot & PROT_WRITE)
|
||||
flags |= PageTable::Flags::ReadWrite | PageTable::Flags::Present;
|
||||
if (args->prot & PROT_EXEC)
|
||||
flags |= PageTable::Flags::Execute | PageTable::Flags::Present;
|
||||
if (args->flags & MAP_FIXED)
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
|
||||
if (args->flags == (MAP_ANONYMOUS | MAP_PRIVATE))
|
||||
if (!(args->flags & MAP_PRIVATE) == !(args->flags & MAP_SHARED))
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
auto region_type = (args->flags & MAP_PRIVATE) ? MemoryRegion::Type::PRIVATE : MemoryRegion::Type::SHARED;
|
||||
|
||||
PageTable::flags_t page_flags = 0;
|
||||
if (args->prot & PROT_READ)
|
||||
page_flags |= PageTable::Flags::Present;
|
||||
if (args->prot & PROT_WRITE)
|
||||
page_flags |= PageTable::Flags::ReadWrite | PageTable::Flags::Present;
|
||||
if (args->prot & PROT_EXEC)
|
||||
page_flags |= PageTable::Flags::Execute | PageTable::Flags::Present;
|
||||
|
||||
if (page_flags == 0)
|
||||
page_flags = PageTable::Flags::Reserved;
|
||||
else
|
||||
page_flags |= PageTable::Flags::UserSupervisor;
|
||||
|
||||
if (args->flags & MAP_ANONYMOUS)
|
||||
{
|
||||
if (args->addr != nullptr)
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
if (args->off != 0)
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
if (args->len % PAGE_SIZE != 0)
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
|
||||
auto region = TRY(MemoryBackedRegion::create(
|
||||
page_table(),
|
||||
args->len,
|
||||
{ .start = 0x400000, .end = KERNEL_OFFSET },
|
||||
MemoryRegion::Type::PRIVATE,
|
||||
PageTable::Flags::UserSupervisor | PageTable::Flags::ReadWrite | PageTable::Flags::Present
|
||||
region_type, page_flags
|
||||
));
|
||||
|
||||
LockGuard _(m_lock);
|
||||
TRY(m_mapped_regions.push_back(BAN::move(region)));
|
||||
return m_mapped_regions.back()->vaddr();
|
||||
}
|
||||
|
||||
auto inode = TRY(m_open_file_descriptors.inode_of(args->fildes));
|
||||
if (inode->mode().ifreg())
|
||||
{
|
||||
if (args->addr != nullptr)
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
|
||||
auto inode_flags = TRY(m_open_file_descriptors.flags_of(args->fildes));
|
||||
if (!(inode_flags & O_RDONLY))
|
||||
return BAN::Error::from_errno(EACCES);
|
||||
if (region_type == MemoryRegion::Type::SHARED)
|
||||
if (!(args->prot & PROT_WRITE) || !(inode_flags & O_WRONLY))
|
||||
return BAN::Error::from_errno(EACCES);
|
||||
|
||||
auto region = TRY(FileBackedRegion::create(
|
||||
inode,
|
||||
page_table(),
|
||||
args->off, args->len,
|
||||
{ .start = 0x400000, .end = KERNEL_OFFSET },
|
||||
region_type, page_flags
|
||||
));
|
||||
|
||||
LockGuard _(m_lock);
|
||||
|
|
Loading…
Reference in New Issue