1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-30 22:58:12 +00:00

UserspaceEmulator: Add basic TLS (thread-local storage) support

The SoftMMU now receives full X86::LogicalAddress values from SoftCPU.
This allows the MMU to reroute TLS accesses to a special memory region.

The ELF executable's PT_TLS header tells us how to allocate the TLS.

Basically, the GS register points to a magical 4-byte area which has
a pointer to the TCB (thread control block). The TCB lives in normal
flat memory space and is accessed through the DS register.
This commit is contained in:
Andreas Kling 2020-07-12 00:54:09 +02:00
parent df95e25eaa
commit 734f63d522
4 changed files with 77 additions and 50 deletions

View file

@ -124,11 +124,23 @@ void Emulator::setup_stack()
bool Emulator::load_elf() bool Emulator::load_elf()
{ {
m_elf->image().for_each_program_header([&](const ELF::Image::ProgramHeader& program_header) { m_elf->image().for_each_program_header([&](const ELF::Image::ProgramHeader& program_header) {
if (program_header.type() != PT_LOAD) if (program_header.type() == PT_LOAD) {
auto region = make<SimpleRegion>(program_header.vaddr().get(), program_header.size_in_memory());
memcpy(region->data(), program_header.raw_data(), program_header.size_in_image());
mmu().add_region(move(region));
return; return;
auto region = make<SimpleRegion>(program_header.vaddr().get(), program_header.size_in_memory()); }
memcpy(region->data(), program_header.raw_data(), program_header.size_in_image()); if (program_header.type() == PT_TLS) {
mmu().add_region(move(region)); auto tcb_region = make<SimpleRegion>(0x20000000, program_header.size_in_memory());
memcpy(tcb_region->data(), program_header.raw_data(), program_header.size_in_image());
auto tls_region = make<SimpleRegion>(0, 4);
tls_region->write32(0, tcb_region->base() + 8);
mmu().add_region(move(tcb_region));
mmu().set_tls_region(move(tls_region));
return;
}
}); });
m_cpu.set_eip(m_elf->image().entry().get()); m_cpu.set_eip(m_elf->image().entry().get());

View file

@ -51,6 +51,7 @@ SoftCPU::SoftCPU(Emulator& emulator)
m_segment[(int)X86::SegmentRegister::DS] = 0x20; m_segment[(int)X86::SegmentRegister::DS] = 0x20;
m_segment[(int)X86::SegmentRegister::ES] = 0x20; m_segment[(int)X86::SegmentRegister::ES] = 0x20;
m_segment[(int)X86::SegmentRegister::SS] = 0x20; m_segment[(int)X86::SegmentRegister::SS] = 0x20;
m_segment[(int)X86::SegmentRegister::GS] = 0x28;
} }
void SoftCPU::dump() const void SoftCPU::dump() const
@ -83,59 +84,59 @@ u32 SoftCPU::read32()
u8 SoftCPU::read_memory8(X86::LogicalAddress address) u8 SoftCPU::read_memory8(X86::LogicalAddress address)
{ {
ASSERT(address.selector() == 0x18 || address.selector() == 0x20); ASSERT(address.selector() == 0x18 || address.selector() == 0x20 || address.selector() == 0x28);
auto value = m_emulator.mmu().read8(address.offset()); auto value = m_emulator.mmu().read8(address);
#ifdef MEMORY_DEBUG #ifdef MEMORY_DEBUG
printf("\033[36;1mread_memory8: @%08x -> %02x\033[0m\n", address.offset(), value); printf("\033[36;1mread_memory8: @%08x:%08x -> %02x\033[0m\n", address.selector(), address.offset(), value);
#endif #endif
return value; return value;
} }
u16 SoftCPU::read_memory16(X86::LogicalAddress address) u16 SoftCPU::read_memory16(X86::LogicalAddress address)
{ {
ASSERT(address.selector() == 0x18 || address.selector() == 0x20); ASSERT(address.selector() == 0x18 || address.selector() == 0x20 || address.selector() == 0x28);
auto value = m_emulator.mmu().read16(address.offset()); auto value = m_emulator.mmu().read16(address);
#ifdef MEMORY_DEBUG #ifdef MEMORY_DEBUG
printf("\033[36;1mread_memory16: @%08x -> %04x\033[0m\n", address.offset(), value); printf("\033[36;1mread_memory16: @%04x:%08x -> %04x\033[0m\n", address.selector(), address.offset(), value);
#endif #endif
return value; return value;
} }
u32 SoftCPU::read_memory32(X86::LogicalAddress address) u32 SoftCPU::read_memory32(X86::LogicalAddress address)
{ {
ASSERT(address.selector() == 0x18 || address.selector() == 0x20); ASSERT(address.selector() == 0x18 || address.selector() == 0x20 || address.selector() == 0x28);
auto value = m_emulator.mmu().read32(address.offset()); auto value = m_emulator.mmu().read32(address);
#ifdef MEMORY_DEBUG #ifdef MEMORY_DEBUG
printf("\033[36;1mread_memory32: @%08x -> %08x\033[0m\n", address.offset(), value); printf("\033[36;1mread_memory32: @%04x:%08x -> %08x\033[0m\n", address.selector(), address.offset(), value);
#endif #endif
return value; return value;
} }
void SoftCPU::write_memory8(X86::LogicalAddress address, u8 value) void SoftCPU::write_memory8(X86::LogicalAddress address, u8 value)
{ {
ASSERT(address.selector() == 0x20); ASSERT(address.selector() == 0x20 || address.selector() == 0x28);
#ifdef MEMORY_DEBUG #ifdef MEMORY_DEBUG
printf("\033[35;1mwrite_memory8: @%08x <- %02x\033[0m\n", address.offset(), value); printf("\033[35;1mwrite_memory8: @%04x:%08x <- %02x\033[0m\n", address.selector(), address.offset(), value);
#endif #endif
m_emulator.mmu().write8(address.offset(), value); m_emulator.mmu().write8(address, value);
} }
void SoftCPU::write_memory16(X86::LogicalAddress address, u16 value) void SoftCPU::write_memory16(X86::LogicalAddress address, u16 value)
{ {
ASSERT(address.selector() == 0x20); ASSERT(address.selector() == 0x20 || address.selector() == 0x28);
#ifdef MEMORY_DEBUG #ifdef MEMORY_DEBUG
printf("\033[35;1mwrite_memory16: @%08x <- %04x\033[0m\n", address.offset(), value); printf("\033[35;1mwrite_memory16: @%04x:%08x <- %04x\033[0m\n", address.selector(), address.offset(), value);
#endif #endif
m_emulator.mmu().write16(address.offset(), value); m_emulator.mmu().write16(address, value);
} }
void SoftCPU::write_memory32(X86::LogicalAddress address, u32 value) void SoftCPU::write_memory32(X86::LogicalAddress address, u32 value)
{ {
ASSERT(address.selector() == 0x20); ASSERT(address.selector() == 0x20 || address.selector() == 0x28);
#ifdef MEMORY_DEBUG #ifdef MEMORY_DEBUG
printf("\033[35;1mwrite_memory32: @%08x <- %08x\033[0m\n", address.offset(), value); printf("\033[35;1mwrite_memory32: @%04x:%08x <- %08x\033[0m\n", address.selector(), address.offset(), value);
#endif #endif
m_emulator.mmu().write32(address.offset(), value); m_emulator.mmu().write32(address, value);
} }
void SoftCPU::push32(u32 value) void SoftCPU::push32(u32 value)

View file

@ -28,10 +28,13 @@
namespace UserspaceEmulator { namespace UserspaceEmulator {
SoftMMU::Region* SoftMMU::find_region(u32 address) SoftMMU::Region* SoftMMU::find_region(X86::LogicalAddress address)
{ {
if (address.selector() == 0x28)
return m_tls_region.ptr();
for (auto& region : m_regions) { for (auto& region : m_regions) {
if (region.contains(address)) if (region.contains(address.offset()))
return &region; return &region;
} }
return nullptr; return nullptr;
@ -39,75 +42,81 @@ SoftMMU::Region* SoftMMU::find_region(u32 address)
void SoftMMU::add_region(NonnullOwnPtr<Region> region) void SoftMMU::add_region(NonnullOwnPtr<Region> region)
{ {
ASSERT(!find_region(region->base())); ASSERT(!find_region({ 0x20, region->base() }));
// FIXME: More sanity checks pls // FIXME: More sanity checks pls
m_regions.append(move(region)); m_regions.append(move(region));
} }
u8 SoftMMU::read8(u32 address) void SoftMMU::set_tls_region(NonnullOwnPtr<Region> region)
{
ASSERT(!m_tls_region);
m_tls_region = move(region);
}
u8 SoftMMU::read8(X86::LogicalAddress address)
{ {
auto* region = find_region(address); auto* region = find_region(address);
if (!region) { if (!region) {
warn() << "SoftMMU::read8: No region for @" << (const void*)address; warn() << "SoftMMU::read8: No region for @" << (const void*)address.offset();
TODO(); TODO();
} }
return region->read8(address - region->base()); return region->read8(address.offset() - region->base());
} }
u16 SoftMMU::read16(u32 address) u16 SoftMMU::read16(X86::LogicalAddress address)
{ {
auto* region = find_region(address); auto* region = find_region(address);
if (!region) { if (!region) {
warn() << "SoftMMU::read16: No region for @" << (const void*)address; warn() << "SoftMMU::read16: No region for @" << (const void*)address.offset();
TODO(); TODO();
} }
return region->read16(address - region->base()); return region->read16(address.offset() - region->base());
} }
u32 SoftMMU::read32(u32 address) u32 SoftMMU::read32(X86::LogicalAddress address)
{ {
auto* region = find_region(address); auto* region = find_region(address);
if (!region) { if (!region) {
warn() << "SoftMMU::read32: No region for @" << (const void*)address; warn() << "SoftMMU::read32: No region for @" << (const void*)address.offset();
TODO(); TODO();
} }
return region->read32(address - region->base()); return region->read32(address.offset() - region->base());
} }
void SoftMMU::write8(u32 address, u8 value) void SoftMMU::write8(X86::LogicalAddress address, u8 value)
{ {
auto* region = find_region(address); auto* region = find_region(address);
if (!region) { if (!region) {
warn() << "SoftMMU::write8: No region for @" << (const void*)address; warn() << "SoftMMU::write8: No region for @" << (const void*)address.offset();
TODO(); TODO();
} }
region->write8(address - region->base(), value); region->write8(address.offset() - region->base(), value);
} }
void SoftMMU::write16(u32 address, u16 value) void SoftMMU::write16(X86::LogicalAddress address, u16 value)
{ {
auto* region = find_region(address); auto* region = find_region(address);
if (!region) { if (!region) {
warn() << "SoftMMU::write16: No region for @" << (const void*)address; warn() << "SoftMMU::write16: No region for @" << (const void*)address.offset();
TODO(); TODO();
} }
region->write16(address - region->base(), value); region->write16(address.offset() - region->base(), value);
} }
void SoftMMU::write32(u32 address, u32 value) void SoftMMU::write32(X86::LogicalAddress address, u32 value)
{ {
auto* region = find_region(address); auto* region = find_region(address);
if (!region) { if (!region) {
warn() << "SoftMMU::write32: No region for @" << (const void*)address; warn() << "SoftMMU::write32: No region for @" << (const void*)address.offset();
TODO(); TODO();
} }
region->write32(address - region->base(), value); region->write32(address.offset() - region->base(), value);
} }
} }

View file

@ -27,7 +27,9 @@
#pragma once #pragma once
#include <AK/NonnullOwnPtrVector.h> #include <AK/NonnullOwnPtrVector.h>
#include <AK/OwnPtr.h>
#include <AK/Types.h> #include <AK/Types.h>
#include <LibX86/Instruction.h>
namespace UserspaceEmulator { namespace UserspaceEmulator {
@ -63,18 +65,21 @@ public:
u32 m_size { 0 }; u32 m_size { 0 };
}; };
u8 read8(u32 address); u8 read8(X86::LogicalAddress);
u16 read16(u32 address); u16 read16(X86::LogicalAddress);
u32 read32(u32 address); u32 read32(X86::LogicalAddress);
void write8(u32 address, u8 value); void write8(X86::LogicalAddress, u8);
void write16(u32 address, u16 value); void write16(X86::LogicalAddress, u16);
void write32(u32 address, u32 value); void write32(X86::LogicalAddress, u32);
Region* find_region(X86::LogicalAddress);
Region* find_region(u32 address);
void add_region(NonnullOwnPtr<Region>); void add_region(NonnullOwnPtr<Region>);
void set_tls_region(NonnullOwnPtr<Region>);
private: private:
OwnPtr<Region> m_tls_region;
NonnullOwnPtrVector<Region> m_regions; NonnullOwnPtrVector<Region> m_regions;
}; };