Added sforkr(2) that controls the child registers as well.

sfork(2) now calls sforkr(2) with the current registers.

This will prove useful in creating threads, where user-space now can fully
control what state the child will start in. This is unlike the Linux clone
system call that accepts a pointer to the child stack; this is more powerful
and somehow simpler. Note that this will create a rather raw thread; no
thread initization has been done by the standard thread API (when it is
implemented), so this feature shouldn't be used by programmers unless they
know what they are doing.

fork(2) now calls sfork(2) directly. Also removed fork(2) and sfork(2) from
the kernel as they are done using sforkr(2) now. So technically they aren't
system calls right now, but that could always change.
This commit is contained in:
Jonas 'Sortie' Termansen 2012-04-05 23:00:47 +02:00
parent 6f36ecf0b3
commit 6367a2352e
11 changed files with 393 additions and 53 deletions

View File

@ -46,6 +46,7 @@ kernelinfo.o \
init.o \
signal.o \
$(CPU)/signal.o \
$(CPU)/fork.o \
start.o \
time.o \
random.o \

View File

@ -29,6 +29,10 @@
#define _UNISTD_H 1
#include <features.h>
#if defined(_SORTIX_SOURCE)
#include <stdint.h>
#include <sortix/fork.h>
#endif
#include <sortix/seek.h>
#include <sortix/unistd.h>
@ -187,6 +191,7 @@ size_t pwriteleast(int fd, const void* buf, size_t least, size_t max, off_t off)
size_t readall(int fd, void* buf, size_t count);
size_t readleast(int fd, void* buf, size_t least, size_t max);
pid_t sfork(int flags);
pid_t sforkr(int flags, sforkregs_t* regs);
int uptime(uintmax_t* usecssinceboot);
size_t writeall(int fd, const void* buf, size_t count);
size_t writeleast(int fd, const void* buf, size_t least, size_t max);

View File

@ -36,8 +36,7 @@ namespace Maxsi
{
DEFN_SYSCALL1_VOID(SysExit, SYSCALL_EXIT, int);
DEFN_SYSCALL3(int, SysExecVE, SYSCALL_EXEC, const char*, char* const*, char* const*);
DEFN_SYSCALL0(pid_t, SysFork, SYSCALL_FORK);
DEFN_SYSCALL1(pid_t, SysSFork, SYSCALL_SFORK, int);
DEFN_SYSCALL2(pid_t, SysSForkR, SYSCALL_SFORKR, int, sforkregs_t*);
DEFN_SYSCALL0(pid_t, SysGetPID, SYSCALL_GETPID);
DEFN_SYSCALL0(pid_t, SysGetParentPID, SYSCALL_GETPPID);
DEFN_SYSCALL3(pid_t, SysWait, SYSCALL_WAIT, pid_t, int*, int);
@ -73,14 +72,21 @@ namespace Maxsi
_exit(status);
}
DUAL_FUNCTION(pid_t, fork, Fork, ())
extern "C" pid_t sforkr(int flags, sforkregs_t* regs)
{
return SysFork();
return SysSForkR(flags, regs);
}
extern "C" pid_t __call_sforkr_with_regs(int flags);
extern "C" pid_t sfork(int flags)
{
return SysSFork(flags);
return __call_sforkr_with_regs(flags);
}
DUAL_FUNCTION(pid_t, fork, Fork, ())
{
return sfork(SFFORK);
}
DUAL_FUNCTION(pid_t, getpid, GetPID, ())

67
libmaxsi/x64/fork.s Normal file
View File

@ -0,0 +1,67 @@
/*******************************************************************************
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2012.
This file is part of LibMaxsi.
LibMaxsi is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
LibMaxsi 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 Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License
along with LibMaxsi. If not, see <http://www.gnu.org/licenses/>.
x64/fork.s
Assembly functions related to forking x64 processes.
*******************************************************************************/
.section .text
.globl __call_sforkr_with_regs
.type __call_sforkr_with_regs, @function
__call_sforkr_with_regs:
pushq %rbp
movq %rsp, %rbp
# The actual system call expects a struct sforkregs_x64 containing the state
# of each register in the child. Since we create an identical copy, we
# simply set each member of the structure to our own state. Note that since
# the stack goes downwards, we create it in the reverse order.
pushfq
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %r11
pushq %r10
pushq %r9
pushq %r8
pushq %rbp
pushq %rsp
pushq %rsi
pushq %rdi
pushq %rdx
pushq %rcx
pushq %rbx
pushq $0 # rax, result of sfork is 0 for the child.
pushq $after_fork # rip, child will start execution from here.
# Call sforkr with a nice pointer to our structure. Note that %rdi contains
# the flag parameter that this function accepted.
movq %rsp, %rsi
call sforkr
after_fork:
# The value in %rax determines whether we are child or parent. There is no
# need to clean up the stack from the above pushes, leaveq sets %rsp to %rbp
# which does that for us.
leaveq
retq

62
libmaxsi/x86/fork.s Normal file
View File

@ -0,0 +1,62 @@
/*******************************************************************************
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2012.
This file is part of LibMaxsi.
LibMaxsi is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
LibMaxsi 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 Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License
along with LibMaxsi. If not, see <http://www.gnu.org/licenses/>.
x86/fork.s
Assembly functions related to forking x86 processes.
*******************************************************************************/
.section .text
.globl __call_sforkr_with_regs
.type __call_sforkr_with_regs, @function
__call_sforkr_with_regs:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %edx # flags parameter, edx need not be preserved.
# The actual system call expects a struct sforkregs_x86 containing the state
# of each register in the child. Since we create an identical copy, we
# simply set each member of the structure to our own state. Note that since
# the stack goes downwards, we create it in the reverse order.
pushfl
pushl %ebp
pushl %esp
pushl %esi
pushl %edi
pushl %edx
pushl %ecx
pushl %ebx
pushl $0 # rax, result of sfork is 0 for the child.
pushl $after_fork # rip, child will start execution from here.
# Call sforkr with a nice pointer to our structure. Note that %edi contains
# the flag parameter that this function accepted.
pushl %esp
pushl %edx
call sforkr
after_fork:
# The value in %eax determines whether we are child or parent. There is no
# need to clean up the stack from the above pushes, leavel sets %esp to %ebp
# which does that for us.
leavel
retl

View File

@ -0,0 +1,89 @@
/*******************************************************************************
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2012.
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/>.
sortix/fork.h
Declarations related to the fork family of system calls on Sortix.
*******************************************************************************/
#ifndef SORTIX_FORK_H
#define SORTIX_FORK_H
#include <features.h>
#include <sortix/x86/fork.h>
#include <sortix/x64/fork.h>
__BEGIN_DECLS
#define R_OK 4 /* Test for read permission. */
#define W_OK 2 /* Test for write permission. */
#define X_OK 1 /* Test for execute permission. */
#define F_OK 0 /* Test for existence. */
/* The sfork system call is much like the rfork system call found in Plan 9 and
BSD systems, however it works slightly differently and was renamed to avoid
conflicts with existing programs. In particular, it never forks an item
unless its bit is set, whereas rfork sometimes forks an item by default. If
you wish to fork certain items simply set the proper flags. Note that since
flags may be added from time to time, you should use various compound flags
defined below such as SFFORK and SFALL. It can be useful do combine these
compount flags with bitoperations, for instance "I want traditional fork,
except share the working dir pointer" is sfork(SFFORK & ~SFCWD). */
#define SFPROC (1<<0) /* Creates child, otherwise affect current process. */
#define SFPID (1<<1) /* Allocates new PID. */
#define SFFD (1<<2) /* Fork file descriptor table. */
#define SFMEM (1<<3) /* Forks address space. */
#define SFCWD (1<<4) /* Forks current directory pointer. */
#define SFROOT (1<<5) /* Forks root directory pointer. */
#define SFNAME (1<<6) /* Forks namespace. */
#define SFSIG (1<<7) /* Forks signal table. */
#define SFCSIG (1<<8) /* Child will have no pending signals, like fork(2). */
/* Creates a new thread in this process. Beware that it will share the stack of
the parent thread and that various threading featues may not have been set up
properly. You should use the standard threading API unless you know what you
are doing; remember that you can always sfork more stuff after the standard
threading API returns control to you. */
#define SFTHREAD (SFPROC | SFCSIG)
/* Provides traditional fork(2) behavior; use this instead of the above values
if you want "as fork(2), but also fork foo", or "as fork(2), except bar". In
those cases it is better to sfork(SFFORK & ~SFFOO); or sfork(SFFORK | SFBAR);
as that would allow to add new flags to SFFORK if a new kernel feature is
added to the system that applications don't know about yet. */
#define SFFORK (SFPROC | SFPID | SFFD | SFMEM | SFCWD | SFROOT | SFCSIG)
/* This allows creating a process that is completely forked from the original
process, unlike SFFORK which does share a few things (such as the process
namespace). Note that there is a few unset high bits in this value, these
are reserved and must not be set. */
#define SFALL ((1<<20)-1)
#ifdef PLATFORM_X86
typedef struct sforkregs_x86 sforkregs_t;
#elif defined(PLATFORM_X64)
typedef struct sforkregs_x64 sforkregs_t;
#else
#warning No sforkresgs_cpu structure declared
#endif
__END_DECLS
#endif

View File

@ -35,7 +35,6 @@
#define SYSCALL_SET_FREQUENCY 9
#define SYSCALL_EXEC 10
#define SYSCALL_PRINT_PATH_FILES 11
#define SYSCALL_FORK 12
#define SYSCALL_GETPID 13
#define SYSCALL_GETPPID 14
#define SYSCALL_GET_FILEINFO 15
@ -74,7 +73,7 @@
#define SYSCALL_KERNELINFO 48
#define SYSCALL_PREAD 49
#define SYSCALL_PWRITE 50
#define SYSCALL_SFORK 51
#define SYSCALL_SFORKR 51
#define SYSCALL_MAX_NUM 52 /* index of highest constant + 1 */
#endif

View File

@ -17,8 +17,8 @@
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
unistd.h
Standard symbolic constants and types.
sortix/unistd.h
Standard symbolic constants and types for Sortix.
*******************************************************************************/
@ -34,38 +34,6 @@ __BEGIN_DECLS
#define X_OK 1 /* Test for execute permission. */
#define F_OK 0 /* Test for existence. */
/* The sfork system call is much like the rfork system call found in Plan 9 and
BSD systems, however it works slightly differently and was renamed to avoid
conflicts with existing programs. In particular, it never forks an item
unless its bit is set, whereas rfork sometimes forks an item by default. If
you wish to fork certain items simply set the proper flags. Note that since
flags may be added from time to time, you should use various compound flags
defined below such as SFFORK and SFALL. It can be useful do combine these
compount flags with bitoperations, for instance "I want traditional fork,
except share the working dir pointer" is sfork(SFFORK & ~SFCWD). */
#define SFPROC (1<<0) /* Creates child, otherwise affect current process. */
#define SFPID (1<<1) /* Allocates new PID. */
#define SFFD (1<<2) /* Fork file descriptor table. */
#define SFMEM (1<<3) /* Forks address space. */
#define SFCWD (1<<4) /* Forks current directory pointer. */
#define SFROOT (1<<5) /* Forks root directory pointer. */
#define SFNAME (1<<6) /* Forks namespace. */
#define SFSIG (1<<7) /* Forks signal table. */
#define SFCSIG (1<<8) /* Child will have no pending signals, like fork(2). */
/* Provides traditional fork(2) behavior; use this instead of the above values
if you want "as fork(2), but also fork foo", or "as fork(2), except bar". In
those cases it is better to sfork(SFFORK & ~SFFOO); or sfork(SFFORK | SFBAR);
as that would allow to add new flags to SFFORK if a new kernel feature is
added to the system that applications don't know about yet. */
#define SFFORK (SFPROC | SFPID | SFFD | SFMEM | SFCWD | SFROOT | SFCSIG)
/* This allows creating a process that is completely forked from the original
process, unlike SFFORK which does share a few things (such as the process
namespace). Note that there is a few unset high bits in this value, these
are reserved and must not be set. */
#define SFALL ((1<<20)-1)
__END_DECLS
#endif

View File

@ -0,0 +1,57 @@
/*******************************************************************************
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2012.
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/>.
sortix/x64/fork.h
Declarations related to the fork family of system calls on x64 Sortix.
*******************************************************************************/
#ifndef SORTIX_X64_FORK_H
#define SORTIX_X64_FORK_H
#include <features.h>
__BEGIN_DECLS
struct sforkregs_x64
{
uint64_t rip;
uint64_t rax;
uint64_t rbx;
uint64_t rcx;
uint64_t rdx;
uint64_t rdi;
uint64_t rsi;
uint64_t rsp;
uint64_t rbp;
uint64_t r8;
uint64_t r9;
uint64_t r10;
uint64_t r11;
uint64_t r12;
uint64_t r13;
uint64_t r14;
uint64_t r15;
uint64_t rflags;
};
__END_DECLS
#endif

View File

@ -0,0 +1,49 @@
/*******************************************************************************
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2012.
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/>.
sortix/x86/fork.h
Declarations related to the fork family of system calls on x86 Sortix.
*******************************************************************************/
#ifndef SORTIX_X86_FORK_H
#define SORTIX_X86_FORK_H
#include <features.h>
__BEGIN_DECLS
struct sforkregs_x86
{
uint32_t eip;
uint32_t eax;
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
uint32_t edi;
uint32_t esi;
uint32_t esp;
uint32_t ebp;
uint32_t eflags;
};
__END_DECLS
#endif

View File

@ -24,6 +24,7 @@
#include <sortix/kernel/platform.h>
#include <sortix/unistd.h>
#include <sortix/fork.h>
#include <libmaxsi/error.h>
#include <libmaxsi/memory.h>
#include <libmaxsi/string.h>
@ -479,14 +480,56 @@ namespace Sortix
return SysExevVEStage2(state);
}
pid_t SysSFork(int flags)
pid_t SysSForkR(int flags, sforkregs_t* regs)
{
// TODO: Properly support sfork(2).
// TODO: Properly support sforkr(2).
if ( flags != SFFORK ) { Error::Set(ENOSYS); return -1; }
// Prepare the state of the clone.
Syscall::SyscallRegs()->result = 0;
CurrentThread()->SaveRegisters(Syscall::InterruptRegs());
CPU::InterruptRegisters cpuregs;
Maxsi::Memory::Set(&cpuregs, 0, sizeof(cpuregs));
#if defined(PLATFORM_X64)
cpuregs.rip = regs->rip;
cpuregs.userrsp = regs->rsp;
cpuregs.rax = regs->rax;
cpuregs.rbx = regs->rbx;
cpuregs.rcx = regs->rcx;
cpuregs.rdx = regs->rdx;
cpuregs.rdi = regs->rdi;
cpuregs.rsi = regs->rsi;
cpuregs.rbp = regs->rbp;
cpuregs.r8 = regs->r8;
cpuregs.r9 = regs->r9;
cpuregs.r10 = regs->r10;
cpuregs.r11 = regs->r11;
cpuregs.r12 = regs->r12;
cpuregs.r13 = regs->r13;
cpuregs.r14 = regs->r14;
cpuregs.r15 = regs->r15;
cpuregs.cs = 0x18 | 0x3;
cpuregs.ds = 0x20 | 0x3;
cpuregs.ss = 0x20 | 0x3;
//cpuregs.rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
cpuregs.rflags = (1<<1) | (1<<9) | (1<<21);
#elif defined(PLATFORM_X86)
cpuregs.eip = regs->eip;
cpuregs.useresp = regs->esp;
cpuregs.eax = regs->eax;
cpuregs.ebx = regs->ebx;
cpuregs.ecx = regs->ecx;
cpuregs.edx = regs->edx;
cpuregs.edi = regs->edi;
cpuregs.esi = regs->esi;
cpuregs.ebp = regs->ebp;
cpuregs.cs = 0x18 | 0x3;
cpuregs.ds = 0x20 | 0x3;
cpuregs.ss = 0x20 | 0x3;
//cpuregs.eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
cpuregs.eflags = (1<<1) | (1<<9) | (1<<21);
#else
#error SysSForkR needs to know about your platform
#endif
CurrentThread()->SaveRegisters(&cpuregs);
Process* clone = CurrentProcess()->Fork();
if ( !clone ) { return -1; }
@ -494,11 +537,6 @@ namespace Sortix
return clone->pid;
}
pid_t SysFork()
{
return SysSFork(SFFORK);
}
pid_t SysGetPID()
{
return CurrentProcess()->pid;
@ -801,8 +839,7 @@ namespace Sortix
void Process::Init()
{
Syscall::Register(SYSCALL_EXEC, (void*) SysExecVE);
Syscall::Register(SYSCALL_FORK, (void*) SysFork);
Syscall::Register(SYSCALL_SFORK, (void*) SysSFork);
Syscall::Register(SYSCALL_SFORKR, (void*) SysSForkR);
Syscall::Register(SYSCALL_GETPID, (void*) SysGetPID);
Syscall::Register(SYSCALL_GETPPID, (void*) SysGetParentPID);
Syscall::Register(SYSCALL_EXIT, (void*) SysExit);