Page 1 of 1

EHCI Driver Problems With VirtualBox

Posted: Wed Feb 29, 2012 7:33 pm
by Hoozim
Hello,

My OS has a working EHCI/USB20/MASS STORAGE driver (tested on real hardware). I am trying to get my operating system to work with Virtual Box but the EHCI driver's EHCI Reset doesn't work. The specification says that the controller is supposed to set the bit to zero once the reset is finished with. Yet VirtualBox does not do this.

Here are is my init function and my EHCI reset function:

Code: Select all

void EHCI::ConfigureEHCI(dword baseAddressPhysical, byte interrupt)
{
	EHCI::Private::BaseAddressPhysical = baseAddressPhysical;
	EHCI::Private::Interrupt = interrupt;
	
	dword baseAddress = (dword)VirtualMemory::PCIMemory(baseAddressPhysical, 1);
	EHCI::Private::BaseAddress = baseAddress;


	EHCI::Private::CapabilityRegisters* capRegs = (EHCI::Private::CapabilityRegisters*)baseAddress;
	EHCI::Private::CapRegister = capRegs;
	EHCI::Private::NumberOfPorts = capRegs->HCSPARAMS.N_PORTS;
	
	if (capRegs->HCCPARAMS.Addressing64Bit)
	{
		EHCI::Private::Addressing64Bit = 1;
		//return;  Virtual Box Uses a 64 Bit EHCI
	}

	if (capRegs->HCSPARAMS.PortPowerControl)
	{
		EHCI::Private::PortPowerControl = 1;
	}

	EHCI::Private::OperationalRegistersBase = EHCI::Private::BaseAddress + capRegs->CAPLENGTH;
	EHCI::Private::OperationalRegisters* opRegs = (EHCI::Private::OperationalRegisters*)EHCI::Private::OperationalRegistersBase;
	EHCI::Private::OpRegister = (EHCI::Private::OperationalRegisters*)EHCI::Private::OperationalRegistersBase;
	EHCI::Private::PORTSCBaseAddress = ((EHCI::Private::OperationalRegistersBase) + 0x44);
	
	IDT::InstallIRQ((dword)&EHCI::Private::EHCIIRQHandler, 0x8, PModeGate | PresentGate, 32 + interrupt);
	USB20::ConfigureUSB20();

	EHCI::Private::ResetEHCI();

	for (int i = 0; i < EHCI::Private::NumberOfPorts; i++)
	{
		EHCI::Private::PORTSC* port = EHCI::Private::SelectPortSC(i);
		if (EHCI::Private::PortPowerControl)
		{
			port->PortPower = 1;
		}
	}
}

void EHCI::Private::ResetEHCI()
{
	
	EHCI::Private::OpRegister->USBCMD.RunStop = 0;
	while (EHCI::Private::OpRegister->USBSTS.HCHalted == 0)
	{
		PIT::WaitMilliseconds(30);
	}
	EHCI::Private::OpRegister->USBCMD.HostControllerReset = 1;
	while (EHCI::Private::OpRegister->USBCMD.HostControllerReset == 1)
	{
		PIT::WaitMilliseconds(20);
	}
	

	EHCI::Private::OpRegister->CTRLDSSEGMENT = 0;
	EHCI::Private::EnableInterrupts();
	EHCI::Private::OpRegister->PERIODICLISTBASE = 0;
	EHCI::Private::OpRegister->ASYNCLISTADDR = 0;
	EHCI::Private::OpRegister->USBCMD.RunStop = 1;
	while (EHCI::Private::OpRegister->USBSTS.HCHalted == 1)
	{
		PIT::WaitMilliseconds(20);
	}
	EHCI::Private::OpRegister->CONFIGFLAG.ConfigureFlag = 1;
	PIT::WaitMilliseconds(50);
}
Thank you in advance.

Hoozim

Re: EHCI Driver Problems With VirtualBox

Posted: Wed Feb 29, 2012 11:29 pm
by Brendan
Hi,

Every polling loop should have a time-out, in case of hardware failure. For example:

Code: Select all

    time_out = 500 / 20;
    while (time_out > 0)
    {
        if(EHCI::Private::OpRegister->USBCMD.HostControllerReset == 0) break;
        PIT::WaitMilliseconds(20);
        time_out--;
    }
    if (time_out <= 0) {
            // Handle the time out properly instead of locking up
    }
Next, is anything "volatile"? Can you disassemble the code and find out if it actually does what the C++ source code says?

If you're sure the code generated by the compiler is 100% correct, then the problem may actually be VirtualBox. I know they've had trouble with EHCI before.


Cheers,

Brendan

Re: EHCI Driver Problems With VirtualBox

Posted: Thu Mar 01, 2012 4:32 pm
by Hoozim
I set all of the static variables to 'volatile'. The reset now succeeds! Yet, device configuration still fails. Here is the code responsible for transaction:

Code: Select all

void EHCI::Private::PerformTransaction(dword AsyncAddress)
{
	EHCI::Private::OpRegister->USBSTS.USBINT = 1;
	EHCI::Private::USBInterrupt = false;
	EHCI::Private::OpRegister->ASYNCLISTADDR = VirtualMemory::PhysicalAddress(AsyncAddress);
	EHCI::Private::OpRegister->USBCMD.AsynchronousScheduleEnable = 1;

	while (EHCI::Private::OpRegister->USBSTS.AsynchronousScheduleStatus == 0)
	{
		PIT::WaitMilliseconds(5);
	}
	while (!EHCI::Private::USBInterrupt)
	{
		PIT::WaitMilliseconds(5);
	}
	EHCI::Private::USBInterrupt = false;
	EHCI::Private::OpRegister->USBCMD.AsynchronousScheduleEnable = 0;
	while (EHCI::Private::OpRegister->USBSTS.AsynchronousScheduleStatus == 1)
	{
		PIT::WaitMilliseconds(5);
	}
	return;
}

EHCI::Private::QTD* EHCI::Private::AllocateQTD(dword next)
{
	EHCI::Private::QTD* qtd = (EHCI::Private::QTD*)Heap::Malloc(sizeof(EHCI::Private::QTD), 32);
	memset(qtd, 0, sizeof(EHCI::Private::QTD));
	if (next == 0)
		qtd->NextQTDPointer = 1;
	else
		qtd->NextQTDPointer = VirtualMemory::PhysicalAddress(next);
	return qtd;
}

dword EHCI::Private::AllocateQTDBuffer(EHCI::Private::QTD* qtd)
{
	void* data = Heap::Malloc(4096, 4096);
	memset(data, 0, 4096);
	qtd->BufferPointer0 = VirtualMemory::PhysicalAddress((dword)data);
	qtd->BufferPointer1 = qtd->BufferPointer2 = qtd->BufferPointer3 = qtd->BufferPointer4 = 0;
	return (dword)data;
}

void EHCI::Private::CreateQueueHead(void* QHAddr, dword HorizontalPointer, void* FirstQTD, byte DeviceAddr, byte EndpointNum, dword MaxPacketSize)
{
	EHCI::Private::QueueHead* qh = (EHCI::Private::QueueHead*)QHAddr;
	memset(QHAddr, 0, sizeof(EHCI::Private::QueueHead));

	qh->HorizontalPointer = VirtualMemory::PhysicalAddress(HorizontalPointer) | 2;

	qh->DeviceAddress = DeviceAddr;
	qh->InactivateOnNextTransaction = 0;
	qh->EndpointNumber = EndpointNum;
	qh->EndpointSpeed = 2; 
	qh->DTC = 1; 
	qh->H = 1;
	qh->MaximumPacketLength = MaxPacketSize;
	qh->ControlEndpointFlag = 0;
	qh->NakReloadFlag = 15;

	qh->SMask = 0;
	qh->CMask = 0;
	qh->HubAddr = 0;
	qh->PortNumber = 0;
	qh->Mult = 1;

	if (FirstQTD == 0)
	{
		qh->QTD.NextQTDPointer = 1;
	}
	else
	{
		qh->QTD.NextQTDPointer = VirtualMemory::PhysicalAddress((dword)FirstQTD);
	}
}

void* EHCI::Private::CreateSetupQTD(dword next, bool toggle, dword tokenBytes, dword type, dword requestID, dword valueLo, dword valueHi, dword index, dword length, dword* buff)
{
	byte SETUP = 2;

	EHCI::Private::QTD* qtd = EHCI::Private::AllocateQTD(next);
	qtd->AlternateNextQTDPointer = 1;
	qtd->Token.Status = 0x80;
	qtd->Token.PIDCode = 2;
	qtd->Token.ErrorCounter = 3;
	qtd->Token.CurrentPage = 0;
	qtd->Token.InterruptOnComplete = 1;
	qtd->Token.TotalBytestoTransfer = tokenBytes;
	qtd->Token.DataToggle = toggle;

	dword buffer = EHCI::Private::AllocateQTDBuffer(qtd);
	EHCI::Private::SetupData* request = (EHCI::Private::SetupData*)buffer;
	request->RequestType = type;
	request->RequestID = requestID;
	request->ValueHi = valueHi;
	request->ValueLow = valueLo;
	request->Index = index;
	request->Length = length;

	*buff = buffer;
	return (void*)qtd;
}

void* EHCI::Private::CreateIOQTD(dword next, byte direction, bool toggle, dword tokenBytes, dword *buff)
{
	byte OUT = 0;
	byte IN = 1;

	EHCI::Private::QTD* qtd = EHCI::Private::AllocateQTD(next);
	qtd->AlternateNextQTDPointer = 1;
	qtd->Token.Status = 0x80;
	qtd->Token.PIDCode = direction;
	qtd->Token.ErrorCounter = 3;
	qtd->Token.CurrentPage = 0;
	qtd->Token.InterruptOnComplete = 1;
	qtd->Token.TotalBytestoTransfer = tokenBytes;
	qtd->Token.DataToggle = toggle;
	
	dword buffer = EHCI::Private::AllocateQTDBuffer(qtd);
	*buff = buffer;
	return (void*)qtd;
}

byte EHCI::SetupTransaction(byte DevAddr, byte EndpointNum, word BytesToTransfer, byte RequestType, byte Request, byte ValueHi, byte ValueLow, word Index, byte* Data, byte direction, byte port)
{
	byte OUT = 0;
	byte IN = 1;
	byte SETUP = 2;

	void* virtAList = Heap::Malloc(sizeof(EHCI::Private::QueueHead), 32);
	EHCI::Private::OpRegister->USBCMD.AsynchronousScheduleEnable = 0;

	dword SetupBuffer = 0;
	dword DataBuffer = 0;

	
	if (BytesToTransfer == 0)
	{
		void* HandShakeQTD = 0;
		HandShakeQTD = EHCI::Private::CreateIOQTD(0, IN, 1, 0, &DataBuffer);
		Heap::Free((void*)DataBuffer);
		void* SetupQTD = EHCI::Private::CreateSetupQTD((dword)HandShakeQTD, 0, 8, RequestType, Request, ValueLow, ValueHi, Index, 0, &SetupBuffer);

		EHCI::Private::CreateQueueHead(virtAList, (dword)virtAList, SetupQTD, DevAddr, EndpointNum, 64);
		EHCI::Private::PerformTransaction((dword)virtAList);

		if ((((EHCI::Private::QTD*)SetupQTD)->Token.Status) != 0)
		{
			Video::Print("\nQTD Status: ");
			Video::PrintNumber(((EHCI::Private::QTD*)SetupQTD)->Token.Status, 10);
			Video::NewLine();
		}

		if (((EHCI::Private::QTD*)SetupQTD)->Token.Status != 0)
		{
			Video::Print("\nTransaction Error!!\n");
			Heap::Free((void*)SetupBuffer);
			Heap::Free((void*)virtAList);
			Heap::Free((void*)HandShakeQTD);
			Heap::Free((void*)SetupQTD);
			return 0;
		}

		Heap::Free((void*)SetupBuffer);
		Heap::Free((void*)virtAList);
		Heap::Free((void*)HandShakeQTD);
		Heap::Free((void*)SetupQTD);
		return 1;
	}

	void* HandShakeQTD = 0;
	if (direction == IN)
	{
		HandShakeQTD = EHCI::Private::CreateIOQTD(0, OUT, 1, 0, &DataBuffer);
	}
	else
	{
		HandShakeQTD = EHCI::Private::CreateIOQTD(0, IN, 1, 0, &DataBuffer);
	}
	Heap::Free((void*)DataBuffer);
	void* DataQTD = EHCI::Private::CreateIOQTD((dword)HandShakeQTD, direction, 1, BytesToTransfer, &DataBuffer);
	if (direction == OUT)
	{
		memcpy((void*)DataBuffer, Data, BytesToTransfer);
	}
	void* SetupQTD = EHCI::Private::CreateSetupQTD((dword)DataQTD, 0, 8, RequestType, Request, ValueLow, ValueHi, Index, BytesToTransfer, &SetupBuffer);

	EHCI::Private::CreateQueueHead(virtAList, (dword)virtAList, SetupQTD, DevAddr, EndpointNum, 64);
	EHCI::Private::PerformTransaction((dword)virtAList);

	if (direction == IN)
	{
		memcpy(Data, (void*)DataBuffer, BytesToTransfer);
	}

	if ((((EHCI::Private::QTD*)SetupQTD)->Token.Status) != 0)
	{
		Video::Print("\nQTD Status: ");
		Video::PrintNumber(((EHCI::Private::QTD*)SetupQTD)->Token.Status, 10);
		Video::NewLine();
	}

	if (((EHCI::Private::QTD*)SetupQTD)->Token.Status != 0)
	{
		Video::Print("\nTransaction Error!!\n");
		Heap::Free((void*)SetupBuffer);
		Heap::Free((void*)DataBuffer);
		Heap::Free((void*)virtAList);
		Heap::Free((void*)HandShakeQTD);
		Heap::Free((void*)DataQTD);
		Heap::Free((void*)SetupQTD);
		return 0;
	}

	Heap::Free((void*)SetupBuffer);
	Heap::Free((void*)DataBuffer);
	Heap::Free((void*)virtAList);
	Heap::Free((void*)HandShakeQTD);
	Heap::Free((void*)DataQTD);
	Heap::Free((void*)SetupQTD);
	return 1;
}