mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
- Move all ASM internal intrinsics to intrin_i.h. Request for KJK::Hyperion/hackbunny to look at the GCC ones and optimize/fix them up.
- Add MSVC versions for some of them (not yet complete). - Fix the fact that KeGetLocalDescriptorTable was setting the LDT instead of retrieving it. - Fix bug in LIST_FOR_EACH and LIST_FOR_EACH_SAFE which was setting the flink to NULL instead of checking if the flink is NULL. One more reason these damned macros should've never been used. - Use MSVC-intrinsics when applicable (_disable/_enable, etc). - Fix JOB_SET_ARRAY problems. - Fix buffer overflow in SystemProcessInformation QSI_DEF. - Fix some broken compares/arithmetic to due to lack of parens. - Add some ASSERTS to some unknown functions that make pointer assumptions. svn path=/trunk/; revision=24650
This commit is contained in:
parent
6f17cc84f7
commit
b5e25167e2
30 changed files with 282 additions and 316 deletions
|
@ -154,7 +154,7 @@ NTAPI
|
|||
FrLdrStartup(ULONG Magic)
|
||||
{
|
||||
/* Disable Interrupts */
|
||||
Ke386DisableInterrupts();
|
||||
_disable();
|
||||
|
||||
/* Re-initalize EFLAGS */
|
||||
Ke386EraseFlags();
|
||||
|
@ -202,7 +202,7 @@ FrLdrSetupPae(ULONG Magic)
|
|||
}
|
||||
|
||||
/* Set the PDBR */
|
||||
Ke386SetPageTableDirectory(PageDirectoryBaseAddress);
|
||||
Ke386SetCr3(PageDirectoryBaseAddress);
|
||||
|
||||
/* Enable Paging and Write Protect*/
|
||||
Ke386SetCr0(Ke386GetCr0() | X86_CR0_PG | X86_CR0_WP);
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
#include <arch/i386/i386.h>
|
||||
#include <arch/i386/machpc.h>
|
||||
#include <arch/i386/machxbox.h>
|
||||
#include <internal/i386/intrin_i.h>
|
||||
#include <internal/i386/ke.h>
|
||||
#endif
|
||||
/* misc files */
|
||||
|
|
|
@ -560,13 +560,13 @@ WinLdrTurnOnPaging(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
|
|||
//BS->ExitBootServices(ImageHandle,MapKey);
|
||||
|
||||
// Disable Interrupts
|
||||
Ke386DisableInterrupts();
|
||||
_disable();
|
||||
|
||||
// Re-initalize EFLAGS
|
||||
Ke386EraseFlags();
|
||||
|
||||
// Set the PDBR
|
||||
Ke386SetPageTableDirectory((ULONG_PTR)PDE);
|
||||
Ke386SetCr3((ULONG_PTR)PDE);
|
||||
|
||||
// Enable paging by modifying CR0
|
||||
Ke386SetCr0(Ke386GetCr0() | CR0_PG);
|
||||
|
|
|
@ -264,11 +264,9 @@ typedef struct _DESCRIPTOR
|
|||
#include <poppack.h>
|
||||
|
||||
#ifndef NTOS_MODE_USER
|
||||
|
||||
//
|
||||
// Macro to get current KPRCB
|
||||
//
|
||||
#ifndef __GNUC__ // fixme
|
||||
FORCEINLINE
|
||||
struct _KPRCB *
|
||||
KeGetCurrentPrcb(VOID)
|
||||
|
@ -276,12 +274,6 @@ KeGetCurrentPrcb(VOID)
|
|||
return (struct _KPRCB *)(ULONG_PTR)__readfsdword(FIELD_OFFSET(KPCR, Prcb));
|
||||
}
|
||||
|
||||
//
|
||||
// Macro to get current previous mode
|
||||
//
|
||||
#define KeGetPreviousMode ExGetPreviousMode
|
||||
#endif
|
||||
|
||||
//
|
||||
// FN/FX (FPU) Save Area Structures
|
||||
//
|
||||
|
|
|
@ -914,6 +914,16 @@ typedef struct _THREAD_BASIC_INFORMATION
|
|||
|
||||
#ifndef NTOS_MODE_USER
|
||||
|
||||
//
|
||||
// Job Set Array
|
||||
//
|
||||
typedef struct _JOB_SET_ARRAY
|
||||
{
|
||||
HANDLE JobHandle;
|
||||
ULONG MemberLevel;
|
||||
ULONG Flags;
|
||||
} JOB_SET_ARRAY, *PJOB_SET_ARRAY;
|
||||
|
||||
//
|
||||
// EPROCESS Quota Structures
|
||||
//
|
||||
|
|
|
@ -3384,7 +3384,6 @@ typedef enum _JOBOBJECTINFOCLASS {
|
|||
JobObjectJobSetInformation,
|
||||
MaxJobObjectInfoClass
|
||||
} JOBOBJECTINFOCLASS;
|
||||
#endif
|
||||
|
||||
typedef struct _JOB_SET_ARRAY
|
||||
{
|
||||
|
@ -3392,6 +3391,7 @@ typedef struct _JOB_SET_ARRAY
|
|||
DWORD MemberLevel;
|
||||
DWORD Flags;
|
||||
} JOB_SET_ARRAY, *PJOB_SET_ARRAY;
|
||||
#endif
|
||||
|
||||
typedef struct _JOBOBJECT_BASIC_ACCOUNTING_INFORMATION {
|
||||
LARGE_INTEGER TotalUserTime;
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
*/
|
||||
#define LIST_FOR_EACH(elem, list, type, field) \
|
||||
for ((elem) = CONTAINING_RECORD((list)->Flink, type, field); \
|
||||
&(elem)->field != (list) || (elem = NULL); \
|
||||
&(elem)->field != (list) || (elem == NULL); \
|
||||
(elem) = CONTAINING_RECORD((elem)->field.Flink, type, field))
|
||||
|
||||
/* iterate through the list using a list entry, with safety against removal
|
||||
|
@ -50,7 +50,7 @@
|
|||
#define LIST_FOR_EACH_SAFE(cursor, cursor2, list, type, field) \
|
||||
for ((cursor) = CONTAINING_RECORD((list)->Flink, type, field), \
|
||||
(cursor2) = CONTAINING_RECORD((cursor)->field.Flink, type, field); \
|
||||
&(cursor)->field != (list) || (cursor = NULL); \
|
||||
&(cursor)->field != (list) || (cursor == NULL); \
|
||||
(cursor) = (cursor2), \
|
||||
(cursor2) = CONTAINING_RECORD((cursor)->field.Flink, type, field))
|
||||
|
||||
|
|
|
@ -786,6 +786,7 @@ CcRosGetCacheSegmentChain(PBCB Bcb,
|
|||
Previous = CacheSegList[i];
|
||||
}
|
||||
}
|
||||
ASSERT(Previous);
|
||||
Previous->NextInChain = NULL;
|
||||
|
||||
return(STATUS_SUCCESS);
|
||||
|
|
|
@ -480,7 +480,7 @@ KiInterruptDispatch3 (ULONG vector, PKIRQ_TRAPFRAME Trapframe)
|
|||
* Enable interrupts
|
||||
* NOTE: Only higher priority interrupts will get through
|
||||
*/
|
||||
Ke386EnableInterrupts();
|
||||
_enable();
|
||||
|
||||
#ifndef CONFIG_SMP
|
||||
if (VECTOR2IRQ(vector) == 0)
|
||||
|
@ -500,7 +500,7 @@ KiInterruptDispatch3 (ULONG vector, PKIRQ_TRAPFRAME Trapframe)
|
|||
/*
|
||||
* End the system interrupt.
|
||||
*/
|
||||
Ke386DisableInterrupts();
|
||||
_disable();
|
||||
|
||||
if (old_level==PASSIVE_LEVEL && Trapframe->Cs != KGDT_R0_CODE)
|
||||
{
|
||||
|
@ -512,8 +512,8 @@ KiInterruptDispatch3 (ULONG vector, PKIRQ_TRAPFRAME Trapframe)
|
|||
DPRINT("PID: %d, TID: %d CS %04x/%04x\n",
|
||||
((PETHREAD)CurrentThread)->ThreadsProcess->UniqueProcessId,
|
||||
((PETHREAD)CurrentThread)->Cid.UniqueThread,
|
||||
Trapframe->Cs,
|
||||
CurrentThread->TrapFrame ? CurrentThread->TrapFrame->Cs : 0);
|
||||
Trapframe->SegCs,
|
||||
CurrentThread->TrapFrame ? CurrentThread->TrapFrame->SegCs : 0);
|
||||
if (CurrentThread->TrapFrame == NULL)
|
||||
{
|
||||
OldTrapFrame = CurrentThread->TrapFrame;
|
||||
|
@ -521,9 +521,9 @@ KiInterruptDispatch3 (ULONG vector, PKIRQ_TRAPFRAME Trapframe)
|
|||
CurrentThread->TrapFrame = &KernelTrapFrame;
|
||||
}
|
||||
|
||||
Ke386EnableInterrupts();
|
||||
_enable();
|
||||
KiDeliverApc(UserMode, NULL, NULL);
|
||||
Ke386DisableInterrupts();
|
||||
_disable();
|
||||
|
||||
ASSERT(KeGetCurrentThread() == CurrentThread);
|
||||
if (CurrentThread->TrapFrame == &KernelTrapFrame)
|
||||
|
|
|
@ -686,8 +686,8 @@ QSI_DEF(SystemProcessInformation)
|
|||
SpiCur->QuotaPagedPoolUsage = pr->QuotaUsage[0];
|
||||
SpiCur->QuotaPeakNonPagedPoolUsage = pr->QuotaPeak[1];
|
||||
SpiCur->QuotaNonPagedPoolUsage = pr->QuotaUsage[1];
|
||||
SpiCur->PagefileUsage = pr->QuotaUsage[3];
|
||||
SpiCur->PeakPagefileUsage = pr->QuotaPeak[3];
|
||||
SpiCur->PagefileUsage = pr->QuotaUsage[2];
|
||||
SpiCur->PeakPagefileUsage = pr->QuotaPeak[2];
|
||||
SpiCur->PrivatePageCount = pr->CommitCharge;
|
||||
ThreadInfo = (PSYSTEM_THREAD_INFORMATION)(SpiCur + 1);
|
||||
|
||||
|
@ -1687,10 +1687,10 @@ NtFlushInstructionCache (
|
|||
IN ULONG NumberOfBytesToFlush
|
||||
)
|
||||
{
|
||||
PAGED_CODE();
|
||||
PAGED_CODE();
|
||||
|
||||
__asm__("wbinvd\n");
|
||||
return STATUS_SUCCESS;
|
||||
Ke386WbInvd();
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
ULONG
|
||||
|
|
|
@ -34,9 +34,9 @@ PVOID ExpNlsSectionPointer;
|
|||
#ifndef CONFIG_SMP
|
||||
#define ExAcquireResourceLock(l, i) { \
|
||||
(void)i; \
|
||||
Ke386DisableInterrupts(); \
|
||||
_disable(); \
|
||||
}
|
||||
#define ExReleaseResourceLock(l, i) Ke386EnableInterrupts();
|
||||
#define ExReleaseResourceLock(l, i) _enable();
|
||||
#else
|
||||
#define ExAcquireResourceLock(l, i) KeAcquireSpinLock(l, i);
|
||||
#define ExReleaseResourceLock(l, i) KeReleaseSpinLock(l, i);
|
||||
|
|
202
reactos/ntoskrnl/include/internal/i386/intrin_i.h
Normal file
202
reactos/ntoskrnl/include/internal/i386/intrin_i.h
Normal file
|
@ -0,0 +1,202 @@
|
|||
#ifndef _INTRIN_INTERNAL_
|
||||
#define _INTRIN_INTERNAL_
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
#define LOCK "lock ; "
|
||||
#else
|
||||
#define LOCK ""
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
|
||||
#define Ke386SetInterruptDescriptorTable(X) \
|
||||
__asm__("lidt %0\n\t" \
|
||||
: /* no outputs */ \
|
||||
: "m" (X));
|
||||
|
||||
#define Ke386GetInterruptDescriptorTable(X) \
|
||||
__asm__("sidt %0\n\t" \
|
||||
: /* no outputs */ \
|
||||
: "m" (X));
|
||||
|
||||
#define Ke386SetGlobalDescriptorTable(X) \
|
||||
__asm__("lgdt %0\n\t" \
|
||||
: /* no outputs */ \
|
||||
: "m" (X));
|
||||
|
||||
#define Ke386GetGlobalDescriptorTable(X) \
|
||||
__asm__("sgdt %0\n\t" \
|
||||
: /* no outputs */ \
|
||||
: "m" (X));
|
||||
|
||||
#define Ke386GetLocalDescriptorTable(X) \
|
||||
__asm__("sldt %0\n\t" \
|
||||
: /* no outputs */ \
|
||||
: "m" (X));
|
||||
|
||||
#define Ke386SetLocalDescriptorTable(X) \
|
||||
__asm__("lldt %w0\n\t" \
|
||||
: /* no outputs */ \
|
||||
: "q" (X));
|
||||
|
||||
#define Ke386SaveFlags(x) __asm__ __volatile__("pushfl ; popl %0":"=g" (x): /* no input */)
|
||||
#define Ke386RestoreFlags(x) __asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory")
|
||||
|
||||
#define _Ke386GetSeg(N) ({ \
|
||||
unsigned int __d; \
|
||||
__asm__("movl %%" #N ",%0\n\t" :"=r" (__d)); \
|
||||
__d; \
|
||||
})
|
||||
|
||||
#define _Ke386SetSeg(N,X) __asm__ __volatile__("movl %0,%%" #N : :"r" (X));
|
||||
|
||||
#define _Ke386GetCr(N) ({ \
|
||||
unsigned int __d; \
|
||||
__asm__("movl %%cr" #N ",%0\n\t" :"=r" (__d)); \
|
||||
__d; \
|
||||
})
|
||||
|
||||
#define _Ke386GetDr(N) ({ \
|
||||
unsigned int __d; \
|
||||
__asm__("movl %%dr" #N ",%0\n\t" :"=r" (__d)); \
|
||||
__d; \
|
||||
})
|
||||
|
||||
#define _Ke386SetCr(N,X) __asm__ __volatile__("movl %0,%%cr" #N : :"r" (X));
|
||||
#define _Ke386SetDr(N,X) __asm__ __volatile__("movl %0,%%dr" #N : :"r" (X));
|
||||
|
||||
#define Ke386SetTr(X) __asm__ __volatile__("ltr %%ax" : :"a" (X));
|
||||
#define Ke386GetTr(X) __asm__ __volatile__("str %%ax" : :"a" (X));
|
||||
|
||||
static inline void Ki386Cpuid(ULONG Op, PULONG Eax, PULONG Ebx, PULONG Ecx, PULONG Edx)
|
||||
{
|
||||
__asm__("cpuid"
|
||||
: "=a" (*Eax), "=b" (*Ebx), "=c" (*Ecx), "=d" (*Edx)
|
||||
: "0" (Op));
|
||||
}
|
||||
|
||||
#define Ke386Rdmsr(msr,val1,val2) __asm__ __volatile__("rdmsr" : "=a" (val1), "=d" (val2) : "c" (msr))
|
||||
|
||||
#define Ke386Wrmsr(msr,val1,val2) __asm__ __volatile__("wrmsr" : /* no outputs */ : "c" (msr), "a" (val1), "d" (val2))
|
||||
|
||||
#define FLUSH_TLB { \
|
||||
unsigned int tmp; \
|
||||
__asm__ __volatile__( \
|
||||
"movl %%cr3,%0\n\t" \
|
||||
"movl %0,%%cr3\n\t" \
|
||||
: "=r" (tmp) \
|
||||
:: "memory"); \
|
||||
}
|
||||
|
||||
#define FLUSH_TLB_ONE(addr) __asm__ __volatile__( \
|
||||
"invlpg %0" \
|
||||
: \
|
||||
: "m" (*(volatile long *) (addr)))
|
||||
|
||||
#define Ke386HaltProcessor() __asm__("hlt\n\t");
|
||||
#define Ke386FnInit() __asm__("fninit\n\t");
|
||||
#define Ke386WbInvd() __asm__("wbinvd\n\t");
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
VOID
|
||||
FORCEINLINE
|
||||
Ke386WbInvd(VOID)
|
||||
{
|
||||
__asm wbinvd;
|
||||
}
|
||||
|
||||
VOID
|
||||
FORCEINLINE
|
||||
Ke386FnInit(VOID)
|
||||
{
|
||||
__asm fninit;
|
||||
}
|
||||
|
||||
VOID
|
||||
FORCEINLINE
|
||||
Ke386HaltProcessor(VOID)
|
||||
{
|
||||
__asm hlt;
|
||||
}
|
||||
|
||||
VOID
|
||||
FORCEINLINE
|
||||
Ke386GetInterruptDescriptorTable(OUT KDESCRIPTOR Descriptor)
|
||||
{
|
||||
__asm sidt Descriptor;
|
||||
}
|
||||
|
||||
VOID
|
||||
FORCEINLINE
|
||||
Ke386SetInterruptDescriptorTable(IN KDESCRIPTOR Descriptor)
|
||||
{
|
||||
__asm lidt Descriptor;
|
||||
}
|
||||
|
||||
VOID
|
||||
FORCEINLINE
|
||||
Ke386GetGlobalDescriptorTable(OUT KDESCRIPTOR Descriptor)
|
||||
{
|
||||
__asm sgdt Descriptor;
|
||||
}
|
||||
|
||||
VOID
|
||||
FORCEINLINE
|
||||
Ke386SetGlobalDescriptorTable(IN KDESCRIPTOR Descriptor)
|
||||
{
|
||||
__asm lgdt Descriptor;
|
||||
}
|
||||
|
||||
VOID
|
||||
FORCEINLINE
|
||||
Ke386GetLocalDescriptorTable(OUT USHORT Descriptor)
|
||||
{
|
||||
__asm sldt Descriptor;
|
||||
}
|
||||
|
||||
VOID
|
||||
FORCEINLINE
|
||||
Ke386SetLocalDescriptorTable(IN USHORT Descriptor)
|
||||
{
|
||||
__asm lldt Descriptor;
|
||||
}
|
||||
|
||||
#else
|
||||
#error Unknown compiler for inline assembler
|
||||
#endif
|
||||
|
||||
//
|
||||
// CR Macros
|
||||
//
|
||||
#define Ke386GetCr0() _Ke386GetCr(0)
|
||||
#define Ke386SetCr0(X) _Ke386SetCr(0,X)
|
||||
#define Ke386GetCr2() _Ke386GetCr(2)
|
||||
#define Ke386SetCr2(X) _Ke386SetCr(2,X)
|
||||
#define Ke386GetCr3() _Ke386GetCr(3)
|
||||
#define Ke386SetCr3(X) _Ke386SetCr(3,X)
|
||||
#define Ke386GetCr4() _Ke386GetCr(4)
|
||||
#define Ke386SetCr4(X) _Ke386SetCr(4,X)
|
||||
|
||||
//
|
||||
// DR Macros
|
||||
//
|
||||
#define Ke386GetDr0() _Ke386GetDr(0)
|
||||
#define Ke386SetDr0(X) _Ke386SetDr(0,X)
|
||||
#define Ke386GetDr2() _Ke386GetDr(2)
|
||||
#define Ke386SetDr2(X) _Ke386SetDr(2,X)
|
||||
#define Ke386GetDr4() _Ke386GetDr(4)
|
||||
#define Ke386SetDr4(X) _Ke386SetDr(4,X)
|
||||
|
||||
//
|
||||
// Segment Macros
|
||||
//
|
||||
#define Ke386GetSs() _Ke386GetSeg(ss)
|
||||
#define Ke386GetFs() _Ke386GetSeg(fs)
|
||||
#define Ke386SetFs(X) _Ke386SetSeg(fs, X)
|
||||
#define Ke386SetDs(X) _Ke386SetSeg(ds, X)
|
||||
#define Ke386SetEs(X) _Ke386SetSeg(es, X)
|
||||
|
||||
#endif
|
||||
|
||||
/* EOF */
|
|
@ -136,186 +136,6 @@ KiThreadStartup(PKSYSTEM_ROUTINE SystemRoutine,
|
|||
KTRAP_FRAME TrapFrame);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
#define LOCK "lock ; "
|
||||
#else
|
||||
#define LOCK ""
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define Ke386DisableInterrupts() __asm__("cli\n\t");
|
||||
#define Ke386EnableInterrupts() __asm__("sti\n\t");
|
||||
#define Ke386HaltProcessor() __asm__("hlt\n\t");
|
||||
#define Ke386GetPageTableDirectory(X) \
|
||||
__asm__("movl %%cr3,%0\n\t" : "=d" (X));
|
||||
#define Ke386SetPageTableDirectory(X) \
|
||||
__asm__("movl %0,%%cr3\n\t" \
|
||||
: /* no outputs */ \
|
||||
: "r" (X));
|
||||
#define Ke386SetFileSelector(X) \
|
||||
__asm__("movl %0,%%cr3\n\t" \
|
||||
: /* no outputs */ \
|
||||
: "r" (X));
|
||||
#define Ke386SetLocalDescriptorTable(X) \
|
||||
__asm__("lldt %0\n\t" \
|
||||
: /* no outputs */ \
|
||||
: "m" (X));
|
||||
#define Ke386SetInterruptDescriptorTable(X) \
|
||||
__asm__("lidt %0\n\t" \
|
||||
: /* no outputs */ \
|
||||
: "m" (X));
|
||||
#define Ke386SetGlobalDescriptorTable(X) \
|
||||
__asm__("lgdt %0\n\t" \
|
||||
: /* no outputs */ \
|
||||
: "m" (X));
|
||||
|
||||
#define Ke386GetInterruptDescriptorTable(X) \
|
||||
__asm__("sidt %0\n\t" \
|
||||
: /* no outputs */ \
|
||||
: "m" (X));
|
||||
|
||||
#define Ke386GetGlobalDescriptorTable(X) \
|
||||
__asm__("sgdt %0\n\t" \
|
||||
: /* no outputs */ \
|
||||
: "m" (X));
|
||||
|
||||
#define Ke386GetLocalDescriptorTable(X) \
|
||||
__asm__("lldt %0\n\t" \
|
||||
: /* no outputs */ \
|
||||
: "m" (X));
|
||||
|
||||
#define Ke386SaveFlags(x) __asm__ __volatile__("pushfl ; popl %0":"=g" (x): /* no input */)
|
||||
#define Ke386RestoreFlags(x) __asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory")
|
||||
|
||||
#define _Ke386GetSeg(N) ({ \
|
||||
unsigned int __d; \
|
||||
__asm__("movl %%" #N ",%0\n\t" :"=r" (__d)); \
|
||||
__d; \
|
||||
})
|
||||
|
||||
#define _Ke386GetCr(N) ({ \
|
||||
unsigned int __d; \
|
||||
__asm__("movl %%cr" #N ",%0\n\t" :"=r" (__d)); \
|
||||
__d; \
|
||||
})
|
||||
|
||||
#define _Ke386GetDr(N) ({ \
|
||||
unsigned int __d; \
|
||||
__asm__("movl %%dr" #N ",%0\n\t" :"=r" (__d)); \
|
||||
__d; \
|
||||
})
|
||||
#define _Ke386SetCr(N,X) __asm__ __volatile__("movl %0,%%cr" #N : :"r" (X));
|
||||
#define _Ke386SetDr(N,X) __asm__ __volatile__("movl %0,%%dr" #N : :"r" (X));
|
||||
#define Ke386SetTr(X) __asm__ __volatile__("ltr %%ax" : :"a" (X));
|
||||
|
||||
#define Ke386GetTr(X) __asm__ __volatile__("str %%ax" : :"a" (X));
|
||||
|
||||
#define _Ke386SetSeg(N,X) __asm__ __volatile__("movl %0,%%" #N : :"r" (X));
|
||||
|
||||
#define Ke386GetCr0() _Ke386GetCr(0)
|
||||
#define Ke386SetCr0(X) _Ke386SetCr(0,X)
|
||||
#define Ke386GetCr2() _Ke386GetCr(2)
|
||||
#define Ke386SetCr2(X) _Ke386SetCr(2,X)
|
||||
#define Ke386GetCr4() _Ke386GetCr(4)
|
||||
#define Ke386SetCr4(X) _Ke386SetCr(4,X)
|
||||
#define Ke386GetSs() _Ke386GetSeg(ss)
|
||||
#define Ke386GetFs() _Ke386GetSeg(fs)
|
||||
#define Ke386SetFs(X) _Ke386SetSeg(fs, X)
|
||||
#define Ke386SetDs(X) _Ke386SetSeg(ds, X)
|
||||
#define Ke386SetEs(X) _Ke386SetSeg(es, X)
|
||||
|
||||
static inline LONG Ke386TestAndClearBit(ULONG BitPos, volatile PULONG Addr)
|
||||
{
|
||||
LONG OldBit;
|
||||
|
||||
__asm__ __volatile__(LOCK
|
||||
"btrl %2,%1\n\t"
|
||||
"sbbl %0,%0\n\t"
|
||||
:"=r" (OldBit),"=m" (*Addr)
|
||||
:"Ir" (BitPos)
|
||||
: "memory");
|
||||
return OldBit;
|
||||
}
|
||||
|
||||
static inline LONG Ke386TestAndSetBit(ULONG BitPos, volatile PULONG Addr)
|
||||
{
|
||||
LONG OldBit;
|
||||
|
||||
__asm__ __volatile__(LOCK
|
||||
"btsl %2,%1\n\t"
|
||||
"sbbl %0,%0\n\t"
|
||||
:"=r" (OldBit),"=m" (*Addr)
|
||||
:"Ir" (BitPos)
|
||||
: "memory");
|
||||
return OldBit;
|
||||
}
|
||||
|
||||
|
||||
static inline void Ki386Cpuid(ULONG Op, PULONG Eax, PULONG Ebx, PULONG Ecx, PULONG Edx)
|
||||
{
|
||||
__asm__("cpuid"
|
||||
: "=a" (*Eax), "=b" (*Ebx), "=c" (*Ecx), "=d" (*Edx)
|
||||
: "0" (Op));
|
||||
}
|
||||
|
||||
#define Ke386Rdmsr(msr,val1,val2) __asm__ __volatile__("rdmsr" : "=a" (val1), "=d" (val2) : "c" (msr))
|
||||
|
||||
#define Ke386Wrmsr(msr,val1,val2) __asm__ __volatile__("wrmsr" : /* no outputs */ : "c" (msr), "a" (val1), "d" (val2))
|
||||
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
#define Ke386DisableInterrupts() _cli()
|
||||
#define Ke386EnableInterrupts() _sti()
|
||||
#define Ke386HaltProcessor() __asm hlt
|
||||
#define Ke386GetPageTableDirectory(X) \
|
||||
__asm mov eax, cr3; \
|
||||
__asm mov X, eax;
|
||||
static __forceinline void Ke386SetPageTableDirectory(ULONG X)
|
||||
{
|
||||
__asm mov eax, X
|
||||
__asm mov cr3, eax
|
||||
}
|
||||
#else
|
||||
#error Unknown compiler for inline assembler
|
||||
#endif
|
||||
|
||||
FORCEINLINE
|
||||
struct _KPCR *
|
||||
KeGetCurrentKPCR(VOID)
|
||||
{
|
||||
ULONG Value;
|
||||
#if defined(__GNUC__)
|
||||
__asm__ __volatile__ ("movl %%fs:0x1C, %0\n\t"
|
||||
: "=r" (Value)
|
||||
: /* no inputs */
|
||||
);
|
||||
#elif defined(_MSC_VER)
|
||||
__asm mov eax, fs:[1Ch]
|
||||
__asm mov [Value], eax
|
||||
#endif
|
||||
return (struct _KPCR *) Value;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
FORCEINLINE
|
||||
struct _KPRCB *
|
||||
KeGetCurrentPrcb(VOID)
|
||||
{
|
||||
ULONG Value;
|
||||
#if defined(__GNUC__)
|
||||
__asm__ __volatile__ ("movl %%fs:0x20, %0\n\t"
|
||||
: "=r" (Value)
|
||||
: /* no inputs */
|
||||
);
|
||||
#elif defined(_MSC_VER)
|
||||
__asm mov eax, fs:[20h]
|
||||
__asm mov [Value], eax
|
||||
#endif
|
||||
return (struct _KPRCB *) Value;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif /* __NTOSKRNL_INCLUDE_INTERNAL_I386_KE_H */
|
||||
|
||||
|
|
|
@ -22,31 +22,6 @@
|
|||
#define PA_SYSTEM (0)
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
|
||||
#define FLUSH_TLB { \
|
||||
unsigned int tmp; \
|
||||
__asm__ __volatile__( \
|
||||
"movl %%cr3,%0\n\t" \
|
||||
"movl %0,%%cr3\n\t" \
|
||||
: "=r" (tmp) \
|
||||
:: "memory"); \
|
||||
}
|
||||
|
||||
#define FLUSH_TLB_ONE(addr) __asm__ __volatile__( \
|
||||
"invlpg %0" \
|
||||
: \
|
||||
: "m" (*(volatile long *) (addr)))
|
||||
|
||||
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
/* TODO: Need some way to tell the compiler this is a memory barrier. */
|
||||
#define FLUSH_TLB __asm mov eax, cr3 __asm mov cr3, eax;
|
||||
#else
|
||||
#error Unknown compiler for inline assembler
|
||||
#endif
|
||||
|
||||
struct _EPROCESS;
|
||||
PULONG MmGetPageDirectory(VOID);
|
||||
|
||||
|
|
|
@ -11,16 +11,16 @@
|
|||
//
|
||||
#define KeEnterGuardedRegion() \
|
||||
{ \
|
||||
PKTHREAD Thread = KeGetCurrentThread(); \
|
||||
PKTHREAD _Thread = KeGetCurrentThread(); \
|
||||
\
|
||||
/* Sanity checks */ \
|
||||
ASSERT_IRQL_LESS_OR_EQUAL(APC_LEVEL); \
|
||||
ASSERT(Thread == KeGetCurrentThread()); \
|
||||
ASSERT((Thread->SpecialApcDisable <= 0) && \
|
||||
(Thread->SpecialApcDisable != -32768)); \
|
||||
ASSERT(_Thread == KeGetCurrentThread()); \
|
||||
ASSERT((_Thread->SpecialApcDisable <= 0) && \
|
||||
(_Thread->SpecialApcDisable != -32768)); \
|
||||
\
|
||||
/* Disable Special APCs */ \
|
||||
Thread->SpecialApcDisable--; \
|
||||
_Thread->SpecialApcDisable--; \
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -28,18 +28,18 @@
|
|||
//
|
||||
#define KeLeaveGuardedRegion() \
|
||||
{ \
|
||||
PKTHREAD Thread = KeGetCurrentThread(); \
|
||||
PKTHREAD _Thread = KeGetCurrentThread(); \
|
||||
\
|
||||
/* Sanity checks */ \
|
||||
ASSERT_IRQL_LESS_OR_EQUAL(APC_LEVEL); \
|
||||
ASSERT(Thread == KeGetCurrentThread()); \
|
||||
ASSERT(Thread->SpecialApcDisable < 0); \
|
||||
ASSERT(_Thread == KeGetCurrentThread()); \
|
||||
ASSERT(_Thread->SpecialApcDisable < 0); \
|
||||
\
|
||||
/* Leave region and check if APCs are OK now */ \
|
||||
if (!(++Thread->SpecialApcDisable)) \
|
||||
if (!(++_Thread->SpecialApcDisable)) \
|
||||
{ \
|
||||
/* Check for Kernel APCs on the list */ \
|
||||
if (!IsListEmpty(&Thread->ApcState. \
|
||||
if (!IsListEmpty(&_Thread->ApcState. \
|
||||
ApcListHead[KernelMode])) \
|
||||
{ \
|
||||
/* Check for APC Delivery */ \
|
||||
|
@ -684,11 +684,7 @@ KiRundownThread(IN PKTHREAD Thread)
|
|||
{
|
||||
/* Clear it */
|
||||
KeGetCurrentPrcb()->NpxThread = NULL;
|
||||
#ifdef __GNUC__
|
||||
__asm__("fninit\n\t");
|
||||
#else
|
||||
__asm fninit;
|
||||
#endif
|
||||
Ke386FnInit();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#undef PsGetCurrentProcess
|
||||
#define PsGetCurrentProcess _PsGetCurrentProcess
|
||||
|
||||
#include "i386/intrin_i.h"
|
||||
#include "ke.h"
|
||||
#include "i386/mm.h"
|
||||
#include "i386/fpu.h"
|
||||
|
|
|
@ -46,7 +46,8 @@ IopCheckVpbMounted(IN POPEN_PACKET OpenPacket,
|
|||
|
||||
/* Set VPB mount settings */
|
||||
Raw = !RemainingName->Length && !OpenPacket->RelatedFileObject;
|
||||
Alertable = (OpenPacket->CreateOptions & FILE_SYNCHRONOUS_IO_ALERT);
|
||||
Alertable = (OpenPacket->CreateOptions & FILE_SYNCHRONOUS_IO_ALERT) ?
|
||||
TRUE: FALSE;
|
||||
|
||||
/* Start looping until the VPB is mounted */
|
||||
while (!(DeviceObject->Vpb->Flags & VPB_MOUNTED))
|
||||
|
@ -193,7 +194,7 @@ IopInitializeVpbForMount(IN PDEVICE_OBJECT DeviceObject,
|
|||
Vpb = DeviceObject->Vpb;
|
||||
|
||||
/* Set the VPB as mounted and possibly raw */
|
||||
Vpb->Flags |= VPB_MOUNTED | Raw ? VPB_RAW_MOUNT : 0;
|
||||
Vpb->Flags |= VPB_MOUNTED | (Raw ? VPB_RAW_MOUNT : 0);
|
||||
|
||||
/* Set the stack size */
|
||||
Vpb->DeviceObject->StackSize = AttachedDeviceObject->StackSize;
|
||||
|
@ -477,7 +478,7 @@ IopMountVolume(IN PDEVICE_OBJECT DeviceObject,
|
|||
|
||||
/* Allocate the IRP */
|
||||
Irp = IoAllocateIrp(AttachedDeviceObject->StackSize +
|
||||
FsStackOverhead,
|
||||
(UCHAR)FsStackOverhead,
|
||||
TRUE);
|
||||
if (!Irp)
|
||||
{
|
||||
|
|
|
@ -2716,7 +2716,7 @@ IopEnumerateDetectedDevices(
|
|||
InitializeObjectAttributes(&ObjectAttributes, &DeviceName, OBJ_KERNEL_HANDLE, hDevicesKey, NULL);
|
||||
Status = ZwOpenKey(
|
||||
&hDeviceKey,
|
||||
KEY_QUERY_VALUE + EnumerateSubKeys ? KEY_ENUMERATE_SUB_KEYS : 0,
|
||||
KEY_QUERY_VALUE + (EnumerateSubKeys ? KEY_ENUMERATE_SUB_KEYS : 0),
|
||||
&ObjectAttributes);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
|
|
|
@ -27,19 +27,9 @@ KdpBochsDebugPrint(IN PCH Message,
|
|||
{
|
||||
if (*Message == '\n')
|
||||
{
|
||||
#if defined(_M_IX86) && defined(__GNUC__)
|
||||
/* Don't use WRITE_PORT_UCHAR because hal isn't initialized yet in the very early boot phase. */
|
||||
__asm__("outb %b0, %w1\n\t" :: "a" ('\r'), "d" (BOCHS_LOGGER_PORT));
|
||||
#else
|
||||
WRITE_PORT_UCHAR((PUCHAR)BOCHS_LOGGER_PORT, '\r');
|
||||
#endif
|
||||
__outbyte(BOCHS_LOGGER_PORT, '\r');
|
||||
}
|
||||
#if defined(_M_IX86) && defined(__GNUC__)
|
||||
/* Don't use WRITE_PORT_UCHAR because hal isn't initialized yet in the very early boot phase. */
|
||||
__asm__("outb %b0, %w1\n\t" :: "a" (*Message), "d" (BOCHS_LOGGER_PORT));
|
||||
#else
|
||||
WRITE_PORT_UCHAR((PUCHAR)BOCHS_LOGGER_PORT, *Message);
|
||||
#endif
|
||||
__outbyte(BOCHS_LOGGER_PORT, *Message);
|
||||
Message++;
|
||||
}
|
||||
}
|
||||
|
@ -54,11 +44,7 @@ KdpBochsInit(PKD_DISPATCH_TABLE DispatchTable,
|
|||
|
||||
if (BootPhase == 0)
|
||||
{
|
||||
#if defined(_M_IX86) && defined(__GNUC__)
|
||||
__asm__("inb %w1, %b0\n\t" : "=a" (Value) : "d" (BOCHS_LOGGER_PORT));
|
||||
#else
|
||||
Value = READ_PORT_UCHAR((PUCHAR)BOCHS_LOGGER_PORT);
|
||||
#endif
|
||||
Value = __inbyte(BOCHS_LOGGER_PORT);
|
||||
if (Value != BOCHS_LOGGER_PORT)
|
||||
{
|
||||
KdpDebugMode.Bochs = FALSE;
|
||||
|
|
|
@ -1331,14 +1331,7 @@ KdpGdbEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord,
|
|||
DPRINT("Thread %p acquired mutex\n", PsGetCurrentThread());
|
||||
|
||||
/* Disable hardware debugging while we are inside the stub */
|
||||
#if defined(__GNUC__)
|
||||
__asm__("movl %0,%%db7" : /* no output */ : "r" (0));
|
||||
#elif defined(_MSC_VER)
|
||||
__asm mov eax, 0 __asm mov dr7, eax
|
||||
#else
|
||||
#error Unknown compiler for inline assembler
|
||||
#endif
|
||||
|
||||
_Ke386SetDr(7, 0);
|
||||
GspUnloadBreakpoints(TrapFrame);
|
||||
|
||||
/* Make sure we're debugging the current thread. */
|
||||
|
|
|
@ -1502,7 +1502,7 @@ KdbEnterDebuggerException(
|
|||
|
||||
/* Enter critical section */
|
||||
Ke386SaveFlags(OldEflags);
|
||||
Ke386DisableInterrupts();
|
||||
_disable();
|
||||
|
||||
/* Exception inside the debugger? Game over. */
|
||||
if (InterlockedIncrement(&KdbEntryCount) > 1)
|
||||
|
|
|
@ -1493,7 +1493,7 @@ KdbpCmdGdtLdtIdt(ULONG Argc, PCHAR Argv[])
|
|||
STATIC BOOLEAN
|
||||
KdbpCmdPcr(ULONG Argc, PCHAR Argv[])
|
||||
{
|
||||
PKIPCR Pcr = (PKIPCR)KeGetCurrentKPCR();
|
||||
PKIPCR Pcr = (PKIPCR)KeGetPcr();
|
||||
|
||||
KdbpPrint("Current PCR is at 0x%08x.\n", (INT)Pcr);
|
||||
KdbpPrint(" Tib.ExceptionList: 0x%08x\n"
|
||||
|
@ -1538,7 +1538,7 @@ KdbpCmdPcr(ULONG Argc, PCHAR Argv[])
|
|||
STATIC BOOLEAN
|
||||
KdbpCmdTss(ULONG Argc, PCHAR Argv[])
|
||||
{
|
||||
KTSS *Tss = KeGetCurrentKPCR()->TSS;
|
||||
KTSS *Tss = KeGetPcr()->TSS;
|
||||
|
||||
KdbpPrint("Current TSS is at 0x%08x.\n", (INT)Tss);
|
||||
KdbpPrint(" Eip: 0x%08x\n"
|
||||
|
@ -2440,7 +2440,7 @@ KdbpCliInit()
|
|||
|
||||
/* Enter critical section */
|
||||
Ke386SaveFlags(OldEflags);
|
||||
Ke386DisableInterrupts();
|
||||
_disable();
|
||||
|
||||
/* Interpret the init file... */
|
||||
KdbInitFileBuffer = FileBuffer;
|
||||
|
|
|
@ -183,7 +183,7 @@ KiRetireDpcList(IN PKPRCB Prcb)
|
|||
KefReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);
|
||||
|
||||
/* Re-enable interrupts */
|
||||
Ke386EnableInterrupts();
|
||||
_enable();
|
||||
|
||||
/* Call the DPC */
|
||||
DeferredRoutine(Dpc,
|
||||
|
@ -193,7 +193,7 @@ KiRetireDpcList(IN PKPRCB Prcb)
|
|||
ASSERT_IRQL(DISPATCH_LEVEL);
|
||||
|
||||
/* Disable interrupts and keep looping */
|
||||
Ke386DisableInterrupts();
|
||||
_disable();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -424,7 +424,7 @@ KeRemoveQueueDpc(IN PKDPC Dpc)
|
|||
ASSERT_DPC(Dpc);
|
||||
|
||||
/* Disable interrupts */
|
||||
Ke386DisableInterrupts();
|
||||
_disable();
|
||||
|
||||
/* Get DPC data and type */
|
||||
DpcType = Dpc->Type;
|
||||
|
@ -448,7 +448,7 @@ KeRemoveQueueDpc(IN PKDPC Dpc)
|
|||
}
|
||||
|
||||
/* Re-enable interrupts */
|
||||
Ke386EnableInterrupts();
|
||||
_enable();
|
||||
|
||||
/* Return if the DPC was in the queue or not */
|
||||
return DpcData ? TRUE : FALSE;
|
||||
|
|
|
@ -39,7 +39,7 @@ KeInitExceptions(VOID)
|
|||
{
|
||||
ULONG i;
|
||||
USHORT FlippedSelector;
|
||||
extern KIDTENTRY KiIdt[];
|
||||
extern KIDTENTRY KiIdt[MAXIMUM_IDTVECTOR];
|
||||
|
||||
/* Loop the IDT */
|
||||
for (i = 0; i <= MAXIMUM_IDTVECTOR; i ++)
|
||||
|
|
|
@ -33,7 +33,7 @@ KeSetBaseGdtSelector(ULONG Entry,
|
|||
|
||||
KeAcquireSpinLock(&GdtLock, &oldIrql);
|
||||
|
||||
Gdt = KeGetCurrentKPCR()->GDT;
|
||||
Gdt = KeGetPcr()->GDT;
|
||||
Entry = (Entry & (~0x3)) / 2;
|
||||
|
||||
Gdt[Entry + 1] = (USHORT)(((ULONG)Base) & 0xffff);
|
||||
|
@ -68,7 +68,7 @@ KeSetGdtSelector(ULONG Entry,
|
|||
|
||||
KeAcquireSpinLock(&GdtLock, &oldIrql);
|
||||
|
||||
Gdt = (PULONG) KeGetCurrentKPCR()->GDT;
|
||||
Gdt = (PULONG) KeGetPcr()->GDT;
|
||||
Entry = (Entry & (~0x3)) / 4;
|
||||
|
||||
Gdt[Entry] = Value1;
|
||||
|
@ -193,16 +193,7 @@ NtSetLdtEntries (ULONG Selector1,
|
|||
((PULONG) LdtDescriptor)[0],
|
||||
((PULONG) LdtDescriptor)[1]);
|
||||
|
||||
#if defined(__GNUC__)
|
||||
__asm__("lldtw %%ax"
|
||||
: /* no output */
|
||||
: "a" (KGDT_LDT));
|
||||
#elif defined(_MSC_VER)
|
||||
__asm mov ax, KGDT_LDT
|
||||
__asm lldt ax
|
||||
#else
|
||||
#error Unknown compiler for inline assembler
|
||||
#endif
|
||||
Ke386SetLocalDescriptorTable(KGDT_LDT);
|
||||
|
||||
if(LdtBase)
|
||||
{
|
||||
|
|
|
@ -42,8 +42,8 @@ Ki386EnableGlobalPage(IN volatile ULONG_PTR Context)
|
|||
Ke386SetCr4(Cr4 & ~CR4_PGE);
|
||||
|
||||
/* Flush the TLB */
|
||||
Ke386GetPageTableDirectory(Cr3);
|
||||
Ke386SetPageTableDirectory(Cr3);
|
||||
Cr3 = Ke386GetCr3();
|
||||
Ke386SetCr3(Cr3);
|
||||
|
||||
/* Now enable PGE */
|
||||
Ke386SetCr4(Cr4 | CR4_PGE);
|
||||
|
|
|
@ -122,9 +122,7 @@ MiFlushTlb(PULONG Pt, PVOID Address)
|
|||
PULONG
|
||||
MmGetPageDirectory(VOID)
|
||||
{
|
||||
unsigned int page_dir=0;
|
||||
Ke386GetPageTableDirectory(page_dir);
|
||||
return((PULONG)page_dir);
|
||||
return (PULONG)Ke386GetCr3();
|
||||
}
|
||||
|
||||
static ULONG
|
||||
|
|
|
@ -185,26 +185,24 @@ ExAllocatePoolWithQuotaTag (IN POOL_TYPE PoolType,
|
|||
Process = PsGetCurrentProcess();
|
||||
|
||||
/* PsChargePoolQuota returns an exception, so this needs SEH */
|
||||
#if defined(__GNUC__)
|
||||
_SEH_FILTER(FreeAndGoOn) {
|
||||
_SEH_FILTER(FreeAndGoOn)
|
||||
{
|
||||
/* Couldn't charge, so free the pool and let the caller SEH manage */
|
||||
ExFreePool(Block);
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
} _SEH_TRY {
|
||||
}
|
||||
|
||||
_SEH_TRY
|
||||
{
|
||||
//* FIXME: Is there a way to get the actual Pool size allocated from the pool header? */
|
||||
PsChargePoolQuota(Process, PoolType & PAGED_POOL_MASK, NumberOfBytes);
|
||||
} _SEH_EXCEPT(FreeAndGoOn) {
|
||||
}
|
||||
_SEH_EXCEPT(FreeAndGoOn)
|
||||
{
|
||||
/* Quota Exceeded and the caller had no SEH! */
|
||||
KeBugCheck(STATUS_QUOTA_EXCEEDED);
|
||||
} _SEH_END;
|
||||
#else /* assuming all other Win32 compilers understand SEH */
|
||||
__try {
|
||||
PsChargePoolQuota(Process, PoolType & PAGED_POOL_MASK, NumberOfBytes);
|
||||
}
|
||||
__except (ExFreePool(Block), EXCEPTION_CONTINUE_SEARCH) {
|
||||
KeBugCheck(STATUS_QUOTA_EXCEEDED);
|
||||
}
|
||||
#endif
|
||||
_SEH_END;
|
||||
}
|
||||
|
||||
return Block;
|
||||
|
|
|
@ -263,6 +263,7 @@ MiQueryVirtualMemory (IN HANDLE ProcessHandle,
|
|||
MmUnlockAddressSpace(AddressSpace);
|
||||
if (Address < MmSystemRangeStart)
|
||||
{
|
||||
ASSERT(Process);
|
||||
ObDereferenceObject(Process);
|
||||
}
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ SepInitSecurityIDs(VOID)
|
|||
SeRestrictedSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
|
||||
SeAnonymousLogonSid = ExAllocatePoolWithTag(PagedPool, SidLength1, TAG_SID);
|
||||
|
||||
if (SeNullSid == NULL || SeNullSid == NULL || SeWorldSid == NULL ||
|
||||
if (SeNullSid == NULL || SeWorldSid == NULL ||
|
||||
SeLocalSid == NULL || SeCreatorOwnerSid == NULL ||
|
||||
SeCreatorGroupSid == NULL || SeCreatorOwnerServerSid == NULL ||
|
||||
SeCreatorGroupServerSid == NULL || SeNtAuthoritySid == NULL ||
|
||||
|
|
Loading…
Reference in a new issue