diff --git a/sortix/Makefile b/sortix/Makefile index c50ae359..487c4154 100644 --- a/sortix/Makefile +++ b/sortix/Makefile @@ -100,6 +100,8 @@ memorymanagement.o \ calltrace.o \ $(CPU)/calltrace.o \ kthread.o \ +interlock.o \ +$(CPU)/interlock.o \ panic.o \ keyboard.o \ kb/ps2.o \ diff --git a/sortix/include/sortix/kernel/interlock.h b/sortix/include/sortix/kernel/interlock.h new file mode 100644 index 00000000..a4496231 --- /dev/null +++ b/sortix/include/sortix/kernel/interlock.h @@ -0,0 +1,48 @@ +/******************************************************************************* + + 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 . + + interlock.cpp + Functions that perform non-atomic operations in an atomic manner. + +*******************************************************************************/ + +#ifndef SORTIX_INTERLOCK_H +#define SORTIX_INTERLOCK_H + +namespace Sortix { + +typedef unsigned long (*ilockfunc)(unsigned long, unsigned long); +typedef struct +{ + unsigned long o; // old value + unsigned long n; // new value +} ilret_t; + +// These atomicly modifies a value and return the previous value. +ilret_t InterlockedModify(unsigned long* ptr, + ilockfunc f, + unsigned long user = 0); +ilret_t InterlockedIncrement(unsigned long* ptr); +ilret_t InterlockedDecrement(unsigned long* ptr); +ilret_t InterlockedAdd(unsigned long* ptr, unsigned long arg); +ilret_t InterlockedSub(unsigned long* ptr, unsigned long arg); + +} // namespace Sortix + +#endif diff --git a/sortix/interlock.cpp b/sortix/interlock.cpp new file mode 100644 index 00000000..e3a82242 --- /dev/null +++ b/sortix/interlock.cpp @@ -0,0 +1,73 @@ +/******************************************************************************* + + 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 . + + interlock.cpp + Functions that perform non-atomic operations in an atomic manner. + +*******************************************************************************/ + +#include +#include + +namespace Sortix { + +// TODO: This is likely not the most optimal way to perform these operations. + +extern "C" ilret_t asm_interlocked_modify(unsigned long* val, + ilockfunc f, + unsigned long user); + +ilret_t InterlockedModify(unsigned long* ptr, + ilockfunc f, + unsigned long user) +{ + return asm_interlocked_modify(ptr, f, user); +} + +static unsigned long AddFunction(unsigned long val, unsigned long arg) +{ + return val + arg; +} + +static unsigned long SubFunction(unsigned long val, unsigned long arg) +{ + return val - arg; +} + +ilret_t InterlockedIncrement(unsigned long* ptr) +{ + return InterlockedModify(ptr, AddFunction, 1); +} + +ilret_t InterlockedDecrement(unsigned long* ptr) +{ + return InterlockedModify(ptr, SubFunction, 1); +} + +ilret_t InterlockedAdd(unsigned long* ptr, unsigned long arg) +{ + return InterlockedModify(ptr, AddFunction, arg); +} + +ilret_t InterlockedSub(unsigned long* ptr, unsigned long arg) +{ + return InterlockedModify(ptr, SubFunction, arg); +} + +} // namespace Sortix diff --git a/sortix/x64/interlock.s b/sortix/x64/interlock.s new file mode 100644 index 00000000..d7430040 --- /dev/null +++ b/sortix/x64/interlock.s @@ -0,0 +1,56 @@ +/******************************************************************************* + + 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 . + + x64/interlock.s + Functions that perform non-atomic operations in an atomic manner. + +*******************************************************************************/ + +.section .text + +# Atomicly runs a function on a value and returns the unmodified value. +# ilret_t +# asm_interlocked_modify(unsigned long* val, +# unsigned long (*func)(unsigned long, unsigned long), +# unsigned long user); +.global asm_interlocked_modify +.type asm_interlocked_modify, @function +asm_interlocked_modify: + pushq %rbp + movq %rsp, %rbp +asm_interlocked_modify_retry: + # Save our parameters in case we need to retry. + pushq %rdi + pushq %rsi + pushq %rdx + # Read the current value and calculate the replacement. + movq (%rdi), %rdi + movq %rdx, %rsi + callq *%rsi + movq %rax, %rdx + popq %rax + popq %rdi + popq %rsi + # Atomicly assign the replacement if the value is unchanged. + cmpxchgq %rdx, (%rdi) + # Retry if the value was modified by someone else. + jnz asm_interlocked_modify_retry + # Return the old value in %rax, new one in %rdx. + leaveq + retq diff --git a/sortix/x86/interlock.s b/sortix/x86/interlock.s new file mode 100644 index 00000000..12f990b5 --- /dev/null +++ b/sortix/x86/interlock.s @@ -0,0 +1,61 @@ +/******************************************************************************* + + 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 . + + x86/interlock.s + Functions that perform non-atomic operations in an atomic manner. + +*******************************************************************************/ + +.section .text + +# Atomicly runs a function on a value and returns the unmodified value. +# ilret_t +# asm_interlocked_modify(unsigned long* val, +# unsigned long (*func)(unsigned long, unsigned long), +# unsigned long user); +.global asm_interlocked_modify +.type asm_interlocked_modify, @function +asm_interlocked_modify: + pushl %ebp + movl %esp, %ebp + pushl %ebx +asm_interlocked_modify_retry: + # Read the current value and calculate the replacement. + movl 20(%ebp), %ecx # user + movl 16(%ebp), %edx # func + movl 12(%ebp), %ebx # val + movl (%ebx), %ebx # *val + pushl %ecx + pushl %ebx + call *%edx + # Atomicly assign the replacement if the value is unchanged. + movl %eax, %edx # New value in %edx + movl %ebx, %eax # Old value in %eax + movl 8(%ebp), %ebx # val + cmpxchgl %edx, (%ebx) + # Retry if the value was modified by someone else. + jnz asm_interlocked_modify_retry + # According to the calling convention, the first parameter is secretly a + # pointer to where we store the result, the old value first, then the new. + movl 8(%ebp), %ebx + movl %eax, 0(%ebx) + movl %edx, 4(%ebx) + popl %ebx + leavel + retl $0x4