?? apc.c
字號:
/* * apc.c * * Copyright (C) 2006 Insigme Co., Ltd * * Authors: * - Limin Jin * * This software has been developed while working on the Linux Unified Kernel * project (http://linux.insigma.com.cn) in the Insigma Reaserch Institute, * which is a subdivision of Insigma Co., Ltd (http://www.insigma.com.cn). * * The project is sponsored by Insigma Co., Ltd. * * The authors can be reached at linux@insigma.com.cn. * * This program 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 2 of the License, or (at your * option) any later version. * * Revision History: * Jan 2006 - Created. *//* * apc.c: apc implementation * Reference to ReactOS code */#include <linux/module.h>#include "apc.h"#include "thread.h"#include "process.h"#include "w32syscall.h"#include <linux/winternl.h>#include <linux/list.h>#ifdef CONFIG_UNIFIED_KERNELunsigned long get_apc_dispatcher(void);/* * KiFreeApcRoutine * Free apc */VOIDSTDCALLKiFreeApcRoutine(PKAPC Apc, PKNORMAL_ROUTINE* NormalRoutine, PVOID* NormalContext, PVOID* SystemArgument1, PVOID* SystemArgument2){ /* Free the APC */ kfree(Apc);} /* end KiFreeApcRoutine *//* PsExitSpecialApc*/VOIDSTDCALLPsExitSpecialApc(PKAPC Apc, PKNORMAL_ROUTINE* NormalRoutine, PVOID* NormalContext, PVOID* SystemArgument1, PVOID* SystemArguemnt2){ int exit_status = (int) Apc->normal_routine; ktrace("PsExitSpecialApc called to exit thread: 0x%p\n",thread_find()); /* Free the APC */ kfree(Apc); /* Terminate the Thread */ ExitCurrentThread(current, exit_status); } /* end PsExitSpecialApc *//* * FUNCTION: Tests whether there are any pending APCs for the current thread * and if so the APCs will be delivered on exit from kernel mode */BOOLEANSTDCALLKeTestAlertThread(IN KPROCESSOR_MODE AlertMode){ /* FIXME; */ return 0;}/* * KeInitializeApc * Initialize Apc */VOIDSTDCALLKeInitializeApc(IN PKAPC Apc, IN PKTHREAD Thread, IN KAPC_ENVIRONMENT TargetEnvironment, IN PKKERNEL_ROUTINE KernelRoutine, IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL, IN PKNORMAL_ROUTINE NormalRoutine, IN KPROCESSOR_MODE Mode, IN PVOID Context){ ktrace("KeInitializeApc(Apc %p, Thread %p, Environment %d, KernelRoutine %p, \ RundownRoutine %p, NormalRoutine %p, Mode %d, Context %p)\n", Apc, &Thread, TargetEnvironment, KernelRoutine, RundownRoutine, NormalRoutine, Mode, Context); Apc->type = ApcObject; Apc->size = sizeof(struct kapc); if (TargetEnvironment == CurrentApcEnvironment) Apc->apc_state_index = Thread->apc_state_index; else Apc->apc_state_index = TargetEnvironment; /* Set the Thread and Routines */ Apc->thread = Thread; Apc->kernel_routine = KernelRoutine; Apc->rundown_routine = NULL; /*RundownRoutine has't been implemented*/ Apc->normal_routine = NormalRoutine; /* Check if this is a Special APC, in which case we use KernelMode and no Context */ if (NormalRoutine!=NULL) { Apc->apc_mode = Mode; Apc->normal_context = Context; } else Apc->apc_mode = KernelMode; Apc->inserted = 0; return;} /* end KiInitializeUserApc *//* * KiInsertQueueApc * Insert apc to apc queue */BOOLEANSTDCALLKiInsertQueueApc(PKAPC Apc, KPRIORITY PriorityBoost){ struct kthread *thread = Apc->thread; struct list_head *head, *apc_listentry; struct kapc *queued_apc; struct kapc_state *apc_state; ktrace("KiInsertQueueApc\n"); /* Don't do anything if the APC is already inserted */ if (Apc->inserted) return 0; /* * Three scenarios: * 1) Kernel APC with Normal Routine or User APC = Put it at the end of the List * 2) User APC which is PsExitSpecialApc = Put it at the front of the List * 3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine * Kernel APC list */ apc_state = thread->apc_state_pointer[(int)Apc->apc_state_index]; head = &apc_state->apc_list_head[(int)(Apc->apc_mode)]; if ((Apc->apc_mode != KernelMode) && (Apc->kernel_routine == (PKKERNEL_ROUTINE)PsExitSpecialApc)) { /* 2) */ ktrace("Inserting the Process Exit APC into the Queue\n"); list_add(&Apc->apc_list_entry, head); } else if (!Apc->normal_routine) { /* 3) */ ktrace("Inserting Special APC %p into the Queue\n", Apc); for (apc_listentry = head->next; apc_listentry != head; apc_listentry = apc_listentry->next) { queued_apc = list_entry(apc_listentry, struct kapc, apc_list_entry); if (queued_apc->normal_routine != NULL) break; } /* We found the first "Normal" APC, so write right before it */ apc_listentry = apc_listentry->prev; list_add(&Apc->apc_list_entry, apc_listentry); } else { /* 1) */ ktrace("Inserting Normal APC %p into the %x Queue\n", Apc, Apc->apc_mode); list_add_tail(&Apc->apc_list_entry, head); } /* FIXME * Three possibilites here again: * 1) Kernel APC, The thread is Running: Request an Interrupt * 2) Kernel APC, The Thread is Waiting at PASSIVE_LEVEL and APCs are enabled * and not in progress: Unwait the Thread * 3) User APC, Unwait the Thread if it is alertable */ /* Confirm Insertion */ Apc->inserted = 1; if (Apc->apc_mode == KernelMode) thread->apc_state.kapc_pending = 1; else thread->apc_state.uapc_pending = 1; return 1;} /* end KiInsertQueueApc *//* * KeInsertQueueApc */BOOLEANSTDCALLKeInsertQueueApc(PKAPC Apc, PVOID SystemArgument1, PVOID SystemArgument2, KPRIORITY PriorityBoost){ BOOL inserted; struct kthread *thread; ktrace("KeInsertQueueApc(Apc %p, SystemArgument1 %p, SystemArgument2 %p)\n", Apc, SystemArgument1, SystemArgument2); /* Get the Thread specified in the APC */ thread = Apc->thread; /* Disable interrupt and lock shared resource */ spin_lock_irq(&thread->apc_queue_lock); if (!thread->apc_queueable) { kdebug("Thread doesn't allow APC Queues\n"); spin_unlock_irq(&thread->apc_queue_lock); return 0; } Apc->system_argument1 = SystemArgument1; Apc->system_argument2 = SystemArgument2; inserted = KiInsertQueueApc(Apc, PriorityBoost); /* Enable interrupt and unlock shared resource */ spin_unlock_irq(&thread->apc_queue_lock); return inserted;} /* end KeInsertQueueApc *//* * NtQueueApcThread * system call NtQueueApcThread * which called from user */NTSTATUS STDCALLNtQueueApcThread(HANDLE ThreadHandle, PKNORMAL_ROUTINE ApcRoutine, PVOID NormalContext, PVOID SystemArgument1, PVOID SystemArgument2){ struct kapc *apc; struct ethread *thread; NTSTATUS status; ktrace("NtQueueApcThread\n"); if (!(thread = thread_find())) { return -EINVAL; } /* Set thread_info's flag */ set_tsk_thread_flag(thread->et_task, TIF_APC); if (!(apc = kmalloc(sizeof(struct kapc),GFP_KERNEL))) { kdebug("***error malloc, No memory\n"); return -EINVAL; } KeInitializeApc(apc, &thread->tcb, OriginalApcEnvironment, KiFreeApcRoutine, NULL, ApcRoutine, UserMode, NormalContext); if (!KeInsertQueueApc(apc, SystemArgument1, SystemArgument2, IO_NO_INCREMENT)) status = STATUS_UNSUCCESSFUL; else status = STATUS_SUCCESS; return status;} /* end NtQueueApcThread *//* * KiDeliverApc * Dequeue the apc queue , and call apc function one by one */VOID STDCALLKiDeliverApc(KPROCESSOR_MODE DeliveryMode, PVOID Reserved, struct pt_regs * TrapFrame){ struct ethread *thread; struct list_head * apc_listentry; struct kapc *apc; kernel_routine_t kernel_routine; void * normal_context; normal_routine_t normal_routine; void *system_argument1; void *system_argument2; ktrace("KiDeliverApc(DeliverMode 0x%x, Reserved 0x%p, TrapFrame 0x%p)\n", DeliveryMode, Reserved, TrapFrame); if (!(thread = thread_find())) { kdebug("***error find thread\n"); clear_tsk_thread_flag(thread->et_task,TIF_APC); return; } /* Disable interrupt and lock shared resource */ spin_lock_irq(&thread->tcb.apc_queue_lock); /* Do the Kernel APCs first */ while (!list_empty(&thread->tcb.apc_state.apc_list_head[KernelMode])) { /* Get the next Entry */ apc_listentry = thread->tcb.apc_state.apc_list_head[KernelMode].next; apc = list_entry(apc_listentry, struct kapc, apc_list_entry); /* Save Parameters so that it's safe to free the Object in Kernel Routine*/ normal_routine = apc->normal_routine; kernel_routine = apc->kernel_routine; normal_context = apc->normal_context; system_argument1 = apc->system_argument1; system_argument2 = apc->system_argument2; if (!normal_routine) { ktrace("2) Delivering Special APC: 0x%p\n", apc); /* Remove the APC from the list */ apc->inserted= 0; list_del(apc_listentry); /* Enable interrupt and unlock shared resource */ spin_unlock_irq(&thread->tcb.apc_queue_lock); /* Call the Special APC */ kernel_routine(apc, &normal_routine, &normal_context, &system_argument1, &system_argument2); /* Disable interrupt and lock shared resource again */ spin_lock_irq(&thread->tcb.apc_queue_lock); } else { ktrace("1) Delivering a Normal APC: 0x%p\n", apc); /* FIXME * DeliveryMode must be KernelMode in this case, since one may not * return to umode while being inside a critical section or while * a regular kmode apc is running (the latter should be impossible btw). */ /* Dequeue the APC */ list_del(apc_listentry); apc->inserted = 0; /* Enable interrupt and unlock shared resource */ spin_unlock_irq(&thread->tcb.apc_queue_lock); /* Call the Kernel APC */ kernel_routine(apc, &normal_routine, &normal_context, &system_argument1, &system_argument2); /* If There still is a Normal Routine, then we need to call it */ if (normal_routine) { /* which is unplemented for premting by special apc */ thread->tcb.apc_state.kapc_inprogress = 1; normal_routine(&normal_context, &system_argument1, &system_argument2); } /* Disable interrupt and lock shared resource again */ spin_lock_irq(&thread->tcb.apc_queue_lock); thread->tcb.apc_state.kapc_inprogress = 0; } } /* Clear APC Pending */ thread->tcb.apc_state.kapc_pending = 0; /* Now we do the User APCs */ if ((!list_empty(&thread->tcb.apc_state.apc_list_head[UserMode])) && (thread->tcb.apc_state.uapc_pending)) { /* && (DeliveryMode == UserMode) */ ktrace("3) Delivering a User APC: 0x%p\n", apc); /* Get the APC Object */ apc_listentry = thread->tcb.apc_state.apc_list_head[UserMode].next; apc = list_entry(apc_listentry, struct kapc, apc_list_entry); /* Save Parameters so that it's safe to free the Object in Kernel Routine*/ normal_routine = apc->normal_routine; kernel_routine = apc->kernel_routine; normal_context = apc->normal_context; system_argument1 = apc->system_argument1; system_argument2 = apc->system_argument2; /* Remove the APC from Queue, call the APC */ list_del(apc_listentry); apc->inserted = 0; /* Enable interrupt and unlock shared resource */ spin_unlock_irq(&thread->tcb.apc_queue_lock); kernel_routine(apc, &normal_routine, &normal_context, &system_argument1, &system_argument2); if (!normal_routine) { KeTestAlertThread(UserMode); /* Unimplements */ } else { /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */ KiInitializeUserApc(Reserved, TrapFrame, normal_routine, normal_context, system_argument1, system_argument2); } } else { /* It's not pending anymore */ thread->tcb.apc_state.uapc_pending = 0; /* Enable interrupt and unlock shared resource */ spin_unlock_irq(&thread->tcb.apc_queue_lock); /* Clear thread_info's flag */ clear_tsk_thread_flag(thread->et_task,TIF_APC); } return;} /* end KiDeliverApc *//* * KiInitializeUserApc * Save the trapframe and set the esp for returning to user space to call apc function */VOIDSTDCALLKiInitializeUserApc(IN PVOID Reserved, IN PKTRAP_FRAME TrapFrame, IN PKNORMAL_ROUTINE NormalRoutine, IN PVOID NormalContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2) { PContext context; PULONG esp; ktrace("KiInitializeUserApc, ESP 0x%lx\n", TrapFrame->esp); /* * Save the thread's current context (in other words the registers * that will be restored when it returns to user mode) so the * APC dispatcher can restore them later */ context = (PContext)(((PUCHAR)TrapFrame->esp) - sizeof(*context)); memcpy(context, TrapFrame, sizeof(*context)); /* * Setup the trap frame so the thread will start executing at the * APC Dispatcher when it returns to user-mode */ esp = (PULONG)(((PUCHAR)TrapFrame->esp) - (sizeof(CONTEXT) + (6 * sizeof(ULONG)))); esp[0] = 0xdeadbeef; esp[1] = (ULONG)NormalRoutine; esp[2] = (ULONG)NormalContext; esp[3] = (ULONG)SystemArgument1; esp[4] = (ULONG)SystemArgument2; esp[5] = (ULONG)context; TrapFrame->eip = get_apc_dispatcher(); TrapFrame->esp = (ULONG)esp;} /* end KiInitializeUserApc *//* * NtContinue * Go back to kernel space */NTSTATUS STDCALLNtContinue(IN PContext Context, IN BOOLEAN TestAlert){ PKTRAP_FRAME trap_frame = (PKTRAP_FRAME)current->ethread->tcb.trap_frame; /* * Copy the supplied context over the register information that was saved * on entry to kernel mode, it will then be restored on exit * FIXME: Validate the context */ memcpy(trap_frame, Context, sizeof(*trap_frame)); /* FIXME * Copy floating point context into the thread's FX_SAVE_AREA */ __asm__( "andl %%esp, %%ecx;\n\t" "movl %%ecx, %%ebp;\n\t" "movl %%ebx, %%esp;\n\t" "jmp w32syscall_exit\n\t" : : "b" (trap_frame), "c" (-THREAD_SIZE)); /* This doesn't actually happen b/c KeRosTrapReturn() won't return */ return STATUS_SUCCESS;} /* NtContinue */#endif
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -