/*++ Copyright (c) 1999-2002 Microsoft Corporation Module Name: init.c Abstract: Initialization code for the win32 api equivalents in kernel mode for xbox --*/ #include "dllp.h" #include #define DASHBOARD_TITLE_ID 0xFFFE0000 #define ARRAYSIZE(a) (sizeof(a) / sizeof(a[0])) #include "xmeta.h" COBJECT_STRING DDrive = CONSTANT_OBJECT_STRING( OTEXT("\\??\\D:") ); COBJECT_STRING CdDevice = CONSTANT_OBJECT_STRING( OTEXT("\\Device\\CdRom0") ); COBJECT_STRING MainVol = CONSTANT_OBJECT_STRING( OTEXT("\\Device\\Harddisk0\\partition1\\") ); COBJECT_STRING TDrive = CONSTANT_OBJECT_STRING( OTEXT("\\??\\T:") ); COBJECT_STRING TitleData = CONSTANT_OBJECT_STRING( OTEXT("\\Device\\Harddisk0\\partition1\\TDATA") ); COBJECT_STRING UDrive = CONSTANT_OBJECT_STRING( OTEXT("\\??\\U:") ); COBJECT_STRING UserData = CONSTANT_OBJECT_STRING( OTEXT("\\Device\\Harddisk0\\partition1\\UDATA") ); NTSTATUS XapiValidateDiskPartition( POBJECT_STRING PartitionName ) { return XapiValidateDiskPartitionEx(PartitionName, 16384); } NTSTATUS XapiValidateDiskPartitionEx( POBJECT_STRING PartitionName, ULONG BytesPerCluster ) { NTSTATUS Status; OBJECT_ATTRIBUTES Obja; HANDLE Handle; IO_STATUS_BLOCK IoStatusBlock; FILE_FS_SIZE_INFORMATION SizeInfo; InitializeObjectAttributes( &Obja, PartitionName, OBJ_CASE_INSENSITIVE, NULL, NULL ); // // Open the partition // Status = NtOpenFile( &Handle, (ACCESS_MASK)FILE_LIST_DIRECTORY | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE | FILE_OPEN_FOR_FREE_SPACE_QUERY ); if (NT_SUCCESS(Status)) { // // Determine the size parameters of the volume. // Status = NtQueryVolumeInformationFile( Handle, &IoStatusBlock, &SizeInfo, sizeof(SizeInfo), FileFsSizeInformation ); NtClose(Handle); if (NT_SUCCESS(Status)) { if (SizeInfo.BytesPerSector * SizeInfo.SectorsPerAllocationUnit != BytesPerCluster) { Status = STATUS_UNRECOGNIZED_VOLUME; } } } return Status; } NTSTATUS XapiCopySectionToFile( HANDLE SectionHandle, PSTR pszPathBuffer, UINT cchPathBuffer, PCSTR pcszFileName ) { OBJECT_STRING MetaFilePathString; OBJECT_ATTRIBUTES Obja; IO_STATUS_BLOCK IoStatusBlock; NTSTATUS Status; HANDLE MetaHandle; int nPathLen; ASSERT((INVALID_HANDLE_VALUE != SectionHandle) && pszPathBuffer && pcszFileName); nPathLen = ocslen(pszPathBuffer); lstrcpynO(&(pszPathBuffer[nPathLen]), pcszFileName, cchPathBuffer - nPathLen); RtlInitObjectString(&MetaFilePathString, pszPathBuffer); InitializeObjectAttributes( &Obja, &MetaFilePathString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtCreateFile(&MetaHandle, SYNCHRONIZE | GENERIC_WRITE, &Obja, &IoStatusBlock, NULL, FILE_ATTRIBUTE_SYSTEM, FILE_SHARE_READ, FILE_OPEN_IF, FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT ); pszPathBuffer[nPathLen] = OBJECT_NULL; if (NT_SUCCESS(Status)) { FILE_NETWORK_OPEN_INFORMATION NetworkInfo; Status = NtQueryInformationFile( MetaHandle, &IoStatusBlock, &NetworkInfo, sizeof(NetworkInfo), FileNetworkOpenInformation ); if (NT_SUCCESS(Status)) { // // Only write if this is an empty file (first creation or first time // after an invalid, failed creation) // if (0 == NetworkInfo.EndOfFile.QuadPart) { PBYTE pbSection; // // Attempt to load the title info section // pbSection = XLoadSectionByHandle(SectionHandle); if (pbSection) { Status = NtWriteFile(MetaHandle, NULL, NULL, NULL, &IoStatusBlock, pbSection, XGetSectionSize(SectionHandle), NULL ); if (!NT_SUCCESS(Status)) { XDBGWRN("XAPI", "XapiCopySectionToFile() failed to write to file"); } // // Unload the title info section (we're done with it) // XFreeSectionByHandle(SectionHandle); } } } else { XDBGERR("XAPI", "XapiCopySectionToFile() failed to get meta file size"); } NtClose(MetaHandle); } else if (STATUS_OBJECT_NAME_COLLISION == Status) { // // If the meta data image file already exists, we should continue // as if everything is fine (no need to rewrite this data) // Status = STATUS_SUCCESS; } return Status; } NTSTATUS XapiMapLetterToDirectory( PCOBJECT_STRING pcDriveString, PCOBJECT_STRING pcPathString, PCOSTR pcszTitleId, BOOL fCreateDirectory, LPCWSTR pcszTitleName, BOOL fUpdateTimestamp ) { NTSTATUS Status; OCHAR szPathTemp[MAX_PATH]; OBJECT_ATTRIBUTES Obja; IO_STATUS_BLOCK IoStatusBlock; OBJECT_STRING PerTitlePathString; HANDLE DirHandle; ASSERT(pcDriveString && pcPathString && pcszTitleId); InitializeObjectAttributes( &Obja, (POBJECT_STRING) pcPathString, OBJ_CASE_INSENSITIVE, NULL, NULL ); // // The parent of the subirectory may not already exist // so we open it first with 'FILE_OPEN_IF' to create it, // just in case. This mainly occurs when running // recovery. // Status = NtCreateFile( &DirHandle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &Obja, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT ); if (NT_SUCCESS(Status)) { NtClose(DirHandle); } else if (STATUS_NOT_A_DIRECTORY == Status) { // // XMountAlternateTitle when called on an MU passes a volume (not the // volume's root directory) for pcDriveString. So the above call will // fail with STATUS_NOT_A_DIRECTORY. This is OK though, so we change // to success and continue. Note that if we remove FILE_DIRECTORY_FILE // from the above NtCreateFile, then recovery creates a file named UDATA // instead of a directory and promptly crashes. // Status = STATUS_SUCCESS; } else { XDBGWRN("XAPI", "XapiMapLetterToDirectory(): NtCreateFile() \"%Z\" failed with 0x%08x", pcPathString, Status); } if (NT_SUCCESS(Status)) { ASSERT(ocslen(pcszTitleId) < CCHMAX_HEX_DWORD); ASSERT(pcPathString->Length < (sizeof(szPathTemp) - (CCHMAX_HEX_DWORD * sizeof(OCHAR)))); ocscpy(szPathTemp, pcPathString->Buffer); // // Usually, pcDriveString usually does not have a '\\' on the end, the exception // is on mounting MU's. So here we check for '\\' and only append if necessary. // if(szPathTemp[(pcPathString->Length / sizeof(OCHAR))-1] != OTEXT('\\')) { szPathTemp[pcPathString->Length / sizeof(OCHAR)] = OTEXT('\\'); } else { pcPathString->Length--; } ocscpy(&(szPathTemp[(pcPathString->Length + sizeof(OCHAR)) / sizeof(OCHAR)]), pcszTitleId); RtlInitObjectString(&PerTitlePathString, szPathTemp); InitializeObjectAttributes( &Obja, &PerTitlePathString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtCreateFile( &DirHandle, FILE_LIST_DIRECTORY | SYNCHRONIZE | FILE_GENERIC_WRITE, &Obja, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, fCreateDirectory ? FILE_OPEN_IF : FILE_OPEN, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT ); if (NT_SUCCESS(Status)) { if (fUpdateTimestamp) { FILE_BASIC_INFORMATION BasicInfo; // // Zero all the time values we can set. // RtlZeroMemory(&BasicInfo, sizeof(BasicInfo)); // // Set the last write times // KeQuerySystemTime(&BasicInfo.LastWriteTime); NtSetInformationFile( DirHandle, &IoStatusBlock, &BasicInfo, sizeof(BasicInfo), FileBasicInformation ); } // // Only attempt to write meta data if non-null titlename and pubname strings were passed in // (these are not passed in if we're mounting another title's drive) // if (pcszTitleName) { HANDLE TitleInfoSection; HANDLE TitleImageSection; HANDLE SaveImageSection; TitleInfoSection = XGetSectionHandle("$$XTINFO"); TitleImageSection = XGetSectionHandle("$$XTIMAGE"); SaveImageSection = XGetSectionHandle("$$XSIMAGE"); if ((INVALID_HANDLE_VALUE != TitleInfoSection) || (L'\0' != *pcszTitleName)) { // // Create TITLEMETA.XBX file underneath the save game directory and // write metadata information there // OBJECT_STRING MetaFilePathString; HANDLE MetaHandle; int nPathLen = ocslen(szPathTemp); lstrcpynO(&(szPathTemp[nPathLen]), g_cszTitleMetaFileName, ARRAYSIZE(szPathTemp) - nPathLen); RtlInitObjectString(&MetaFilePathString, szPathTemp); InitializeObjectAttributes( &Obja, &MetaFilePathString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtCreateFile(&MetaHandle, SYNCHRONIZE | GENERIC_WRITE, &Obja, &IoStatusBlock, NULL, FILE_ATTRIBUTE_SYSTEM, FILE_SHARE_READ, FILE_OPEN_IF, FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT ); szPathTemp[nPathLen] = OBJECT_NULL; if (NT_SUCCESS(Status)) { FILE_NETWORK_OPEN_INFORMATION NetworkInfo; Status = NtQueryInformationFile( MetaHandle, &IoStatusBlock, &NetworkInfo, sizeof(NetworkInfo), FileNetworkOpenInformation ); if (NT_SUCCESS(Status)) { // // Only write if this is an empty file (first creation or first time // after an invalid, failed creation) // if (0 == NetworkInfo.EndOfFile.QuadPart) { PVOID pvTitleInfo = NULL; // // Attempt to load the title info section // if (INVALID_HANDLE_VALUE != TitleInfoSection) { pvTitleInfo = XLoadSectionByHandle(TitleInfoSection); } if (pvTitleInfo) { // // Write TITLEMETA.XBX as a copy of the title info section // Status = NtWriteFile(MetaHandle, NULL, NULL, NULL, &IoStatusBlock, pvTitleInfo, XGetSectionSize(TitleInfoSection), NULL ); if (!NT_SUCCESS(Status)) { XDBGWRN("XAPI", "XapiMapLetterToDirectory() failed to write title info file"); } XFreeSectionByHandle(TitleInfoSection); } else { // // Write TITLEMETA.XBX with the title name value from the cert // // // szBuffer size is: 1 signature WCHAR plus one line of metadata // WCHAR szBuffer[1 + MAX_METADATA_LINE]; DWORD dwSize; dwSize = _snwprintf(szBuffer, ARRAYSIZE(szBuffer), L"%lc%ls%lc%ls%ls", g_chUnicodeSignature, g_cszTitleNameTag, g_chEqual, pcszTitleName, g_cszCRLF); dwSize *= sizeof(WCHAR); Status = NtWriteFile(MetaHandle, NULL, NULL, NULL, &IoStatusBlock, szBuffer, dwSize, NULL ); if (!NT_SUCCESS(Status)) { XDBGWRN("XAPI", "XapiMapLetterToDirectory() failed to write title info file"); } } } } else { XDBGERR("XAPI", "XapiMapLetterToDirectory() failed to get meta file size"); } NtClose(MetaHandle); } else if (STATUS_OBJECT_NAME_COLLISION == Status) { // // If the meta data file already exists, we should continue // as if everything is fine (no need to rewrite this data) // Status = STATUS_SUCCESS; } } if (NT_SUCCESS(Status) && (INVALID_HANDLE_VALUE != TitleImageSection)) { Status = XapiCopySectionToFile(TitleImageSection, szPathTemp, ARRAYSIZE(szPathTemp), g_cszTitleImageFileName); } if (NT_SUCCESS(Status) && (INVALID_HANDLE_VALUE != SaveImageSection)) { Status = XapiCopySectionToFile(SaveImageSection, szPathTemp, ARRAYSIZE(szPathTemp), g_cszSaveImageFileName); } } NtClose(DirHandle); } else if (fCreateDirectory) { XDBGWRN("XAPI", "XapiMapLetterToDirectory(): NtCreateFile() \"%s\" failed with 0x%08x", szPathTemp, Status); } } if (NT_SUCCESS(Status)) { Status = IoCreateSymbolicLink((POBJECT_STRING) pcDriveString, &PerTitlePathString); if (!NT_SUCCESS(Status)) { XDBGERR("XAPI", "XapiMapLetterToDirectory(): IoCreateSymbolicLink() failed \"%Z\"->\"%Z\"", pcDriveString, pcPathString); } } return Status; } NTSTATUS XapiSetupPerTitleDriveLetters( DWORD dwTitleId, LPCWSTR pcszTitleName ) { NTSTATUS Status; OCHAR szTitleId[CCHMAX_HEX_DWORD]; DwordToStringO(dwTitleId, szTitleId); Status = XapiMapLetterToDirectory(&TDrive, &TitleData, szTitleId, TRUE, NULL, FALSE); if (NT_SUCCESS(Status)) { Status = XapiMapLetterToDirectory(&UDrive, &UserData, szTitleId, TRUE, pcszTitleName, FALSE); } return Status; } VOID XapiBootToDash( DWORD dwReason, DWORD dwParameter1, DWORD dwParameter2 ) { if (DASHBOARD_TITLE_ID != XeImageHeader()->Certificate->TitleID) { LD_LAUNCH_DASHBOARD LaunchDash; ZeroMemory(&LaunchDash, sizeof(LD_LAUNCH_DASHBOARD)); LaunchDash.dwReason = dwReason; LaunchDash.dwContext = 0; LaunchDash.dwParameter1 = dwParameter1; LaunchDash.dwParameter2 = dwParameter2; XLaunchNewImage(NULL, (PLAUNCH_DATA) &LaunchDash); } else { // // Display Universal Error Message // HalReturnToFirmware(HalFatalErrorRebootRoutine); } } // // Macros to swap the byte order of a USHORT or ULONG at compile time. // #define XapiConstantUshortByteSwap(ushort) \ ((((USHORT)ushort) >> 8) + ((((USHORT)ushort) & 0x00FF) << 8)) #define XapiConstantUlongByteSwap(ulong) \ ((((ULONG)ulong) >> 24) + ((((ULONG)ulong) & 0x00FF0000) >> 8) + \ ((((ULONG)ulong) & 0x0000FF00) << 8) + ((((ULONG)ulong) & 0x000000FF) << 24)) NTSTATUS XapiVerifyMediaInDrive( VOID ) /*++ Routine Description: Verifies that the DVD drive has in fact authenticated X2 media. Arguments: None. Return Value: None. --*/ { NTSTATUS status; SCSI_PASS_THROUGH_DIRECT PassThrough; PCDB Cdb = (PCDB)&PassThrough.Cdb; DVDX2_AUTHENTICATION Authentication; OBJECT_ATTRIBUTES Obja; HANDLE Handle; IO_STATUS_BLOCK IoStatusBlock; ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); InitializeObjectAttributes( &Obja, (POBJECT_STRING) &CdDevice, OBJ_CASE_INSENSITIVE, NULL, NULL ); // // Open the CD/DVD device // status = NtOpenFile( &Handle, (ACCESS_MASK) GENERIC_READ | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT ); if (!NT_SUCCESS(status)) { return status; } // // Prepare the SCSI pass through structure. // RtlZeroMemory(&PassThrough, sizeof(SCSI_PASS_THROUGH_DIRECT)); PassThrough.Length = sizeof(SCSI_PASS_THROUGH_DIRECT); // // Request the authentication page from the DVD-X2 drive. // PassThrough.DataIn = SCSI_IOCTL_DATA_IN; PassThrough.DataBuffer = &Authentication; PassThrough.DataTransferLength = sizeof(DVDX2_AUTHENTICATION); RtlZeroMemory(Cdb, sizeof(CDB)); Cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; Cdb->MODE_SENSE10.PageCode = DVDX2_MODE_PAGE_AUTHENTICATION; *((PUSHORT)&Cdb->MODE_SENSE10.AllocationLength) = (USHORT)XapiConstantUshortByteSwap(sizeof(DVDX2_AUTHENTICATION)); RtlZeroMemory(&Authentication, sizeof(DVDX2_AUTHENTICATION)); status = NtDeviceIoControlFile(Handle, NULL, NULL, NULL, &IoStatusBlock, IOCTL_SCSI_PASS_THROUGH_DIRECT, &PassThrough, sizeof(SCSI_PASS_THROUGH_DIRECT), NULL, 0); NtClose(Handle); if (!NT_SUCCESS(status)) { return status; } // // Check if the DVD-X2 drive thinks that this is a valid CDF header. // if (Authentication.AuthenticationPage.CDFValid != DVDX2_CDF_VALID) { return STATUS_UNRECOGNIZED_MEDIA; } // // Check if the DVD-X2 drive already thinks that we're authenticated. // if ((Authentication.AuthenticationPage.PartitionArea != 0) && (Authentication.AuthenticationPage.Authentication != 0)) { return STATUS_SUCCESS; } // // The DVD-X2 drive does not think that we're authenticated. // return STATUS_UNSUCCESSFUL; } VOID XapiInitProcess( VOID ) /*++ Routine Description: Initializes the Xapi process, loads the title and starts its first thread. Arguments: None. Return Value: None. --*/ { HANDLE ThreadHandle; /* ULONG_PTR EntryPoint; */ ULONG ProcessHeapFlags; RTL_HEAP_PARAMETERS HeapParameters; HANDLE hthread; USHORT CharIndex; DWORD dwReason = XLD_LAUNCH_DASHBOARD_MAIN_MENU; DWORD dwParameter1; DWORD dwParameter2 = 0; NTSTATUS status; // Initialize the auto-power-down feature. XapiInitAutoPowerDown(); RtlZeroMemory( &HeapParameters, sizeof( HeapParameters ) ); ProcessHeapFlags = HEAP_GROWABLE | HEAP_CLASS_0; HeapParameters.Length = sizeof( HeapParameters ); // Note: there are some more parameters in the image that normal Win32 apps use to // modify ProcessHeapFlags and HeapParameters. Here, we just use the heap reserve/commit // sizes specified XapiProcessHeap = RtlCreateHeap(ProcessHeapFlags, NULL, XeImageHeader()->SizeOfHeapReserve, XeImageHeader()->SizeOfHeapCommit, NULL, &HeapParameters); if (XapiProcessHeap == NULL) { dwReason = XLD_LAUNCH_DASHBOARD_ERROR; dwParameter1 = XLD_ERROR_INVALID_XBE; goto handle_error; } if ((XeImageHeader()->Certificate->AllowedMediaTypes & XBEIMAGE_MEDIA_TYPE_MEDIA_MASK) == XBEIMAGE_MEDIA_TYPE_DVD_X2) { // // This title is only allowed to run on X2 media - double check that the drive // has mounted X2 media: // if (!NT_SUCCESS(XapiVerifyMediaInDrive())) { // // Don't display a dash error message - we want to avoid showing an // inapplicable errorduring race conditions where valid media was ejected // while this code was verifying the media // XapiBootToDash(XLD_LAUNCH_DASHBOARD_MAIN_MENU, 0, 0); return; } } if (!(XeImageHeader()->InitFlags & XINIT_NO_SETUP_HARD_DISK)) { // Get the hard disk ready if (!NT_SUCCESS(XapiValidateDiskPartition((POBJECT_STRING) &MainVol))) { dwReason = XLD_LAUNCH_DASHBOARD_ERROR; dwParameter1 = XLD_ERROR_INVALID_HARD_DISK; goto handle_error; } } // Give the DVD/CD drive a drive letter if (!NT_SUCCESS(status = IoCreateSymbolicLink((POBJECT_STRING) &DDrive, (POBJECT_STRING) &CdDevice))) { XDBGWRN("XAPI", "The D: drive is not assigned to the CD/DVD device - error 0x%08x", status); // // Devkits will tend to already have the D: drive assigned, so we'll ignore // this error, but reboot to the dash in all other cases... // if (STATUS_OBJECT_NAME_COLLISION != status) { dwReason = XLD_LAUNCH_DASHBOARD_ERROR; dwParameter1 = XLD_ERROR_INVALID_XBE; goto handle_error; } } if ((!(XeImageHeader()->InitFlags & XINIT_NO_SETUP_HARD_DISK)) && (!(XeImageHeader()->InitFlags & XINIT_DONT_MODIFY_HARD_DISK))) { // After we've initialized everything, start the process if (!NT_SUCCESS(status = XapiSetupPerTitleDriveLetters( XeImageHeader()->Certificate->TitleID, XeImageHeader()->Certificate->TitleName))) { XDBGWRN("XAPI", "Could not set up per-title drive letters"); if (STATUS_DISK_FULL == status) { dwReason = XLD_LAUNCH_DASHBOARD_MEMORY; dwParameter1 = (DWORD) 'T'; // drive letter dwParameter2 = 16; // blocks needed } else { dwReason = XLD_LAUNCH_DASHBOARD_ERROR; dwParameter1 = XLD_ERROR_INVALID_HARD_DISK; } goto handle_error; } } if ((!(XeImageHeader()->InitFlags & XINIT_NO_SETUP_HARD_DISK)) && (XeImageHeader()->InitFlags & XINIT_MOUNT_UTILITY_DRIVE)) { if (!XMountUtilityDrive(XeImageHeader()->InitFlags & XINIT_FORMAT_UTILITY_DRIVE)) { XapiBootToDash(XLD_LAUNCH_DASHBOARD_ERROR, XLD_ERROR_INVALID_HARD_DISK, 0); } } // // Check to see if parental control settings should prevent this title from // running on this box // if (XeImageHeader()->Certificate->GameRatings < XGetParentalControlSetting()) { XDBGWRN("XAPI", "The box's parental control setting prohibits this title from starting"); XDBGWRN("XAPI", "Go to the Xbox dashboard to change the box's parental control setting"); dwReason = XLD_LAUNCH_DASHBOARD_ERROR; dwParameter1 = XLD_ERROR_XBE_PARENTAL_CONTROL; dwParameter2 = XeImageHeader()->Certificate->GameRatings; goto handle_error; } XDBGTRC("XAPI", "InitProcess: SizeOfStack=%d", XeImageHeader()->SizeOfStackCommit); handle_error: if (XLD_LAUNCH_DASHBOARD_MAIN_MENU != dwReason) { XapiBootToDash(dwReason, dwParameter1, dwParameter2); } }