EHCI Driver Problems With VirtualBox

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
Hoozim
Member
Member
Posts: 53
Joined: Fri Jul 23, 2010 8:26 am

EHCI Driver Problems With VirtualBox

Post 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
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: EHCI Driver Problems With VirtualBox

Post 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
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Hoozim
Member
Member
Posts: 53
Joined: Fri Jul 23, 2010 8:26 am

Re: EHCI Driver Problems With VirtualBox

Post 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;
}
Post Reply