x64 now compiles again and runs on real hardware.

A nasty bug was fixed that caused triple faults on systems with > 1 GiB RAM.
This commit is contained in:
Jonas 'Sortie' Termansen 2011-11-29 01:21:59 +01:00
parent 8c146f14c0
commit 0b1c2a77c9
15 changed files with 517 additions and 16 deletions

View File

@ -29,10 +29,15 @@
.type _start, @function
_start:
pushq %rsi
pushq %rdi
call initialize_standard_library
popq %rdi
popq %rsi
# Run main
# TODO: Sortix should set the initial values before this point!
call main
# Terminate the process with main's exit code.

View File

@ -37,6 +37,7 @@ ifdef X86FAMILY
$(CPU)/syscall.o \
$(CPU)/thread.o \
$(CPU)/scheduler.o \
$(CPU)/process.o \
x86-family/x86-family.o
CPUFLAGS:=$(CPUFLAGS) -mno-mmx -mno-sse -mno-sse2 -mno-sse3 -mno-3dnow
endif

View File

@ -214,10 +214,10 @@ namespace Sortix
GDT::Init();
#ifdef PLATFORM_X64
Log::Print("Halt: CPU X64 cannot boot because interrupts are not yet "
Log::Print("Halt: CPU x64 cannot boot because interrupts are not yet "
"supported under 64-bit Sortix.\n");
Log::Print("Sorry, it simply isn't possible to fully boot Sortix in x64 mode yet.\n");
Log::Print("X64 may be working when Sortix 0.5 comes out, or try the git master.\n");
Log::Print("x64 may be working when Sortix 0.6 comes out, or try the git master.\n");
while(true);
#endif

View File

@ -273,8 +273,6 @@ namespace Sortix
addr_t stackpos = CurrentThread()->stackpos + CurrentThread()->stacksize;
addr_t argvpos = stackpos - sizeof(char*) * argc;
char** stackargv = (char**) argvpos;
regs->eax = argc;
regs->ebx = argvpos;
size_t argvsize = 0;
for ( int i = 0; i < argc; i++ )
@ -288,9 +286,7 @@ namespace Sortix
stackpos = argvpos - argvsize;
regs->eip = entry;
regs->useresp = stackpos;
regs->ebp = stackpos;
ExecuteCPU(argc, stackargv, stackpos, entry, regs);
return 0;
}

View File

@ -101,6 +101,7 @@ namespace Sortix
private:
Thread* ForkThreads(Process* processclone);
void ExecuteCPU(int argc, char** argv, addr_t stackpos, addr_t entry, CPU::InterruptRegisters* regs);
public:
void ResetForExecute();

View File

@ -106,5 +106,42 @@ namespace Sortix
// up, the calling function will fill up the physical allocator with
// plenty of nice physical pages. (see Page::InitPushRegion)
}
// Please note that even if this function exists, you should still clean
// up the address space of a process _before_ calling
// DestroyAddressSpace. This is just a hack because it currently is
// impossible to clean up PLM1's using the MM api!
// ---
// TODO: This function is duplicated in {x86,x64}/memorymanagement.cpp!
// ---
void RecursiveFreeUserspacePages(size_t level, size_t offset)
{
PML* pml = PMLS[level] + offset;
for ( size_t i = 0; i < ENTRIES; i++ )
{
if ( !(pml->entry[i] & PML_PRESENT) ) { continue; }
if ( !(pml->entry[i] & PML_USERSPACE) ) { continue; }
if ( !(pml->entry[i] & PML_FORK) ) { continue; }
if ( level > 1 ) { RecursiveFreeUserspacePages(level-1, offset * ENTRIES + i); }
addr_t addr = pml->entry[i] & PML_ADDRESS;
pml->entry[i] = 0;
Page::Put(addr);
}
}
void DestroyAddressSpace()
{
// First let's do the safe part. Garbage collect any PML1/0's left
// behind by user-space. These are completely safe to delete.
RecursiveFreeUserspacePages(TOPPMLLEVEL, 0);
// TODO: Right now this just leaks memory.
// Switch to the address space from when the world was originally
// created. It should contain the kernel, the whole kernel, and
// nothing but the kernel.
PML* const BOOTPML4 = (PML* const) 0x01000UL;
SwitchAddressSpace((addr_t) BOOTPML4);
}
}
}

38
sortix/x64/process.cpp Normal file
View File

@ -0,0 +1,38 @@
/******************************************************************************
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with Sortix. If not, see <http://www.gnu.org/licenses/>.
x64/process.cpp
CPU-specific process code.
******************************************************************************/
#include "platform.h"
#include "process.h"
namespace Sortix
{
void Process::ExecuteCPU(int argc, char** argv, addr_t stackpos, addr_t entry, CPU::InterruptRegisters* regs)
{
regs->rdi = argc;
regs->rsi = (size_t) argv;
regs->rip = entry;
regs->userrsp = stackpos;
regs->rbp = stackpos;
}
}

52
sortix/x64/scheduler.cpp Normal file
View File

@ -0,0 +1,52 @@
/******************************************************************************
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with Sortix. If not, see <http://www.gnu.org/licenses/>.
x64/scheduler.cpp
CPU specific things related to the scheduler.
******************************************************************************/
#include "platform.h"
#include "scheduler.h"
#include "../memorymanagement.h"
#include "descriptor_tables.h"
namespace Sortix
{
namespace Scheduler
{
const size_t KERNEL_STACK_SIZE = 256UL * 1024UL;
const addr_t KERNEL_STACK_END = 0xFFFF800000001000UL;
const addr_t KERNEL_STACK_START = KERNEL_STACK_END + KERNEL_STACK_SIZE;
void InitCPU()
{
// TODO: Prevent the kernel heap and other stuff from accidentally
// also allocating this virtual memory region!
if ( !Memory::MapRangeKernel(KERNEL_STACK_END, KERNEL_STACK_SIZE) )
{
PanicF("could not create kernel stack (%zx to %zx)",
KERNEL_STACK_END, KERNEL_STACK_START);
}
GDT::SetKernelStack((size_t*) KERNEL_STACK_START);
}
}
}

157
sortix/x64/syscall.s Normal file
View File

@ -0,0 +1,157 @@
/******************************************************************************
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with Sortix. If not, see <http://www.gnu.org/licenses/>.
syscall.s
An assembly stub that acts as glue for system calls.
******************************************************************************/
.global syscall_handler
.global resume_syscall
.section .text
.type syscall_handler, @function
syscall_handler:
cli
# Compabillity with InterruptRegisters.
pushq $0x0
pushq $0x80
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %r11
pushq %r10
pushq %r9
pushq %r8
pushq %rax
pushq %rcx
pushq %rdx
pushq %rbx
pushq %rsp
pushq %rsi
pushq %rdi
# Push the user-space data segment.
movl %ds, %ebp
pushq %rbp
# Load the kernel data segment.
movw $0x10, %bp
movl %ebp, %ds
movl %ebp, %es
movl %ebp, %fs
movl %ebp, %gs
# Compabillity with InterruptRegisters.
movq %cr2, %rbp
pushq %rbp
# Store the state structure's pointer so the call can modify it if needed.
movq %rsp, syscall_state_ptr
# By default, assume the system call was complete.
movl $0, system_was_incomplete
# Reset the kernel errno.
movl $0, errno
# Make sure the requested system call is valid.
cmp SYSCALL_MAX, %rax
jb valid_rax
xorq %rax, %rax
valid_rax:
# Read a system call function pointer.
xorq %rbp, %rbp
movq syscall_list(%rbp,%rax,4), %rax
# Oh how nice, user-space put the parameters in: rdi, rsi, rdx, rcx, r8, r9
# Call the system call.
callq *%rax
# Test if the system call was incomplete
movl system_was_incomplete, %ebx
testl %ebx, %ebx
# If the system call was incomplete, the value in %eax is meaningless.
jg return_to_userspace
# The system call was completed, so store the return value.
movq %rax, 72(%rsp)
# Don't forget to update userspace's errno value.
call update_userspace_errno
return_to_userspace:
# Compabillity with InterruptRegisters.
addq $8, %rsp
# Restore the user-space data segment.
popq %rbp
movl %ebp, %ds
movl %ebp, %es
movl %ebp, %fs
movl %ebp, %gs
popq %rdi
popq %rsi
popq %rsp
popq %rbx
popq %rdx
popq %rcx
popq %rax
popq %r8
popq %r9
popq %r10
popq %r11
popq %r12
popq %r13
popq %r14
popq %r15
# Compabillity with InterruptRegisters.
addq $16, %rsp
# Return to user-space.
iretq
.type resume_syscall, @function
resume_syscall:
pushq %rbp
movq %rsp, %rbp
movq %rdi, %rax
movq %rsi, %r11
movq 0(%r11), %rdi
movq 8(%r11), %rsi
movq 16(%r11), %rdx
movq 24(%r11), %rcx
movq 32(%r11), %r8
movq 40(%r11), %r9
callq *%rax
leaveq
retq

139
sortix/x64/thread.cpp Normal file
View File

@ -0,0 +1,139 @@
/******************************************************************************
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with Sortix. If not, see <http://www.gnu.org/licenses/>.
thread.cpp
x64 specific parts of thread.cpp.
******************************************************************************/
#include "platform.h"
#include "thread.h"
namespace Sortix
{
void Thread::SaveRegisters(const CPU::InterruptRegisters* src)
{
registers.rip = src->rip;
registers.userrsp = src->userrsp;
registers.rax = src->rax;
registers.rbx = src->rbx;
registers.rcx = src->rcx;
registers.rdx = src->rdx;
registers.rdi = src->rdi;
registers.rsi = src->rsi;
registers.rbp = src->rbp;
registers.r8 = src->r8;
registers.r9 = src->r9;
registers.r10 = src->r10;
registers.r11 = src->r11;
registers.r12 = src->r12;
registers.r13 = src->r13;
registers.r14 = src->r14;
registers.r15 = src->r15;
registers.cs = src->cs;
registers.ds = src->ds;
registers.ss = src->ss;
registers.rflags = src->rflags;
}
void Thread::LoadRegisters(CPU::InterruptRegisters* dest)
{
dest->rip = registers.rip;
dest->userrsp = registers.userrsp;
dest->rax = registers.rax;
dest->rbx = registers.rbx;
dest->rcx = registers.rcx;
dest->rdx = registers.rdx;
dest->rdi = registers.rdi;
dest->rsi = registers.rsi;
dest->rbp = registers.rbp;
dest->r8 = registers.r8;
dest->r9 = registers.r9;
dest->r10 = registers.r10;
dest->r11 = registers.r11;
dest->r12 = registers.r12;
dest->r13 = registers.r13;
dest->r14 = registers.r14;
dest->r15 = registers.r15;
dest->cs = registers.cs;
dest->ds = registers.ds;
dest->ss = registers.ss;
dest->rflags = registers.rflags;
}
const size_t FLAGS_CARRY = (1<<0);
const size_t FLAGS_RESERVED1 = (1<<1); /* read as one */
const size_t FLAGS_PARITY = (1<<2);
const size_t FLAGS_RESERVED2 = (1<<3);
const size_t FLAGS_AUX = (1<<4);
const size_t FLAGS_RESERVED3 = (1<<5);
const size_t FLAGS_ZERO = (1<<6);
const size_t FLAGS_SIGN = (1<<7);
const size_t FLAGS_TRAP = (1<<8);
const size_t FLAGS_INTERRUPT = (1<<9);
const size_t FLAGS_DIRECTION = (1<<10);
const size_t FLAGS_OVERFLOW = (1<<11);
const size_t FLAGS_IOPRIVLEVEL = (1<<12) | (1<<13);
const size_t FLAGS_NESTEDTASK = (1<<14);
const size_t FLAGS_RESERVED4 = (1<<15);
const size_t FLAGS_RESUME = (1<<16);
const size_t FLAGS_VIRTUAL8086 = (1<<17);
const size_t FLAGS_ALIGNCHECK = (1<<18);
const size_t FLAGS_VIRTINTR = (1<<19);
const size_t FLAGS_VIRTINTRPEND = (1<<20);
const size_t FLAGS_ID = (1<<21);
void CreateThreadCPU(Thread* thread, addr_t entry)
{
const uint64_t CS = 0x18;
const uint64_t DS = 0x20;
const uint64_t RPL = 0x3;
CPU::InterruptRegisters regs;
regs.rip = entry;
regs.userrsp = thread->stackpos + thread->stacksize;
regs.rax = 0;
regs.rbx = 0;
regs.rcx = 0;
regs.rdx = 0;
regs.rdi = 0;
regs.rsi = 0;
regs.rbp = thread->stackpos + thread->stacksize;
regs.r8 = 0;
regs.r9 = 0;
regs.r10 = 0;
regs.r11 = 0;
regs.r12 = 0;
regs.r13 = 0;
regs.r14 = 0;
regs.r15 = 0;
regs.cs = CS | RPL;
regs.ds = DS | RPL;
regs.ss = DS | RPL;
regs.rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
thread->SaveRegisters(&regs);
}
void Thread::HandleSignalCPU(CPU::InterruptRegisters* regs)
{
regs->rdi = currentsignal->signum;
regs->rip = (size_t) sighandler;
}
}

View File

@ -24,10 +24,26 @@
#include <libmaxsi/platform.h>
#include "x64.h"
#include "log.h"
namespace Sortix
{
namespace X64
{
void InterruptRegisters::LogRegisters() const
{
Log::PrintF("[cr2=0x%zx,ds=0x%zx,rdi=0x%zx,rsi=0x%zx,rbp=0x%zx,"
"rsp=0x%zx,rbx=0x%zx,rcx=0x%zx,rax=0x%zx,r8=0x%zx,"
"r9=0x%zx,r10=0x%zx,r11=0x%zx,r12=0x%zx,r13=0x%zx,"
"r14=0x%zx,r15=0x%zx,int_no=0x%zx,err_code=0x%zx,"
"rip=0x%zx,cs=0x%zx,rflags=0x%zx,userrsp=0x%zx,"
"ss=0x%zx]",
cr2, ds, rdi, rsi, rbp,
rsp, rbx, rcx, rax, r8,
r9, r10, r11, r12, r13,
r14, r15, int_no, err_code,
rip, cs, rflags, userrsp,
ss);
}
}
}

View File

@ -35,10 +35,23 @@ namespace Sortix
{
uint64_t cr2;
uint64_t ds; // Data segment selector
uint64_t rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax; // Pushed by pusha.
uint64_t r8, r9, r10, r11, r12, r13, r14, r15; // Pushed by pusha.
uint64_t rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax;
uint64_t r8, r9, r10, r11, r12, r13, r14, r15;
uint64_t int_no, err_code; // Interrupt number and error code (if applicable)
uint64_t eip, cs, eflags, useresp, ss; // Pushed by the processor automatically.
uint64_t rip, cs, rflags, userrsp, ss; // Pushed by the processor automatically.
public:
void LogRegisters() const;
};
struct SyscallRegisters
{
uint64_t cr2; // For compabillity with above, may be removed soon.
uint64_t ds;
uint64_t di, si, bp, trash, b, d, c; union { uint64_t a; uint64_t result; };
uint64_t r8, r9, r10, r11, r12, r13, r14, r15;
uint64_t int_no, err_code; // Also compabillity.
uint64_t ip, cs, flags, sp, ss;
};
}
}

View File

@ -115,7 +115,7 @@ namespace Sortix
// until the pebibyte era of RAM.
if ( 0 < Page::pagesnotonstack )
{
Log::PrintF("%zu bytes of RAM aren't used due to technical"
Log::PrintF("%zu bytes of RAM aren't used due to technical "
"restrictions.\n", Page::pagesnotonstack * 0x1000UL);
}
@ -178,7 +178,10 @@ namespace Sortix
// This call will also succeed, since there are plenty of physical
// pages available and it might need some.
Memory::MapKernel(page, (addr_t) (STACK + stacklength));
if ( !Memory::MapKernel(page, (addr_t) (STACK + stacklength)) )
{
Panic("Unable to extend page stack, which should have worked");
}
// TODO: This may not be needed during the boot process!
//Memory::InvalidatePage((addr_t) (STACK + stacklength));
@ -345,6 +348,9 @@ namespace Sortix
addr_t& entry = pml->entry[childid];
// Find the index of the next PML in the fractal mapped memory.
size_t childoffset = offset * ENTRIES + childid;
if ( !(entry & PML_PRESENT) )
{
// TODO: Possible memory leak when page allocation fails.
@ -353,7 +359,7 @@ namespace Sortix
entry = page | flags;
// Invalidate the new PML and reset it to zeroes.
addr_t pmladdr = (addr_t) (PMLS[i-1] + childid);
addr_t pmladdr = (addr_t) (PMLS[i-1] + childoffset);
InvalidatePage(pmladdr);
Maxsi::Memory::Set((void*) pmladdr, 0, sizeof(PML));
}
@ -365,8 +371,7 @@ namespace Sortix
"code calling this function", physical, mapto, i-1);
}
// Find the index of the next PML in the fractal mapped memory.
offset = offset * ENTRIES + childid;
offset = childoffset;
}
// Actually map the physical page to the virtual page.

View File

@ -106,6 +106,9 @@ namespace Sortix
// up the address space of a process _before_ calling
// DestroyAddressSpace. This is just a hack because it currently is
// impossible to clean up PLM1's using the MM api!
// ---
// TODO: This function is duplicated in {x86,x64}/memorymanagement.cpp!
// ---
void RecursiveFreeUserspacePages(size_t level, size_t offset)
{
PML* pml = PMLS[level] + offset;

38
sortix/x86/process.cpp Normal file
View File

@ -0,0 +1,38 @@
/******************************************************************************
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with Sortix. If not, see <http://www.gnu.org/licenses/>.
x86/process.cpp
CPU-specific process code.
******************************************************************************/
#include "platform.h"
#include "process.h"
namespace Sortix
{
void Process::ExecuteCPU(int argc, char** argv, addr_t stackpos, addr_t entry, CPU::InterruptRegisters* regs)
{
regs->eax = argc;
regs->ebx = (size_t) argv;
regs->eip = entry;
regs->useresp = stackpos;
regs->ebp = stackpos;
}
}