/// <summary> /// Creates a new page directory using only physical memory (used in Init) /// </summary> /// <param name="flags">The flags</param> /// <returns>The page directory</returns> public static PageDirectory *CreateNewDirectoryPhysically(PageFlags flags) { // Allocate a new block of physical memory to store our physical page in PageDirectory *directory = (PageDirectory *)Heap.AlignedAlloc(0x1000, sizeof(PageDirectory)); directory->PhysicalDirectory = directory; if (directory == null) { Panic.DoPanic("directory == null"); } // Allocate the tables for (int i = 0; i < 1024; i++) { PageTable *table = (PageTable *)PhysicalMemoryManager.Alloc(); if (table == null) { Panic.DoPanic("table == null"); } Memory.Memclear(table, sizeof(PageTable)); // Note: At this point, virtual address == physical address due to identity mapping directory->PhysicalTables[i] = (int)table | (int)flags; directory->VirtualTables[i] = (int)table; } return(directory); }
/// <summary> /// Starts a new process based on the given path and arguments /// </summary> /// <param name="path">The path</param> /// <param name="argv">The arguments</param> /// <param name="flags">Spawn flags</param> /// <returns>Errorcode or PID</returns> public static int StartProcess(string path, string[] argv, Task.SpawnFlags flags) { if (argv == null) { Panic.DoPanic("argv == null"); } Node node = VFS.GetByAbsolutePath(path); if (node == null) { return(-(int)ErrorCode.ENOENT); } // Open and create buffer VFS.Open(node, (int)FileMode.O_RDONLY); byte[] buffer = new byte[node.Size]; if (buffer == null) { Heap.Free(node); VFS.Close(node); return(-(int)ErrorCode.ENOMEM); } // Fill buffer contents VFS.Read(node, 0, node.Size, buffer); VFS.Close(node); // Pass execution to ELF loader int status = ELFLoader.Execute(buffer, node.Size, argv, flags); Heap.Free(buffer); Heap.Free(node); return(status); }
/// <summary> /// Wait for drive to be finished /// </summary> /// <param name="port">Port IO base</param> private static void poll(uint port) { wait400ns(port); byte status; do { status = PortIO.In8((ushort)(port + ATA_REG_STATUS)); }while ((status & ATA_STATUS_BSY) > 0); while ((status & ATA_STATUS_DRQ) == 0) { status = PortIO.In8((ushort)(port + ATA_REG_STATUS)); if ((status & ATA_STATUS_DF) > 0) { Panic.DoPanic("Device fault!"); } if ((status & ATA_STATUS_ERR) > 0) { Panic.DoPanic("ERR IN ATA!!"); } } }
/// <summary> /// Do identify and read needed shizzle /// </summary> private void DoIdentify() { void *adr = Heap.Alloc(0x1000); int status = Identify(0, 1, adr); int statusCode = (status >> COMP_STATUS_SC_SHIFT) & COMP_STATUS_SC_MASK; if (statusCode != COMP_STATUS_SC_SUC) { Panic.DoPanic("[NVMe] Couldn't identifiy"); } NVMe_ID_Ctrl *idCtrl = (NVMe_ID_Ctrl *)adr; mFirmwareRevision = new char[8]; mSerialNumber = new char[20]; mModel = new char[40]; nNumNamespaces = (int)idCtrl->Nn; Memory.Memcpy(Util.ObjectToVoidPtr(mFirmwareRevision), idCtrl->FirmwareRevision, 8); Memory.Memcpy(Util.ObjectToVoidPtr(mSerialNumber), idCtrl->SerialNumber, 20); Memory.Memcpy(Util.ObjectToVoidPtr(mModel), idCtrl->ModelNumber, 40); Heap.Free(adr); }
/// <summary> /// Adds an IRQ to the table /// </summary> /// <param name="slot">The slot</param> /// <param name="pin">The pin</param> /// <param name="irq">The IRQ</param> private static void addIRQ(uint slot, uint pin, uint irq, uint polarity, uint trigger) { if (slot >= PCI_BUS_DEV_MAX || pin > PCI_PINS) { Panic.DoPanic("[PCI] Invalid IRQ added"); } PciIRQEntry entry = new PciIRQEntry(); entry.Irq = irq; entry.Flags = 0; if (polarity == 0 || polarity == 3) { entry.Flags |= IOApic.IOAPIC_REDIR_POLARITY_LOW; } else { entry.Flags |= IOApic.IOAPIC_REDIR_POLARITY_HIGH; } if (trigger == 0 || trigger == 3) { entry.Flags |= IOApic.IOAPIC_REDIR_TRIGGER_LEVEL; } else { entry.Flags |= IOApic.IOAPIC_REDIR_TRIGGER_EDGE; } m_irqTable[(slot * PCI_PINS) + pin] = entry; }
/// <summary> /// Temporary kernel memory allocation before a real heap is set up /// </summary> /// <param name="size">The size</param> /// <param name="align">If the block should be aligned</param> /// <returns>A block of memory</returns> public static unsafe void *KAlloc(int size, bool align) { #if HEAP_DEBUG if (useRealHeap) { Panic.DoPanic("[HEAP] KAlloc has been called after real heap started!"); } #endif if (PhysicalMemoryManager.IsInitialized) { return(PhysicalMemoryManager.AllocRange(size)); } else { uint address = (uint)CurrentEnd; if (align) { address = Paging.AlignUp(address); } // At least 4byte align if ((address & 3) != 0) { address &= 3; address += 4; } CurrentEnd = (void *)(address + size); return((void *)address); } }
/// <summary> /// Translate ISA IRQ to a redirection entry /// </summary> /// <param name="irq">The ISA IRQ number</param> /// <returns>The override</returns> public static ISAOverride GetISARedirection(uint irq) { if (irq >= 16) { Panic.DoPanic("Requested an ISA redirection for IRQ >= 16"); } return(m_intSourceOverrides[irq]); }
/// <summary> /// Create queues (for now just 1..) /// </summary> private void CreateQueues() { var queue = CreateQueue(1); if (queue == null) { Panic.DoPanic("[NVMe] Couldn't make queue"); } mIOQueue = queue; }
/// <summary> /// Sets the IRQ routing /// </summary> private static void setIRQRouting() { m_irqTable = new PciIRQEntry[PCI_BUS_DEV_MAX * PCI_PINS]; // Find root PCI device and handle it int status = Acpica.AcpiGetDevices("PNP0A03", onRootDevice, null, null); if (status != Acpica.AE_OK) { Panic.DoPanic("[PCI] Couldn't get root device"); } }
/// <summary> /// Gets the block from the pointer /// </summary> /// <param name="ptr">The pointer</param> /// <returns>The block</returns> private static unsafe Block *getBlockFromPtr(void *ptr) { Block *block = (Block *)((int)ptr - sizeof(Block)); #if HEAP_USE_MAGIC if (block->Magic != HEAP_MAGIC) { Panic.DoPanic("[HEAP] block->Magic != HEAP_MAGIC"); } #endif return(block); }
/// <summary> /// Called as callback when a root device is found /// </summary> /// <param name="Object">The object</param> /// <param name="NestingLevel">Nesting level</param> /// <param name="Context">User context</param> /// <param name="ReturnValue">Return value if early returned</param> /// <returns>Status</returns> private static int onRootDevice(void *Object, uint NestingLevel, void *Context, void **ReturnValue) { // Get routing table AcpiObjects.Buffer buffer; buffer.Length = Acpica.ACPI_ALLOCATE_BUFFER; int status = Acpica.AcpiGetIrqRoutingTable(Object, &buffer); if (status != Acpica.AE_OK) { Panic.DoPanic("[PCI] Couldn't get ACPI IRQ Routing Table"); } // The last entry will have Length 0 Acpica.PCIRoutingTable *table = (Acpica.PCIRoutingTable *)buffer.Pointer; while (table->Length > 0) { // No reference source if (table->Source[0] == 0) { uint slot = (uint)(table->Address >> 16); addIRQ(slot, table->Pin, table->SourceIndex, 0, 0); } // Source is not null, that means we need to lookup the reference resource else { void *handle = null; status = Acpica.AcpiGetHandle(Object, Util.CharPtrToString(table->Source), &handle); if (status != Acpica.AE_OK) { Panic.DoPanic("[PCI] Couldn't load references handle"); } status = Acpica.AcpiWalkResources(handle, Acpica.METHOD_NAME__CRS, onIRQResource, table); if (status != Acpica.AE_OK) { Panic.DoPanic("[PCI] Couldn't process resources for IRQ"); } } // Next entry table = (Acpica.PCIRoutingTable *)((byte *)table + table->Length); } // The object returned should be freed by us Acpica.AcpiOsFree(buffer.Pointer); return(Acpica.AE_OK); }
/// <summary> /// Creates a block descriptor with given minimal size /// </summary> /// <param name="size">The minimal size</param> /// <returns>The block descriptor</returns> private static unsafe BlockDescriptor *createBlockDescriptor(int size) { #if HEAP_DEBUG_DESCRIPTOR Console.WriteLine("[HEAP] Creating a new block descriptor"); #endif // Add size of Block and BlockDescriptor size += sizeof(Block) + sizeof(BlockDescriptor); // Allocate descriptor size = getRequiredPageCount(size) * 0x1000; BlockDescriptor *descriptor = (BlockDescriptor *)Paging.AllocateVirtual(size); #if HEAP_DEBUG_DESCRIPTOR Console.Write("[HEAP] New descriptor is at 0x"); Console.WriteHex((int)descriptor); Console.Write(", physical: 0x"); Console.WriteHex((int)Paging.GetPhysicalFromVirtual(descriptor)); Console.Write('\n'); #endif if (descriptor == null) { Panic.DoPanic("descriptor == null"); } // Setup block Block *first = (Block *)((int)descriptor + sizeof(BlockDescriptor)); first->Prev = null; first->Next = null; first->Size = size - sizeof(BlockDescriptor); first->Used = false; first->Descriptor = descriptor; #if HEAP_USE_MAGIC first->Magic = HEAP_MAGIC; #endif // Setup descriptor descriptor->FreeSpace = size - sizeof(BlockDescriptor); descriptor->First = first; descriptor->FirstFree = first; descriptor->Next = null; #if HEAP_USE_MAGIC descriptor->Magic = HEAP_MAGIC; #endif return(descriptor); }
/// <summary> /// ISR handler /// </summary> /// <param name="regsPtr">Pointer to registers</param> public static unsafe void Handler(Regs *regsPtr) { int isrNum = regsPtr->IntNum; // If the kernel caused this, do a panic if (!Tasking.IsActive || Tasking.CurrentTask.PID == 0) { Panic.DoPanic(errorCodes[isrNum], regsPtr); } // Otherwise send a signal else { #if ALWAYS_DO_PANIC Panic.DoPanic(errorCodes[isrNum], regsPtr); #else Tasking.CurrentTask.ProcessSignal(isrToSignal[isrNum]); #endif } }
/// <summary> /// Frees a page directory /// </summary> /// <param name="directory">The directory</param> public static void FreeDirectory(PageDirectory *directory) { if (directory == CurrentDirectory) { Panic.DoPanic("Tried to free the current page directory"); } if (directory == KernelDirectory) { Panic.DoPanic("Tried to free the kernel page directory"); } // The directory was cloned, so it was allocated in one huge block PageDirectory *virt = CurrentDirectory; PageDirectory *phys = CurrentDirectoryPhysical; SetPageDirectory(KernelDirectory, KernelDirectory); Heap.Free(directory); SetPageDirectory(virt, phys); }
/// <summary> /// Gets the physical address from a virtual address /// </summary> /// <param name="pageDir">The page directory to look into</param> /// <param name="virt">The virtual address</param> /// <returns>The physical address</returns> public static void *GetPhysicalFromVirtual(PageDirectory *pageDir, void *virt) { // Get indices int address = (int)virt; int remaining = address % 0x1000; int frame = address / 0x1000; // Find corresponding table to the virtual address PageTable *table = (PageTable *)pageDir->VirtualTables[frame / 1024]; if (table == null) { Panic.DoPanic("table == null"); } // Calculate page int page = table->Pages[frame & (1024 - 1)]; return((void *)(GetFrameAddress(page) + remaining)); }
/// <summary> /// Clones a page directory and its tables /// </summary> /// <param name="source">The source page directory</param> /// <returns>The cloned page directory</returns> public static PageDirectory *CloneDirectory(PageDirectory *source) { // Note: sizeof(PageDirectory) is not neccesarily a page int pageDirSizeAligned = (int)AlignUp((uint)sizeof(PageDirectory)); // One block for the page directory and the page tables int allocated = (int)Heap.AlignedAlloc(0x1000, pageDirSizeAligned + 1024 * sizeof(PageTable)); if (allocated == 0) { Panic.DoPanic("Couldn't clone page directory because there is no memory left"); } PageDirectory *destination = (PageDirectory *)allocated; destination->PhysicalDirectory = (PageDirectory *)GetPhysicalFromVirtual((void *)allocated); for (int i = 0; i < 1024; i++) { int sourceTable = source->VirtualTables[i]; if (sourceTable == 0) { Panic.DoPanic("sourceTable == 0?!"); } // Get the pointer without the flags and the flags seperately PageTable *sourceTablePtr = (PageTable *)sourceTable; int flags = source->PhysicalTables[i] & 0xFFF; // Calculate addresses int addressOffset = pageDirSizeAligned + i * sizeof(PageTable); PageTable *newTable = (PageTable *)(allocated + addressOffset); int newTablePhysical = (int)GetPhysicalFromVirtual(newTable); // Copy table data and set pointers Memory.Memcpy(newTable, sourceTablePtr, sizeof(PageTable)); destination->PhysicalTables[i] = newTablePhysical | flags; destination->VirtualTables[i] = (int)newTable; } return(destination); }
/// <summary> /// Read version /// </summary> private void ReadVersion() { uint version = mRegs->Version; if (version == 0x00010200) { mVersion = 102; } else if (version == 0x00010100) { mVersion = 101; } else if (version == 0x00010000) { mVersion = 100; } else { Panic.DoPanic("[NVMe] Wrong NVMe version"); } }
/// <summary> /// Gets a sufficient block descriptor for the required size /// </summary> /// <param name="size">The required size</param> /// <returns>The block descriptor</returns> private static unsafe BlockDescriptor *getSufficientDescriptor(BlockDescriptor *first, int size) { BlockDescriptor *descriptor = first; if (descriptor == null) { descriptor = firstDescriptor; } // Search for a big enough descriptor while (true) { #if HEAP_USE_MAGIC if (descriptor->Magic != HEAP_MAGIC) { Panic.DoPanic("descriptor->magic != HEAP_MAGIC"); } #endif if (descriptor != first && descriptor->FreeSpace >= size) { return(descriptor); } if (descriptor->Next == null) { break; } descriptor = descriptor->Next; } // Create next descriptor because there is no descriptor that is big enough BlockDescriptor *newDescriptor = createBlockDescriptor(size); descriptor->Next = newDescriptor; return(newDescriptor); }
/// <summary> /// Enable NVMe controller /// </summary> private void EnableController() { mRegs->ControllerConfig = (0) << CC_MPS_SHIFT; // 4KB mRegs->ControllerConfig |= CC_AMS_RR | CC_SHN_NO; // Round robin, no shutdown notifcation mRegs->ControllerConfig |= (6 << CC_IOSQES_SHIFT) | (4 << CC_IOCQES_SHIFT); // Setr Queue sizes mRegs->ControllerConfig |= CC_ENABLE | CC_CSS_NVM; // Enable and NVM // Wait till device is ready uint status = mRegs->ControllerStatus; int timeOut = 0; while ((status & CSTS_RDY) == 0) { Tasking.CurrentTask.CurrentThread.Sleep(0, 100); timeOut += 100; if (timeOut >= mTimeout) { Panic.DoPanic("[NVMe] Could not enable controller in time"); } status = mRegs->ControllerStatus; } }
/// <summary> /// Initializes ACPI /// </summary> public static void Init() { int status = Acpica.AcpiInitializeSubsystem(); if (status != Acpica.AE_OK) { Panic.DoPanic("[ACPI] Couldn't initialize ACPICA subsystem"); } status = Acpica.AcpiInitializeTables(null, 16, false); if (status != Acpica.AE_OK) { Panic.DoPanic("[ACPI] Couldn't initialize tables"); } status = Acpica.AcpiInstallAddressSpaceHandler((void *)Acpica.ACPI_ROOT_OBJECT, Acpica.ACPI_ADR_SPACE_SYSTEM_MEMORY, (void *)Acpica.ACPI_DEFAULT_HANDLER, null, null); if (status != Acpica.AE_OK) { Panic.DoPanic("[ACPI] Could not install address space handler for system memory"); } status = Acpica.AcpiInstallAddressSpaceHandler((void *)Acpica.ACPI_ROOT_OBJECT, Acpica.ACPI_ADR_SPACE_SYSTEM_IO, (void *)Acpica.ACPI_DEFAULT_HANDLER, null, null); if (status != Acpica.AE_OK) { Panic.DoPanic("[ACPI] Could not install address space handler for system IO"); } status = Acpica.AcpiInstallAddressSpaceHandler((void *)Acpica.ACPI_ROOT_OBJECT, Acpica.ACPI_ADR_SPACE_PCI_CONFIG, (void *)Acpica.ACPI_DEFAULT_HANDLER, null, null); if (status != Acpica.AE_OK) { Panic.DoPanic("[ACPI] Could not install address space handler for PCI"); } status = Acpica.AcpiLoadTables(); if (status != Acpica.AE_OK) { Panic.DoPanic("[ACPI] Couldn't load tables"); } status = Acpica.AcpiEnableSubsystem(Acpica.ACPI_FULL_INITIALIZATION); if (status != Acpica.AE_OK) { Panic.DoPanic("[ACPI] Couldn't enable subsystems:"); } status = Acpica.AcpiInitializeObjects(Acpica.ACPI_FULL_INITIALIZATION); if (status != Acpica.AE_OK) { Panic.DoPanic("[ACPI] Couldn't initialize objects"); } // MADT table contains info about APIC, processors, ... Acpica.TableHeader *table; status = Acpica.AcpiGetTable("APIC", 1, &table); if (status != Acpica.AE_OK) { Panic.DoPanic("[ACPI] Couldn't find MADT table"); } parseMADT((MADT *)table); // Set ACPI to APIC using the "_PIC" method, note that this method is not always present // so we "can" ignore the AE_NOT_FOUND error AcpiObjects.IntegerObject arg1; arg1.Type = AcpiObjects.ObjectType.Integer; arg1.Value = 1; AcpiObjects.ObjectList args; args.Count = 1; args.Pointer = &arg1; status = Acpica.AcpiEvaluateObject((void *)Acpica.ACPI_ROOT_OBJECT, "_PIC", &args, null); if (status != Acpica.AE_NOT_FOUND && status != Acpica.AE_OK) { Panic.DoPanic("[ACPI] Couldn't call _PIC method"); } // We are done here Console.WriteLine("[ACPI] Initialized"); }
/// <summary> /// Allocates an aligned piece of memory /// </summary> /// <param name="alignment">The alignment</param> /// <param name="size">The size</param> public static unsafe void *AlignedAlloc(int alignment, int size) { #if HEAP_DEBUG if (size <= 0) { Panic.DoPanic("[HEAP] size <= 0"); } if (alignment <= 0) { Panic.DoPanic("[HEAP] alignment <= 0"); } #endif if (useRealHeap) { // Find a descriptor that is big enough to hold the block header and its data // We need to look for something that can hold an aligned size if alignment is requested size += sizeof(Block); // Safe size if (size % alignment != 0) { size = size - (size % alignment); size += alignment; } Block * currentBlock = null; Block * previousBlock = null; BlockDescriptor *descriptor = getSufficientDescriptor(null, size); if (descriptor == null) { Panic.DoPanic("[HEAP] descriptor == null"); } retry: currentBlock = descriptor->FirstFree; previousBlock = null; // Lock descriptor Mutex.InternalLock(&descriptor->Lock); // Search in the descriptor while (true) { // Can fit in here if (currentBlock->Used || currentBlock->Size < size) { goto nextBlock; } #if HEAP_USE_MAGIC if (currentBlock->Magic != HEAP_MAGIC) { Panic.DoPanic("currentBlock->Magic != HEAP_MAGIC"); } #endif // Check if this block data would be aligned int currentData = (int)currentBlock + sizeof(Block); int remainder = currentData % alignment; // Not aligned if (remainder != 0) { // Split the current block into two // The first part is a padding // The second part is the block we want for allocation // This only happens if there is enough space left // Size of gap int gapSize = alignment - remainder; int newSize = currentBlock->Size - gapSize; // Would the new block be too small to fit our data into? if (newSize < size) { goto nextBlock; } // Store old data Block *newNext = currentBlock->Next; bool newUsed = currentBlock->Used; // Move block forward currentBlock = (Block *)((int)currentBlock + gapSize); currentBlock->Used = newUsed; currentBlock->Prev = previousBlock; currentBlock->Next = newNext; currentBlock->Size = newSize; currentBlock->Descriptor = descriptor; // Increase size of previous block if needed if (previousBlock != null) { previousBlock->Next = currentBlock; previousBlock->Size += gapSize; // If the block is used and the gap is merged // that means that the total free space in this descriptor decreases if (currentBlock->Used) { descriptor->FreeSpace -= gapSize; } } // There's space left to move this block and let the first block point to the new one else if (gapSize >= sizeof(Block)) { // Update first block Block *first = descriptor->First; descriptor->FreeSpace -= gapSize; first->Used = false; first->Prev = null; first->Next = currentBlock; first->Size = gapSize; first->Descriptor = descriptor; #if HEAP_USE_MAGIC first->Magic = HEAP_MAGIC; #endif currentBlock->Prev = first; } // This is the first block that was moved else { // Update header descriptor->First = currentBlock; descriptor->FirstFree = currentBlock; } } // Calculate leftover part for the next block int leftover = currentBlock->Size - size; // Update header currentBlock->Used = true; currentBlock->Descriptor = descriptor; currentBlock->Prev = previousBlock; #if HEAP_USE_MAGIC currentBlock->Magic = HEAP_MAGIC; #endif // Update descriptor descriptor->FreeSpace -= size; // If we have something left over, create a new block if (leftover > sizeof(Block) + 4) { // Update header Block *afterBlock = (Block *)((int)currentBlock + size); afterBlock->Size = leftover; afterBlock->Used = false; afterBlock->Next = currentBlock->Next; afterBlock->Prev = currentBlock; afterBlock->Descriptor = descriptor; #if HEAP_USE_MAGIC afterBlock->Magic = HEAP_MAGIC; #endif if (currentBlock->Next != null) { currentBlock->Next->Prev = afterBlock; } currentBlock->Next = afterBlock; currentBlock->Size = size; // Update descriptor if ((int)currentBlock < (int)descriptor->FirstFree) { descriptor->FirstFree = currentBlock; } } // Return block (skip header) Mutex.InternalUnlock(&descriptor->Lock); return((void *)((int)currentBlock + sizeof(Block))); // Next block nextBlock: { previousBlock = currentBlock; currentBlock = currentBlock->Next; Mutex.InternalUnlock(&descriptor->Lock); if (currentBlock == null) { // This was the last block in the descriptor // Due to alignment issues we haven't found a good place // Get another descriptor that has enough free space descriptor = getSufficientDescriptor(descriptor, size); goto retry; } } } } else { if (alignment == 0x1000) { return(KAlloc(size, true)); } else if (alignment == 4) { return(KAlloc(size, false)); } else { Panic.DoPanic("[HEAP] Unsupported alignment in early allocation"); return(null); } } }
/// <summary> /// Frees a piece of memory /// </summary> /// <param name="ptr">The pointer</param> public static unsafe void Free(void *ptr) { #if HEAP_DEBUG if (ptr == null) { Panic.DoPanic("[HEAP] Free(null)"); } #endif Block * block = getBlockFromPtr(ptr); BlockDescriptor *descriptor = block->Descriptor; #if HEAP_DEBUG if (!block->Used) { Panic.DoPanic("[HEAP] Tried to free a block that was already freed"); } #endif Mutex.InternalLock(&descriptor->Lock); // Not used anymore block->Used = false; block->Descriptor->FreeSpace += block->Size; if ((int)block->Descriptor->FirstFree > (int)block) { block->Descriptor->FirstFree = block; } // Merge forward if (block->Next != null && !block->Next->Used) { Block *next = block->Next; block->Size += next->Size; block->Next = next->Next; if ((int)block->Descriptor->FirstFree > (int)next) { block->Descriptor->FirstFree = next; } if (next->Next != null) { next->Next->Prev = block; } } // Merge backwards if (block->Prev != null && !block->Prev->Used) { Block *prev = block->Prev; prev->Size += block->Size; prev->Next = block->Next; if ((int)block->Descriptor->FirstFree > (int)prev) { block->Descriptor->FirstFree = prev; } if (block->Next != null) { block->Next->Prev = prev; } } Mutex.InternalUnlock(&descriptor->Lock); }