/*++ Copyright (c) 2012 Minoca Corp. All Rights Reserved Module Name: init.c Abstract: This module contains code to initialize the I/O subsystem. Author: Evan Green 16-Sep-2012 Environment: Kernel --*/ // // ------------------------------------------------------------------- Includes // #include #include #include #include "iop.h" #include "pagecach.h" // // ---------------------------------------------------------------- Definitions // // // ------------------------------------------------------ Data Type Definitions // /*++ Structure Description: This structure defines the iteration context that initializes the physical address space arbiter. Members: PreviousEnd - Stores the end of the previous descriptor. Status - Stores the final status code. --*/ typedef struct _IO_INIT_PHYSICAL_MAP_ITERATOR { ULONGLONG PreviousEnd; KSTATUS Status; } IO_INIT_PHYSICAL_MAP_ITERATOR, *PIO_INIT_PHYSICAL_MAP_ITERATOR; // // ----------------------------------------------- Internal Function Prototypes // KSTATUS IopInitializeDeviceSupport ( VOID ); KSTATUS IopInitializeResourceAllocation ( PKERNEL_INITIALIZATION_BLOCK Parameters ); KSTATUS IopInitializeBootDrivers ( PKERNEL_INITIALIZATION_BLOCK Parameters ); KSTATUS IopInitializeDeviceDatabase ( PKERNEL_INITIALIZATION_BLOCK Parameters ); KSTATUS IopCreateBootDevices ( PKERNEL_INITIALIZATION_BLOCK Parameters ); VOID IopInitializePhysicalAddressArbiterIterator ( PMEMORY_DESCRIPTOR_LIST DescriptorList, PMEMORY_DESCRIPTOR Descriptor, PVOID Context ); // // -------------------------------------------------------------------- Globals // // // ------------------------------------------------------------------ Functions // KSTATUS IoInitialize ( ULONG Phase, PKERNEL_INITIALIZATION_BLOCK Parameters ) /*++ Routine Description: This routine initializes the I/O subsystem. Arguments: Phase - Supplies the initialization phase. Parameters - Supplies a pointer to the kernel's initialization information. Return Value: Status code. --*/ { PBOOT_ENTRY BootEntry; ULONG Flags; KSTATUS Status; UINTN SystemDirectorySize; INITIALIZE_LIST_HEAD(&IoDeviceList); IoDeviceListLock = KeCreateQueuedLock(); if (IoDeviceListLock == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeEnd; } // // Copy the boot information over. // if (Parameters->BootEntry != NULL) { BootEntry = Parameters->BootEntry; ASSERT((sizeof(IoBootInformation.SystemDiskIdentifier) == sizeof(BootEntry->DiskId)) && (sizeof(IoBootInformation.SystemPartitionIdentifier) == sizeof(BootEntry->PartitionId))); RtlCopyMemory(&(IoBootInformation.SystemDiskIdentifier), &(BootEntry->DiskId), sizeof(IoBootInformation.SystemDiskIdentifier)); RtlCopyMemory(&(IoBootInformation.SystemPartitionIdentifier), &(BootEntry->PartitionId), sizeof(IoBootInformation.SystemPartitionIdentifier)); // // Copy the system directory path. // if (BootEntry->SystemPath != NULL) { SystemDirectorySize = RtlStringLength(BootEntry->SystemPath) + 1; IoSystemDirectoryPath = MmAllocateNonPagedPool(SystemDirectorySize, IO_ALLOCATION_TAG); if (IoSystemDirectoryPath == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeEnd; } RtlStringCopy(IoSystemDirectoryPath, BootEntry->SystemPath, SystemDirectorySize); } } RtlCopyMemory(&(IoBootInformation.BootTime), &(Parameters->BootTime), sizeof(SYSTEM_TIME)); // // Create the Interfaces object directory. // IoInterfaceDirectory = ObCreateObject(ObjectDirectory, NULL, "Interface", sizeof("Interface"), sizeof(OBJECT_HEADER), NULL, OBJECT_FLAG_USE_NAME_DIRECTLY, IO_ALLOCATION_TAG); if (IoInterfaceDirectory == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeEnd; } IoInterfaceLock = KeCreateQueuedLock(); if (IoInterfaceLock == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeEnd; } // // Create the IRP directory. // IoIrpDirectory = ObCreateObject(ObjectDirectory, NULL, "Irp", sizeof("Irp"), sizeof(OBJECT_HEADER), NULL, OBJECT_FLAG_USE_NAME_DIRECTLY, IO_ALLOCATION_TAG); if (IoIrpDirectory == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeEnd; } // // Create the pipe directory. // IoPipeDirectory = ObCreateObject(ObjectDirectory, NULL, "Pipe", sizeof("Pipe"), sizeof(OBJECT_HEADER), NULL, OBJECT_FLAG_USE_NAME_DIRECTLY, FI_ALLOCATION_TAG); if (IoPipeDirectory == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeEnd; } // // Create the shared memory object directory. // IoSharedMemoryObjectDirectory = ObCreateObject( ObjectDirectory, NULL, "SharedMemoryObjects", sizeof("SharedMemoryObjects"), sizeof(OBJECT_HEADER), NULL, OBJECT_FLAG_USE_NAME_DIRECTLY, FI_ALLOCATION_TAG); if (IoSharedMemoryObjectDirectory == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeEnd; } // // Initialize the file system list head and create the lock protecting // access to it. // INITIALIZE_LIST_HEAD(&IoFileSystemList); IoFileSystemListLock = KeCreateQueuedLock(); if (IoFileSystemListLock == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeEnd; } // // Create the volume directory. // Flags = OBJECT_FLAG_USE_NAME_DIRECTLY; IoVolumeDirectory = ObCreateObject(ObjectDirectory, NULL, "Volume", sizeof("Volume"), sizeof(OBJECT_HEADER), NULL, Flags, FI_ALLOCATION_TAG); if (IoVolumeDirectory == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeEnd; } // // Initialize support for device information requests. // Status = IopInitializeDeviceInformationSupport(); if (!KSUCCESS(Status)) { goto InitializeEnd; } // // Initialize support for file objects. // Status = IopInitializeFileObjectSupport(); if (!KSUCCESS(Status)) { goto InitializeEnd; } // // Initialize support for path traversal. // Status = IopInitializePathSupport(); if (!KSUCCESS(Status)) { goto InitializeEnd; } // // Initialize support for mount points. // Status = IopInitializeMountPointSupport(); if (!KSUCCESS(Status)) { goto InitializeEnd; } // // Initialize support for the page cache. // Status = IopInitializePageCache(); if (!KSUCCESS(Status)) { goto InitializeEnd; } // // Initialize support for terminals. // Status = IopInitializeTerminalSupport(); if (!KSUCCESS(Status)) { goto InitializeEnd; } // // Initialize the device database. // Status = IopInitializeDeviceDatabase(Parameters); if (!KSUCCESS(Status)) { goto InitializeEnd; } Status = IopInitializeDeviceSupport(); if (!KSUCCESS(Status)) { goto InitializeEnd; } // // Initialize support for resource allocation. // Status = IopInitializeResourceAllocation(Parameters); if (!KSUCCESS(Status)) { goto InitializeEnd; } // // Load all boot drivers and call their entry routines. // Status = IopInitializeBootDrivers(Parameters); if (!KSUCCESS(Status)) { goto InitializeEnd; } // // Create the base devices described in the device map. // Status = IopCreateBootDevices(Parameters); if (!KSUCCESS(Status)) { goto InitializeEnd; } // // Fire up power management. // Status = PmInitializeLibrary(); if (!KSUCCESS(Status)) { goto InitializeEnd; } InitializeEnd: return Status; } // // --------------------------------------------------------- Internal Functions // KSTATUS IopInitializeDeviceSupport ( VOID ) /*++ Routine Description: This routine initializes support for devices. Arguments: None. Return Value: Status code. --*/ { KSTATUS Status; ULONG WorkQueueFlags; WorkQueueFlags = WORK_QUEUE_FLAG_SUPPORT_DISPATCH_LEVEL; IoDeviceWorkQueue = KeCreateWorkQueue(WorkQueueFlags, "IoDeviceWorker"); if (IoDeviceWorkQueue == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeDeviceSupportEnd; } // // Create and initialize the root device. // Status = IoCreateDevice(NULL, NULL, NULL, "Device", NULL, NULL, &IoRootDevice); if (!KSUCCESS(Status)) { goto InitializeDeviceSupportEnd; } ASSERT(IoRootDevice != NULL); InitializeDeviceSupportEnd: return Status; } KSTATUS IopInitializeResourceAllocation ( PKERNEL_INITIALIZATION_BLOCK Parameters ) /*++ Routine Description: This routine initializes support for device resource allocation. Arguments: Parameters - Supplies a pointer to the kernel initialization block. Return Value: Status code. --*/ { IO_INIT_PHYSICAL_MAP_ITERATOR Context; KSTATUS Status; // // Create the physical address arbiter. // Status = IoCreateResourceArbiter(IoRootDevice, ResourceTypePhysicalAddressSpace); if (!KSUCCESS(Status)) { goto InitializeResourceAllocationEnd; } // // Loop through the physical memory descriptor list looking for holes, and // add those holes as allocatable regions. // RtlZeroMemory(&Context, sizeof(IO_INIT_PHYSICAL_MAP_ITERATOR)); Context.Status = STATUS_SUCCESS; MmMdIterate(Parameters->MemoryMap, IopInitializePhysicalAddressArbiterIterator, &Context); if (!KSUCCESS(Context.Status)) { goto InitializeResourceAllocationEnd; } // // Create an I/O space arbiter. // if (ArGetIoPortCount() != 0) { Status = IoCreateResourceArbiter(IoRootDevice, ResourceTypeIoPort); if (!KSUCCESS(Status)) { goto InitializeResourceAllocationEnd; } Status = IoAddFreeSpaceToArbiter(IoRootDevice, ResourceTypeIoPort, 0, ArGetIoPortCount(), 0, NULL, 0); if (!KSUCCESS(Status)) { goto InitializeResourceAllocationEnd; } } // // Create an interrupt line arbiter. // Status = IoCreateResourceArbiter(IoRootDevice, ResourceTypeInterruptLine); if (!KSUCCESS(Status)) { goto InitializeResourceAllocationEnd; } Status = IoAddFreeSpaceToArbiter(IoRootDevice, ResourceTypeInterruptLine, 0, -1, 0, NULL, 0); if (!KSUCCESS(Status)) { goto InitializeResourceAllocationEnd; } // // Create an interrupt vector arbiter. // Status = IoCreateResourceArbiter(IoRootDevice, ResourceTypeInterruptVector); if (!KSUCCESS(Status)) { goto InitializeResourceAllocationEnd; } Status = IoAddFreeSpaceToArbiter(IoRootDevice, ResourceTypeInterruptVector, ArGetMinimumDeviceVector(), ArGetMaximumDeviceVector() + 1, 0, NULL, 0); if (!KSUCCESS(Status)) { goto InitializeResourceAllocationEnd; } // // Create a bus number arbiter. // Status = IoCreateResourceArbiter(IoRootDevice, ResourceTypeBusNumber); if (!KSUCCESS(Status)) { goto InitializeResourceAllocationEnd; } Status = IoAddFreeSpaceToArbiter(IoRootDevice, ResourceTypeBusNumber, 0, -1, 0, NULL, 0); if (!KSUCCESS(Status)) { goto InitializeResourceAllocationEnd; } // // Create a DMA line arbiter. // Status = IoCreateResourceArbiter(IoRootDevice, ResourceTypeDmaChannel); if (!KSUCCESS(Status)) { goto InitializeResourceAllocationEnd; } Status = IoAddFreeSpaceToArbiter(IoRootDevice, ResourceTypeDmaChannel, 0, -1, 0, NULL, 0); if (!KSUCCESS(Status)) { goto InitializeResourceAllocationEnd; } // // Create an empty vendor defined arbiter. Allocation requests that hit // this arbiter will always fail. // Status = IoCreateResourceArbiter(IoRootDevice, ResourceTypeVendorSpecific); if (!KSUCCESS(Status)) { goto InitializeResourceAllocationEnd; } // // Also create an empty GPIO arbiter and an empty simple bus arbiter. // Status = IoCreateResourceArbiter(IoRootDevice, ResourceTypeGpio); if (!KSUCCESS(Status)) { goto InitializeResourceAllocationEnd; } Status = IoCreateResourceArbiter(IoRootDevice, ResourceTypeSimpleBus); if (!KSUCCESS(Status)) { goto InitializeResourceAllocationEnd; } // // Perform any architecture specific initialization. // Status = IopArchInitializeKnownArbiterRegions(); if (!KSUCCESS(Status)) { goto InitializeResourceAllocationEnd; } InitializeResourceAllocationEnd: return Status; } KSTATUS IopInitializeBootDrivers ( PKERNEL_INITIALIZATION_BLOCK Parameters ) /*++ Routine Description: This routine initializes all boot start drivers. Arguments: Parameters - Supplies a pointer to the kernel's initialization information. Return Value: Status code. --*/ { PLIST_ENTRY CurrentEntry; PDRIVER Driver; PLOADED_IMAGE Image; PKPROCESS Process; KSTATUS Status; Status = STATUS_SUCCESS; // // Loop over every loaded image in the kernel process and create a driver // object for it. The driver image list is guarded by the device database // lock rather than the kernel process lock so that threads can be created. // Process = PsGetKernelProcess(); KeAcquireQueuedLock(IoDeviceDatabaseLock); CurrentEntry = Process->ImageListHead.Next; while (CurrentEntry != &(Process->ImageListHead)) { Image = LIST_VALUE(CurrentEntry, LOADED_IMAGE, ListEntry); CurrentEntry = CurrentEntry->Next; // // Skip the kernel module. // if (Image->LoadedLowestAddress == Parameters->KernelModule->LowestAddress) { continue; } Image->SystemContext = Process; Status = IoCreateDriverStructure(Image); if (!KSUCCESS(Status)) { goto InitializeBootDriversEnd; } Status = IopInitializeDriver(Image); if (!KSUCCESS(Status)) { goto InitializeBootDriversEnd; } Driver = Image->SystemExtension; Driver->Flags |= DRIVER_FLAG_LOADED_AT_BOOT; } InitializeBootDriversEnd: KeReleaseQueuedLock(IoDeviceDatabaseLock); return Status; } KSTATUS IopInitializeDeviceDatabase ( PKERNEL_INITIALIZATION_BLOCK Parameters ) /*++ Routine Description: This routine initializes the device to driver database. Arguments: Parameters - Supplies a pointer to the kernel's initialization information. Return Value: Status code. --*/ { PSTR Device; PSTR Driver; PSTR FileEnd; ULONG FileSize; PSTR LineEnd; PSTR Separator; KSTATUS Status; // // Initialize the device database lock and list heads. // IoDeviceDatabaseLock = KeCreateQueuedLock(); if (IoDeviceDatabaseLock == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeDeviceDatabaseEnd; } INITIALIZE_LIST_HEAD(&IoDeviceDatabaseHead); INITIALIZE_LIST_HEAD(&IoDeviceClassDatabaseHead); // // Loop through every entry in the file. // Device = Parameters->DeviceToDriverFile.Buffer; FileSize = Parameters->DeviceToDriverFile.Size; FileEnd = Device + FileSize; while (TRUE) { Driver = NULL; // // Find the end of the line. // LineEnd = RtlStringFindCharacter(Device, '\n', FileSize); if (LineEnd != NULL) { // // If the line is less than 2 characters wide, ignore it. // if (LineEnd - Device < 2) { ASSERT(LineEnd != Device); if (LineEnd + 3 >= FileEnd) { break; } FileSize -= (UINTN)(LineEnd + 1) - (UINTN)Device; Device = LineEnd + 1; continue; } // // Terminate the end. Watch for a CR. // if (*(LineEnd - 1) == '\r') { *(LineEnd - 1) = '\0'; } else { *LineEnd = '\0'; } } // // Find the last equals. // if (*Device != '#') { Separator = RtlStringFindCharacterRight(Device, '=', FileSize); if ((Separator == NULL) || (Separator == LineEnd - 1) || (Separator == Device)) { Status = STATUS_FILE_CORRUPT; goto InitializeDeviceDatabaseEnd; } *Separator = '\0'; Driver = Separator + 1; } // // Add a device or device class entry, depending on the first character // in the line. // if (*Device == 'D') { Status = IoAddDeviceDatabaseEntry(Device + 1, Driver); } else if (*Device == 'C') { Status = IoAddDeviceClassDatabaseEntry(Device + 1, Driver); } else if (*Device == '#') { Status = STATUS_SUCCESS; } else { Status = STATUS_FILE_CORRUPT; } if (!KSUCCESS(Status)) { goto InitializeDeviceDatabaseEnd; } // // Stop if this is end of the file. // if ((LineEnd == NULL) || (LineEnd == FileEnd) || (LineEnd + 1 == FileEnd)) { break; } FileSize -= (UINTN)(LineEnd + 1) - (UINTN)Device; Device = LineEnd + 1; } Status = STATUS_SUCCESS; InitializeDeviceDatabaseEnd: return Status; } KSTATUS IopCreateBootDevices ( PKERNEL_INITIALIZATION_BLOCK Parameters ) /*++ Routine Description: This routine creates all unenumerable devices described at boot time by the device map. Arguments: Parameters - Supplies a pointer to the kernel's initialization information. Return Value: Status code. --*/ { PSTR Device; PSTR DeviceSeparator; PSTR FileEnd; ULONG FileSize; PSTR LineEnd; KSTATUS Status; Status = STATUS_SUCCESS; // // Loop through every entry in the file. // Device = Parameters->DeviceMapFile.Buffer; FileSize = Parameters->DeviceMapFile.Size; FileEnd = Device + FileSize; while (TRUE) { // // Find the end of the line. // LineEnd = RtlStringFindCharacter(Device, '\n', FileSize); if (LineEnd != NULL) { // // If the line is less than 2 characters wide, ignore it. // if (LineEnd - Device < 2) { ASSERT(LineEnd != Device); if (LineEnd + 3 >= FileEnd) { break; } FileSize -= (UINTN)(LineEnd + 1) - (UINTN)Device; Device = LineEnd + 1; continue; } // // Terminate the end. Watch for a CR. // if (*(LineEnd - 1) == '\r') { *(LineEnd - 1) = '\0'; } else { *LineEnd = '\0'; } } // // Find the last colon. // if (*Device != '#') { DeviceSeparator = RtlStringFindCharacterRight(Device, ':', FileSize); if (DeviceSeparator == Device) { Status = STATUS_FILE_CORRUPT; goto CreateBootDevicesEnd; } if (DeviceSeparator != NULL) { *DeviceSeparator = '\0'; } // // Create the device. // Status = IoCreateDevice(NULL, NULL, NULL, Device, NULL, NULL, NULL); if (!KSUCCESS(Status)) { goto CreateBootDevicesEnd; } } // // Stop if this is end of the file. // if ((LineEnd == NULL) || (LineEnd == FileEnd) || (LineEnd + 1 == FileEnd)) { break; } FileSize -= (UINTN)(LineEnd + 1) - (UINTN)Device; Device = LineEnd + 1; } Status = STATUS_SUCCESS; CreateBootDevicesEnd: return Status; } VOID IopInitializePhysicalAddressArbiterIterator ( PMEMORY_DESCRIPTOR_LIST DescriptorList, PMEMORY_DESCRIPTOR Descriptor, PVOID Context ) /*++ Routine Description: This routine is called once for each descriptor in the memory descriptor list. Arguments: DescriptorList - Supplies a pointer to the descriptor list being iterated over. Descriptor - Supplies a pointer to the current descriptor. Context - Supplies an optional opaque pointer of context that was provided when the iteration was requested. Return Value: None. --*/ { PIO_INIT_PHYSICAL_MAP_ITERATOR PhysicalContext; KSTATUS Status; PhysicalContext = Context; ASSERT(PhysicalContext->PreviousEnd <= Descriptor->BaseAddress); // // If there was a gap between the last descriptor and this one, add it // as a hole. // if (PhysicalContext->PreviousEnd < Descriptor->BaseAddress) { Status = IoAddFreeSpaceToArbiter( IoRootDevice, ResourceTypePhysicalAddressSpace, PhysicalContext->PreviousEnd, Descriptor->BaseAddress - PhysicalContext->PreviousEnd, 0, NULL, 0); if (!KSUCCESS(Status)) { PhysicalContext->Status = Status; } } PhysicalContext->PreviousEnd = Descriptor->BaseAddress + Descriptor->Size; return; }