/*********************************************************************** Copyright 2002 Ben Rudiak-Gould. 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA, or visit . ***********************************************************************/ #include "../include/dvdsynth-device.h" #include "kernel-io.h" //#define IOS_SPY DvsDockingBayKernelGlobal* g_callbacks; template static inline T min(T a, T b) { return a>8) \ __asm _emit ((device_id)&255) \ __asm _emit ((device_id)>>8) #define VXD_JUMP(device_id,service_id) VXD_CALL(device_id,32768+(service_id)) #define VXD_STACKCALL_SERVICE(device_id,service_id,prototype) \ __declspec(naked) prototype { VXD_JUMP(device_id,service_id) } VXD_STACKCALL_SERVICE( 16, 7, void __cdecl IOS_Register(DRP* drp) ); extern "C" VXD_STACKCALL_SERVICE( 51, 3, CONFIGRET __cdecl CONFIGMG_Get_Parent(DEVNODE* pdn, DEVNODE dn, ULONG flags) ); VXD_STACKCALL_SERVICE( 1, 97, int __cdecl _CopyPageTable(unsigned lin_page_num, unsigned npages, unsigned* buf, unsigned flags) ); #define PAGEMAPGLOBAL 0x40000000 VXD_STACKCALL_SERVICE( 1, 99, int __cdecl _LinPageLock(unsigned page, unsigned npages, unsigned flags) ); VXD_STACKCALL_SERVICE( 1, 99, int __cdecl _LinPageUnLock(unsigned page, unsigned npages, unsigned flags) ); __declspec(naked) void __stdcall IOS_SendCommand(IOR* ior, DCB_BLOCKDEV* bdd) { __asm { pushad mov esi,[esp+36] mov edi,[esp+40] // mov ebx,[esp+44] VXD_CALL(16,4) popad retn 8 // 12 } } __declspec(naked) unsigned __stdcall Create_Semaphore(unsigned token_count) { __asm { mov ecx,[esp+4] VXD_CALL(1,37) jnc noerror xor eax,eax noerror: retn 4 } } __declspec(naked) unsigned __stdcall Get_VMM_Reenter_Count() { __asm { VXD_CALL(1,6) mov eax,ecx retn } } // Wait_Semaphore flags #define BLOCK_SVC_INTS 1 #define BLOCK_SVC_IF_INTS_LOCKED 2 #define BLOCK_ENABLE_INTS 4 #define BLOCK_POLL 8 #define BLOCK_THREAD_IDLE 16 #define BLOCK_FORCE_SVC_INTS 32 __declspec(naked) void __stdcall Wait_Semaphore(unsigned handle, unsigned flags) { __asm { mov eax,[esp+4] mov ecx,[esp+8] VXD_CALL(1,39) retn 8 } } __declspec(naked) void __stdcall Signal_Semaphore_No_Switch(unsigned handle) { __asm { mov eax,[esp+4] VXD_CALL(1,305) retn 4 } } /*******************************************************************\ \*******************************************************************/ struct SRB { USHORT Length; // 00 UCHAR Function; UCHAR SrbStatus; UCHAR ScsiStatus; // 04 UCHAR PathId; UCHAR TargetId; UCHAR Lun; UCHAR QueueTag; // 08 UCHAR QueueAction; UCHAR CdbLength; UCHAR SenseInfoBufferLength; ULONG SrbFlags; // 0C ULONG DataTransferLength; // 10 ULONG TimeOutValue; // 14 PVOID DataBuffer; // 18 PVOID SenseInfoBuffer; // 1C SRB* NextSrb; // 20 PVOID OriginalRequest; // 24 PVOID SrbExtension; // 28 ULONG QueueSortKey; // 2C UCHAR Cdb[16]; // 30 // The following are Win9x-only fields! PVOID SrbIopPointer; SRB* SrbNextSrb; SRB* SrbNextActiveSrb; UCHAR SrbRetryCount; UCHAR Filler[3]; }; #define SRB_FUNCTION_EXECUTE_SCSI 0x00 /*******************************************************************\ \*******************************************************************/ #ifdef IOS_SPY // I used this code to reverse-engineer ASPI unsigned old_IOS_SendCommand; void PrintHex(unsigned char* p, int n) { if (signed(p) >= 0) return; for (int i=0; iDebugPrintf(fmt, p[i+0], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5], p[i+6], p[i+7], p[i+8], p[i+9], p[i+10], p[i+11], p[i+12], p[i+13], p[i+14], p[i+15]); } } void __stdcall IOSHookFunc(IOR* ior, DCB_BLOCKDEV* bdd, void* something) { if (ior->IOR_func != 15) return; g_callbacks->DebugPrintf("IOS_SendCommand(%08X,%08X,%08X), func=%02X\n", ior, bdd, something, ior->IOR_func); g_callbacks->DebugPrintf("VMM reenter count = %d\n", Get_VMM_Reenter_Count()); const int offset = int(&((DCB*)0)->DCB_bdd); DCB* dcb = (DCB*)((unsigned char*)bdd - offset); unsigned char* iop = (unsigned char*)ior-100; g_callbacks->DebugPrintf("IOP (%08X):\n", iop); PrintHex(iop, 100); // g_callbacks->MemSet(ior->urequestor_usage+1, 0xEE, 16); g_callbacks->DebugPrintf("IOR (%08X):\n", iop+100); PrintHex(iop+100, sizeof(IOR)); unsigned char* srb = (unsigned char*)((IOP*)iop)->IOP_srb; g_callbacks->DebugPrintf("SRB (%08X):\n", srb); PrintHex(srb, sizeof(SRB)); if (ior->IOR_flags & IORF_PHYS_SGDS) { SGD* sgds = (SGD*)ior->IOR_sgd_lin_phys; for (int i=0; iIOR_num_sgds; ++i) { g_callbacks->DebugPrintf("SGD #%d: %08X %08X\n", i, sgds[i].SG_buff_ptr, sgds[i].SG_buff_size); } } unsigned char* srb_extension = srb + sizeof(SRB); g_callbacks->DebugPrintf("SRB extension (%08X):\n", srb_extension); PrintHex(srb_extension, dcb->DCB_srb_ext_size); } __declspec(naked) void HookIOS() { __asm { push esi mov eax,0x00100004 // IOS_SendCommand mov esi,offset hookproc VXD_CALL(1,144) // Hook_Device_Service pop esi retn jmp short hookproc jmp [old_IOS_SendCommand] hookproc: pushad pushfd push ebx push edi push esi call IOSHookFunc popfd popad jmp [old_IOS_SendCommand] } } #endif /*******************************************************************\ \*******************************************************************/ ILB ilb; void __cdecl AsyncEventProc(AEP* paep) { // paep->AEP_result = (paep->AEP_func == AEP_BOOT_COMPLETE) ? AEP_SUCCESS : AEP_FAILURE; paep->AEP_result = AEP_SUCCESS; } void SPTInit() { if (ilb.ILB_service_rtn) return; static DRP drp = { {'X','X','X','X','X','X','X','X'}, DRP_SCSI_LAYER, AsyncEventProc, &ilb, {'D','V','D','S','y','n','t','h',' ','d','e','v','i','c','e',' '}, }; IOS_Register(&drp); g_callbacks->DebugPrintf("after IOS_Register: DRP_reg_result=%x ILB_service_rtn=%x\n", drp.DRP_reg_result, ilb.ILB_service_rtn); #ifdef IOS_SPY HookIOS(); #endif } inline void ILBServiceRtn(ISP* pisp) { ilb.ILB_service_rtn(pisp); } inline bool ILBIOCriteriaRtn(IOR* pior) { return ilb.ILB_io_criteria_rtn(pior) == 0; } inline bool ILBIntIOCriteriaRtn(IOP* piop) { return ilb.ILB_int_io_criteria_rtn(piop) == 0; } __declspec(naked) void __stdcall ILBInternalRequest(IOP* iop, DCB* dcb) { __asm { pushad mov edi,[esp+36] mov ebx,[esp+40] xor edx,edx call [ilb.ILB_internal_request] popad retn 8 } } struct IOPBlock { UCHAR* dcb_expansion; IOP* iop; SGD* sgd_list; SRB* srb; UCHAR* srb_extension; // SenseData* sense; }; bool CreateIOP(DCB* dcb, IOPBlock* result) { ISP_IOP_create i_c; i_c.ISP_i_c_hdr.ISP_func = ISP_CREATE_IOP; i_c.ISP_delta_to_ior = dcb->DCB_cmn.DCB_expansion_length + offset_of_ior_in_iop; i_c.ISP_IOP_size = USHORT( dcb->DCB_cmn.DCB_expansion_length + sizeof(IOP) + dcb->DCB_max_sg_elements*2*sizeof(SGD) + sizeof(SRB) + dcb->DCB_srb_ext_size // + sizeof(SenseData) ); i_c.ISP_i_c_flags = ISP_M_FL_INTERRUPT_TIME; /* ISP_M_FL_MUST_SUCCEED */ ILBServiceRtn(&i_c.ISP_i_c_hdr); if (i_c.ISP_i_c_hdr.ISP_result != 0 || i_c.ISP_IOP_ptr == 0) { return false; } else { unsigned char* p = (unsigned char*)i_c.ISP_IOP_ptr; result->dcb_expansion = p; p += dcb->DCB_cmn.DCB_expansion_length; result->iop = (IOP*)p; p += sizeof(IOP); result->sgd_list = (SGD*)p; p += dcb->DCB_max_sg_elements*2*sizeof(SGD); result->srb = (SRB*)p; p += sizeof(SRB); result->srb_extension = p; // p += dcb->DCB_srb_ext_size; // result->sense = (SenseData*)p; //#ifdef _DEBUG ULONG phys_addr = result->iop->IOP_physical; for (int i=0; iiop->IOP_physical = phys_addr; //#endif result->iop->IOP_ior.urequestor_usage[0] = i_c.ISP_IOP_ptr; // prob. unnecessary return true; } } bool FreeIOP(IOPBlock* block) { ISP_mem_dealloc mem_da; mem_da.ISP_mem_da_hdr.ISP_func = ISP_DEALLOC_MEM; mem_da.ISP_mem_ptr_da = (ULONG)block->dcb_expansion; // iop->IOP_ior.urequestor_usage[0]; ILBServiceRtn(&mem_da.ISP_mem_da_hdr); return (mem_da.ISP_mem_da_hdr.ISP_result == 0); } unsigned scsi_command_semaphore; void __cdecl CompletionHandler() { Signal_Semaphore_No_Switch(scsi_command_semaphore); } unsigned Lin2Phys(unsigned lin) { unsigned page_table_entry; _CopyPageTable(lin >> 12, 1, &page_table_entry, 0); return (lin & 0x00000FFF) + (page_table_entry & 0xFFFFF000); } unsigned SetPhysicalSGDs(void* lin_start, int len, SGD* sgds, int max_sgds) { int num_sgds = 0; unsigned addr = (unsigned)lin_start; unsigned end = addr + len; while (addr < end) { unsigned page_end = (addr & ~4095) + 4096; unsigned length_in_page = min(page_end, end) - addr; unsigned phys = Lin2Phys(addr); if (num_sgds > 0 && phys == sgds[num_sgds-1].SG_buff_ptr + sgds[num_sgds-1].SG_buff_size) { sgds[num_sgds-1].SG_buff_size += length_in_page; } else if (num_sgds >= max_sgds) { // Exceeded the max. number of SGDs! What should we do? // I don't really know, so I just return as much as I // can... break; } else { sgds[num_sgds].SG_buff_ptr = phys; sgds[num_sgds].SG_buff_size = length_in_page; ++num_sgds; } addr += length_in_page; } return num_sgds; } unsigned char* LockMem(unsigned char* p, unsigned len) { if (len==0) return 0; unsigned first_page = unsigned(p)>>12; unsigned last_page = (unsigned(p)+len-1)>>12; return (unsigned char*)_LinPageLock(first_page, last_page-first_page+1, PAGEMAPGLOBAL); } bool UnlockMem(unsigned char* p, int len) { if (p==0 || len==0) return true; unsigned first_page = unsigned(p)>>12; unsigned last_page = (unsigned(p)+len-1)>>12; return !!_LinPageUnLock(first_page, last_page-first_page+1, PAGEMAPGLOBAL); } extern "C" scsi_result_t SendScsiCommand(DCB* dcb, const unsigned char* cdb, int cdblen, unsigned char* buffer, unsigned long* pbuflen, int inout, SenseData* sense) { if (scsi_command_semaphore == 0) { // FIXME: never destroyed scsi_command_semaphore = Create_Semaphore(0); } //g_callbacks->DebugPrintf("SendSCSICommand(%X, %X, %d, %X, %d, %d, %X)\n", dcb, cdb, cdblen, buffer, *pbuflen, inout, sense); IOPBlock block; if (!CreateIOP(dcb, &block)) return MAKE_SCSIRESULT(SRB_STATUS_BUSY, 0, 0, 0, 0); //g_callbacks->DebugPrintf("SendSCSICommand: IOP = %X\n", iop); IOP* iop = block.iop; iop->IOP_original_dcb = dcb; iop->IOP_srb = ULONG(block.srb); iop->IOP_ior.IOR_func = IOR_SCSI_PASS_THROUGH; iop->IOP_ior.IOR_status = IORS_CMD_IN_PROGRESS; // Necessary? A good idea? // ASPI doesn't set IORF_SYNC_COMMAND or IORF_BYPASS_VOLTRK iop->IOP_ior.IOR_flags = IORF_CHAR_COMMAND | IORF_VERSION_002 | IORF_INHIBIT_GEOM_RECOMPUTE | IORF_SRB_VALID; iop->IOP_ior.IOR_flags |= IORF_BYPASS_VOLTRK; iop->IOP_ior.IOR_callback = CompletionHandler; iop->IOP_ior.IOR_xfer_count = *pbuflen; iop->IOP_ior.IOR_buffer_ptr = buffer; iop->IOP_ior.IOR_req_req_handle = iop; iop->IOP_ior.IOR_req_vol_handle = 0; iop->IOP_ior.IOR_sgd_lin_phys = ULONG(block.sgd_list); iop->IOP_ior.IOR_num_sgds = 0; // ??? iop->IOP_ior.IOR_vol_designtr = dcb->DCB_cmn.DCB_unit_number; /* SGD* sgds = (SGD*)(iop->IOP_ior.IOR_sgd_lin_phys); if (dcb->DCB_cmn.DCB_dmd_flags & DCB_dmd_phys_sgd) { iop->IOP_ior.IOR_num_sgds = SetPhysicalSGDs(buffer, *pbuflen, sgds, dcb->DCB_max_sg_elements); } else { iop->IOP_ior.IOR_num_sgds = 1; sgds[0].SG_buff_ptr = ULONG(buffer); sgds[0].SG_buff_size = *pbuflen; } if (dcb->DCB_cmn.DCB_dmd_flags & DCB_dmd_phys_sgd_ptr) { iop->IOP_ior.IOR_sgd_lin_phys = Lin2Phys(iop->IOP_ior.IOR_sgd_lin_phys); } */ SRB* srb = block.srb; srb->Length = sizeof(SRB); srb->Function = SRB_FUNCTION_EXECUTE_SCSI; srb->SrbStatus = 0; srb->ScsiStatus = 0; srb->PathId = dcb->DCB_bus_number; srb->TargetId = dcb->DCB_scsi_target_id; srb->Lun = dcb->DCB_scsi_lun; srb->CdbLength = cdblen; srb->SenseInfoBufferLength = 14; unsigned long buflen = *pbuflen; srb->SrbFlags = (buflen == 0) ? 0 : ((inout & 3) << 6); srb->DataTransferLength = buflen; srb->TimeOutValue = 0; srb->DataBuffer = buffer; srb->SenseInfoBuffer = sense; srb->NextSrb = 0; srb->OriginalRequest = 0; srb->SrbExtension = (char*)srb + sizeof(SRB); for (int i=0; iCdb[i] = cdb[i]; srb->SrbIopPointer = block.iop; srb->SrbNextSrb = 0; scsi_result_t result = MAKE_SCSIRESULT(SRB_STATUS_BUSY, 0, 0, 0, 0); if (ILBIntIOCriteriaRtn(iop)) { iop->IOP_ior.IOR_next = 0; //iop->IOP_ior.IOR_flags &= ~(IORF_SYNC_COMMAND | IORF_BYPASS_VOLTRK); g_callbacks->DebugPrintf("SendSCSICommand: entering IOS_SendCommand\n"); IOS_SendCommand(&iop->IOP_ior, &dcb->DCB_bdd); //#ifdef IOS_SPY // IOSHookFunc(&iop->IOP_ior, &dcb->DCB_bdd, 0); //#endif // ILBInternalRequest(iop, dcb); g_callbacks->DebugPrintf("SendSCSICommand: waiting for completion\n"); Wait_Semaphore(scsi_command_semaphore, BLOCK_SVC_INTS|BLOCK_THREAD_IDLE); g_callbacks->DebugPrintf("SendSCSICommand: completed (IOP=%X,status=%d)\n", iop, iop->IOP_ior.IOR_status); g_callbacks->DebugPrintf("SrbStatus = %X ScsiStatus = %X sense_key = %X asc = %X ascq = %X\n", srb->SrbStatus, srb->ScsiStatus, sense->sense_key, sense->asc, sense->ascq); *pbuflen = srb->DataTransferLength; if (iop->IOP_ior.IOR_status == IORS_INVALID_COMMAND) { return MAKE_SCSIRESULT_ERROR(0x52000); // INVALID COMMAND OPERATION CODE } else if (iop->IOP_ior.IOR_status == IORS_NO_MEDIA) { return MAKE_SCSIRESULT_ERROR(0x23A00); // MEDIUM NOT PRESENT } else if (iop->IOP_ior.IOR_status == IORS_UNCERTAIN_MEDIA) { return MAKE_SCSIRESULT_ERROR(0x62800); // NOT READY TO READY CHANGE } else if (srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) { // Assume ERROR / CHECK CONDITION in this case. // The actual SRB status is sometimes 0x80 == // SRB_STATUS_PENDING | SRB_STATUS_AUTOSENSE_VALID. // What the heck??? result = MAKE_SCSIRESULT(SRB_STATUS_ERROR, SCSISTAT_CHECK_CONDITION, sense->sense_key, sense->asc, sense->ascq); // g_callbacks->MemCpy(sense, block.sense, srb->SenseInfoBufferLength); } else if (srb->SrbStatus == 0) { // probably was never changed from the value I set??? } else { unsigned char scsierror = (srb->SrbStatus == SRB_STATUS_ERROR) ? srb->ScsiStatus : 0; result = MAKE_SCSIRESULT(srb->SrbStatus, scsierror, 0, 0, 0); } } //g_callbacks->DebugPrintf("SendSCSICommand: entering FreeIOP\n"); if (!FreeIOP(&block)) { g_callbacks->DebugPrintf("FreeIOP failed!\n"); } //g_callbacks->DebugPrintf("SendSCSICommand: exiting FreeIOP\n"); return result; } extern "C" DCB* GetNextDCB(DCB* last_dcb) { ISP_GET_FRST_NXT_DCB gfnd; g_callbacks->MemSet(&gfnd, 0, sizeof(gfnd)); gfnd.ISP_gfnd_hdr.ISP_func = ISP_GET_FIRST_NEXT_DCB; gfnd.ISP_gfnd_dcb_offset = (ULONG)last_dcb; gfnd.ISP_gfnd_dcb_type = 0xFF; ILBServiceRtn(&gfnd.ISP_gfnd_hdr); DCB* rtn = (DCB*)gfnd.ISP_gfnd_found_dcb; if (gfnd.ISP_gfnd_hdr.ISP_result != 0) rtn = 0; return rtn; } scsi_result_t __cdecl ScsiCommand(DvsDeviceKernel* self, const unsigned char* cdb, int cdblen, unsigned char* buffer, unsigned long* pbuflen, int inout, SenseData* sense) { return SendScsiCommand(((MirrorDriveKernel*)self)->dcb, cdb, cdblen, buffer, pbuflen, inout, sense); } extern "C" dvs_scsi_func* GetDispatchFunc() { return ScsiCommand; } extern "C" void __cdecl DvdsynthDriverInit(DvsDockingBayKernelGlobal* callbacks) { g_callbacks = callbacks; SPTInit(); } extern "C" unsigned __stdcall DLLEntry(void*, unsigned, void*) { return 1; }