Page 1 of 1

IOApic & Lapic issues

Posted: Thu Nov 08, 2012 2:38 pm
by Anon5710
Hello,

I'm not really a OS developer (yet!), i'm an embedded software developer for my employer.
My Current task is implementing an APIC interrupt based system for windows compact 7. (wich isn't included ! )

I do realize that windows is frowend upon here but i'll be supplying my peace offer ... errr Source coude. :)

First, my understanding of IOAPIC and interrupts.
We have an IOAPIC and a local Apic Lapic, The IOAPIC is connected to diffrent external and internal interrupt sources. Whenever such an interrupt is triggerd (be edge or level) , the IOAPIC creates an interrupt message with an vector embedded in that message. The lapic detects these messages and if a valid destination is found, a cpu. That cpu jumps to that vector and start executing the specific ISR for that vector.

My problem : My cpu never jumps to any ISR and halts booting.


My attempt:
I query that mptabels and use that information to fill in the IOAPIC destination registers.
I use the same interrupt & vector logic as this pdf http://www.osdever.net/tutorials/pdf/apic.pdf.
I use the physical destination mode with the ID set to 0, so it should send interrupts to the BSP Cpu.
I believe i have enabeld both the APIC and the lapic, byt still no succes :(

I do have some entries that appear twice or more in the mptabels ?

my redirection entries:

Code: Select all

19 0x00000000_00010000   ----> 0x00000000_0000A0B1
19 0x00000000_00010000   ----> 0x00000000_0000A0B1
18 0x00000000_00010000   ----> 0x00000000_0000A0B9
16 0x00000000_00010000   ----> 0x00000000_0000A0B1
23 0x00000000_00010000   ----> 0x00000000_0000A0B9
23 0x00000000_00010000   ----> 0x00000000_0000A0B9
19 0x00000000_00010000   ----> 0x00000000_0000A0B9
16 0x00000000_00010000   ----> 0x00000000_0000A0B9
19 0x00000000_00010000   ----> 0x00000000_0000A0B9
16 0x00000000_00010000   ----> 0x00000000_0000A0B9
0  0x00000000_00010000   ----> 0x00000000_000000D9
1  0x00000000_00010000   ----> 0x00000000_000000D1
2  0x00000000_00010000   ----> 0x00000000_000000C9
3  0x00000000_00010000   ----> 0x00000000_00000081
4  0x00000000_00010000   ----> 0x00000000_00000079
5  0x00000000_00010000   ----> 0x00000000_00000071
6  0x00000000_00010000   ----> 0x00000000_00000069
7  0x00000000_00010000   ----> 0x00000000_00000061
8  0x00000000_00010000   ----> 0x00000000_000000C1
11  0x00000000_00010000   ----> 0x00000000_000000A9
12  0x00000000_00010000   ----> 0x00000000_000000A1
13  0x00000000_00010000   ----> 0x00000000_00000099
14  0x00000000_00010000   ----> 0x00000000_00000091
15  0x00000000_00010000   ----> 0x00000000_00000089
9  0x00000000_00010000   ----> 0x00000000_000000B9
10  0x00000000_00010000   ----> 0x00000000_000000B1
17 0x00000000_00010000   ----> 0x00000000_0000A051
20 0x00000000_00010000   ----> 0x00000000_0000A039
21 0x00000000_00010000   ----> 0x00000000_0000A031
22 0x00000000_00010000   ----> 0x00000000_0000A029
Can anyone spot something off in my code ?
Or the reason why i don't fall in my interrupt ?

My complete debug output (printing various apic lapic registers ) can be found in pgxapic.h

Any help will be greatly appriciated !
Regards Anon5710

Re: Apic issues

Posted: Thu Nov 08, 2012 2:39 pm
by Anon5710

Code: Select all

#include <windows.h>

#ifdef __cplusplus
extern "C" {
#include "pci.h"
#include <pc.h>
#include <oalintr.h>
#include <nkintr.h>
#include <oal.h>
#include <msr.h>



#endif  // __cplusplus

#define LPC_SIZE 0xf3
#define RCBA_SIZE 0x341F
#define OIC_OFFSET 0x31FF
#define APIC_INDEX	0x00
#define APIC_DATA	0x10

#define APIC_ID_REG 0x00
#define APIC_ADRESS 0xFEC00000
#define APIC_VER_REG 0x01
#define APIC_REDIRECTION_REG 0x10
#define LAPIC_SIZE	0x3F0
#define VECTOR_OFFSET 0x21



typedef struct
{				
  USHORT VendorID;				//0-1
  USHORT DeviceID;				//2-3
  USHORT Command;				//4-5 
  USHORT Status;				//6-7
  UCHAR RevisionID;				//8
  UCHAR ProgIf;					//9
  UCHAR SubClass;				//10
  UCHAR BaseClass;				//11
  UCHAR CacheLineSize;			//12
  UCHAR LatencyTimer;			//13
  UCHAR HeaderType;				//14 
  UCHAR DeviceSpecific[225];	//15-239
  DWORD rcba;					//240-243
}
PciConfig_Reg;

//MP Floating Pointer Structure.
typedef struct
{
	char signature[4];
	DWORD physaddr;
	BYTE length;
	BYTE spec_rev;
	BYTE checksum;
	BYTE feature1;
	BYTE feature2;
	BYTE feature3;
	BYTE feature4;
	BYTE feature5;
}
mpfloatstruct;


typedef struct
{
	char Signature[4];	//4
	WORD Length;		//6
	BYTE Revision;		//7
	BYTE CheckSum;		//8
	char OemID[8];		//16
	char ProductID[12];	//28
	DWORD OemTablePointer;	//32
	WORD OemTableSize;		//34
	WORD EntryCount;		//36
	DWORD LocalApicAddr;	//40
	WORD ExTableLength;		//42
	BYTE ExTableCheckSum;	//43
}
mpConfigHeader;

typedef struct
{
	BYTE EntryType;			//0
	BYTE LocalApicID;		//1
	BYTE LocalApicVersion;	//2
	BYTE CPUFlags;			//3
	BYTE StepModel;			//4
	BYTE Family;			//5
	WORD unused;			//6-7
	DWORD FeatureFlags;		//8-11
}
ProcessorEntry;

typedef struct
{
	BYTE EntryType;
	BYTE BusID;
	char BusString[6];
}
BusEntry;

typedef struct
{
	BYTE EntryType;
	BYTE ApicID;
	BYTE Version;
	BYTE ApicFlags;
	DWORD ApicAddr;
}
IOApicEntry;

typedef struct
{
	BYTE EntryType;
	BYTE IntType;
	WORD IntFlag;
	BYTE SourceBusID;
	BYTE SourceBusIRQ;
	BYTE DestApicID;
	BYTE DestApicPin;
}
InterruptEntry;

typedef struct
{
	BYTE EntryType;			//0
	BYTE InterruptType;		//1
	WORD PO_EL;				//2-3
	BYTE SourceBusID;		//4
	BYTE SourceBusIRQ;		//5
	BYTE DestLocalApicID;	//6
	BYTE LintIN;			//7
}
LocalInterrupt;


#define RCBA_REG_SIZE	 0x3420
#define OIC_OFFSET_SIZE  0x31FF
#define KBYTE	1024



BOOL CheckSum (const BYTE* pbData, DWORD cbLength);

void x86InitAPIC(void);
DWORD   PhysicalAddressToVirtual(DWORD, DWORD*);
DWORD ApicRead(DWORD* apic, BYTE index);
void ApicWrite(DWORD* apic, BYTE index, DWORD value );
void SetApic_std( DWORD* apic, BYTE index, BYTE vector);
void SetApic_pci( DWORD* apic, BYTE index, BYTE vector);
static ULONG pgxISR();
BOOL pgx_OALIntrInit (void);
void pgx_OALIntrMapInit(void);
void pgx_OALIntrStaticTranslate( UINT32 sysIntr,UINT32 irq );
void store_mptabels ( IOApicEntry* IoapicTable, InterruptEntry InterruptTable[25]  );
void print_debug(void);
void ConfigRedirection(InterruptEntry InterruptTable[25], BYTE PciMap[4], DWORD* apic);
void GetPciIRQ(BYTE PciMap[4]);

///////////////////////////////////
void EnableApic(void);
void map_irq_to_vector(DWORD *ptr);


static UINT32 g_oalSysIntr2Irq[SYSINTR_MAXIMUM];


static UINT32 g_oalIrq2SysIntr[OAL_INTR_IRQ_MAXIMUM];
BYTE g_oalSysIntrIntialized[SYSINTR_MAXIMUM];
UINT32 g_oalIrqSignaled[OAL_INTR_IRQ_MAXIMUM];



#define MYPOW(bit) ((1 << (bit)))


//////////////////////////////////////////
// Debug functions
void print_mptabels(void) ;

//////////////////////////////////













#ifdef __cplusplus

}
#endif  // __cplusplus

/*------------------MyLog--------------------------
OIC value : 0xFFFFFFF0
The internal IOxAPIC is enabeld (+address decode) 0xFFFFFFF1
0x2000000
0x2000000
19 0x00000000_00010000   ----> 0x00000000_0000A0B1
19 0x00000000_00010000   ----> 0x00000000_0000A0B1
18 0x00000000_00010000   ----> 0x00000000_0000A0B9
16 0x00000000_00010000   ----> 0x00000000_0000A0B1
23 0x00000000_00010000   ----> 0x00000000_0000A0B9
23 0x00000000_00010000   ----> 0x00000000_0000A0B9
19 0x00000000_00010000   ----> 0x00000000_0000A0B9
16 0x00000000_00010000   ----> 0x00000000_0000A0B9
19 0x00000000_00010000   ----> 0x00000000_0000A0B9
16 0x00000000_00010000   ----> 0x00000000_0000A0B9
0  0x00000000_00010000   ----> 0x00000000_000000D9
1  0x00000000_00010000   ----> 0x00000000_000000D1
2  0x00000000_00010000   ----> 0x00000000_000000C9
3  0x00000000_00010000   ----> 0x00000000_00000081
4  0x00000000_00010000   ----> 0x00000000_00000079
5  0x00000000_00010000   ----> 0x00000000_00000071
6  0x00000000_00010000   ----> 0x00000000_00000069
7  0x00000000_00010000   ----> 0x00000000_00000061
8  0x00000000_00010000   ----> 0x00000000_000000C1
11  0x00000000_00010000   ----> 0x00000000_000000A9
12  0x00000000_00010000   ----> 0x00000000_000000A1
13  0x00000000_00010000   ----> 0x00000000_00000099
14  0x00000000_00010000   ----> 0x00000000_00000091
15  0x00000000_00010000   ----> 0x00000000_00000089
9  0x00000000_00010000   ----> 0x00000000_000000B9
10  0x00000000_00010000   ----> 0x00000000_000000B1
17 0x00000000_00010000   ----> 0x00000000_0000A051
20 0x00000000_00010000   ----> 0x00000000_0000A039
21 0x00000000_00010000   ----> 0x00000000_0000A031
22 0x00000000_00010000   ----> 0x00000000_0000A029
0x00000000_FEE00900
 Task Pri Reg : 0x00000000
 Task Pri Reg : 0x00000000
 Local Apic ID Reg : 0x00000000
 Local Apic Ver Reg : 0x00050014
 Logical destination Reg : 0x00000000
 Spurious Interrupt Vector Reg : 0x0000010F
 Spurious Interrupt Vector Reg : 0x0000010F
 LVT CMCI reg : 0x00000000
Hookvector 0x0021
Hookvector 0x0029
Hookvector 0x0031
Hookvector 0x0039
Hookvector 0x0041
Hookvector 0x0049
Hookvector 0x0051
Hookvector 0x0059
Hookvector 0x0061
Hookvector 0x0069
Hookvector 0x0071
Hookvector 0x0079
Hookvector 0x0081
Hookvector 0x0089
Hookvector 0x0091
Hookvector 0x0099
Hookvector 0x00a1
Hookvector 0x00a9
Hookvector 0x00b1
Hookvector 0x00b9
Hookvector 0x00c1
Hookvector 0x00c9
Hookvector 0x00d1
Hookvector 0x00d9
------------------MyLog--------------------------*/

Code: Select all

#include "pgxapic.h"
//use only c construction !
//C Run time isn't fully initialised



//in this global array i define the vector for each IRQ (don't forget VECTOR_OFFSET!)
//i follow the standard priority
BYTE g_IRQ_to_vector[24] = {0xB8, 0xB0, 0xA8, 0x60, 0x58, 0x50, 0x48, 0x40, 0xA0, 0x98, 0x90, 0x88, 0x80, 0x78, 0x70, 0x68, 0x38, 0x30, 0x28, 0x20, 0x18, 0x10, 0x8, 0x0};


void x86InitAPIC( void )
{	
	NKOutputDebugString (TEXT("+x86InitAPIC\r\n"));
	OALMSG(TRUE , (L"------------------MyLog--------------------------\r\n"));

	/////////////////////////////////////////////////////////////////
	//Step1-6 : config IOAPIC
	//Step1 : get config information from mptabels
	//This won't work when there are more apic tabels !
	IOApicEntry IoapicTable;
	InterruptEntry InterruptTable[25];
	memset(  (char*)InterruptTable, '\0', sizeof(InterruptEntry)*25);
	store_mptabels ( &IoapicTable, InterruptTable  );

	//Step2 : get IRQ for INTA -> intD
	BYTE PciMap[4];
	GetPciIRQ( PciMap );

	
	//Step3 : Apic Enable 
    EnableApic();

    //Step4 memory mapping the APIC
	DWORD dwsize = 0x40;
	DWORD* apic = (DWORD* )PhysicalAddressToVirtual(APIC_ADRESS, &dwsize);
	
	//substep, write apicID
	OALMSG(TRUE , (L"0x%X\r\n",ApicRead(apic,0) ));
	ApicWrite(apic, 0, ApicRead(apic,0) );
	OALMSG(TRUE , (L"0x%X\r\n",ApicRead(apic,0) ));


	//Step5 Configure the redirection entries
	ConfigRedirection(InterruptTable, PciMap, apic );

	//Step6 Map the remaining redirection entries that weren't specified by the MPtabel specs
	for(int i=0; i<24*2; i+=2)
	{
		if( ApicRead(apic, APIC_REDIRECTION_REG+i) == 0x00010000 )
		{
			if(i <= 30 )
			{
				SetApic_std( apic, i, VECTOR_OFFSET + g_IRQ_to_vector[i/2] );
			}
			else
			{
				SetApic_pci( apic, i, VECTOR_OFFSET +g_IRQ_to_vector[i/2] );
			}
		}
	}


	

	DWORD lpdwValHigh = 0;
	DWORD lpdwValLow = 0;
	NKrdmsr( 0x1B, &lpdwValHigh,  &lpdwValLow );
	lpdwValLow |= MYPOW(11);
	NKwrmsr( 0x1B, lpdwValHigh,  lpdwValLow );
	OALMSG(TRUE , (L"0x%08X_%08X\r\n",lpdwValHigh, lpdwValLow));

	
	dwsize = LAPIC_SIZE;
	char* Lapic = (char* )PhysicalAddressToVirtual(0xFEE00000, &dwsize);


	OALMSG(TRUE , (L" Task Pri Reg : 0x%08X\r\n",   *((DWORD*)(Lapic+0x80)) ));
	*((DWORD*)(Lapic+0x80)) &=  ~0xF0;
	OALMSG(TRUE , (L" Task Pri Reg : 0x%08X\r\n", *((DWORD*)(Lapic+0x80)) ));


	OALMSG(TRUE , (L" Local Apic ID Reg : 0x%08X\r\n", *((DWORD*)(Lapic+0x20)) ));
	OALMSG(TRUE , (L" Local Apic Ver Reg : 0x%08X\r\n", *((DWORD*)(Lapic+0x30)) ));

	OALMSG(TRUE , (L" Logical destination Reg : 0x%08X\r\n", *((DWORD*)(Lapic+0xD0)) ));
	OALMSG(TRUE , (L" Spurious Interrupt Vector Reg : 0x%08X\r\n", *((DWORD*)(Lapic+0xF0)) ));
	*((DWORD*)(Lapic+0xF0)) |= MYPOW(8);
	OALMSG(TRUE , (L" Spurious Interrupt Vector Reg : 0x%08X\r\n", *((DWORD*)(Lapic+0xF0)) ));
	OALMSG(TRUE , (L" LVT CMCI reg : 0x%08X\r\n", *((DWORD*)(Lapic+0x2F0)) ));



	for (i = VECTOR_OFFSET; i < VECTOR_OFFSET + 8*24; i+=8)
    {
		OALMSG(TRUE , (L"Hookvector 0x%04x\r\n",i ));
        HookInterrupt(i, (FARPROC)pgxISR);
    }



	OALMSG(TRUE , (L"------------------MyLog--------------------------\r\n"));
	OALMSG(TRUE, (L"-x86InitAPIC\r\n"));	
}


DWORD PhysicalAddressToVirtual(DWORD dwPhyAddr, DWORD* pdwSize)
{
    DWORD dwOffset;
    DWORD dwSize;
	DWORD dwAlt=dwPhyAddr;

    if (pdwSize == NULL)
    {
        return NULL;
    }
    // This offset is because NKCreateStaticMapping will shift 8 bits for making 
    // address is page aligned before allocate static memory, so we need to know
    // how many bytes will be offset, and then add it back before return.
    dwOffset = dwPhyAddr & 0xFF;
    OALMSG(OAL_MEMORY , (TEXT("AcpiStaticMapping(): Offset in Page is 0x%X!!!\r\n"),dwOffset));
    
    dwSize = *pdwSize + dwOffset;
    OALMSG(OAL_MEMORY , (TEXT("AcpiStaticMapping(): Size will be allocate is 0x%X!!!\r\n"), dwSize));
    
    *pdwSize = dwSize;
	
	if( (dwAlt & 0xfff) != 0 )
	{
		//page size in windows ce 7 = 4096 bytes
		//this means that adresses like 0x****_*X** will fail some checks and NKCreate Static mapping will fail !
		//So pagealigning with 12 bits !
		dwOffset = dwPhyAddr & 0xFFF;
		dwSize = *pdwSize + dwOffset;
		dwAlt = dwAlt & 0xFFFFF000;
		OALMSG(FALSE , (L"Not page-alligned ! Adjusting with extra offset\r\n"));
		return reinterpret_cast<DWORD>(NKCreateStaticMapping(dwAlt >> 8, dwSize)) + dwOffset;
	}
    return reinterpret_cast<DWORD>(NKCreateStaticMapping(dwPhyAddr >> 8, dwSize)) + dwOffset;
}

BOOL CheckSum (const BYTE* pbData, DWORD cbLength)
{
	
    BYTE bSum = 0;
    for (DWORD i = 0; i < cbLength; ++i)
	{
		bSum += *pbData++;
    }
    return bSum == 0;	//clever did not know that!
}

DWORD ApicRead(DWORD* apic, BYTE index)
{
	//tell the apic we want BYTE index
	*apic = index;
	//return the data from IOWIN
	return *((DWORD*)(((BYTE* )apic)+APIC_DATA));
}

void ApicWrite(DWORD* apic, BYTE index, DWORD value )
{
	//tell the apic we want BYTE index
	*apic = index;
	//write the datain  IOWIN
	*((DWORD*)(((BYTE* )apic)+APIC_DATA)) = value;

}


void SetApic_std( DWORD* apic, BYTE index, BYTE vector)
{
	//assume default settings
	DWORD high_redirection=0x0;
	DWORD low_redirection=0x00010000;

	//low_redirection = ApicRead(apic, APIC_REDIRECTION_REG+index );
	high_redirection = ApicRead(apic, APIC_REDIRECTION_REG+1+index);
	OALMSG(TRUE , (L"%d  0x%08X_%08X   ----> ",index/2,high_redirection,low_redirection));
	
	high_redirection &= ~MYPOW(31);
	high_redirection &= ~MYPOW(30);
    high_redirection &= ~MYPOW(29);
	high_redirection &= ~MYPOW(28);



	low_redirection &= ~MYPOW(16);		//unmasked
	low_redirection &= ~MYPOW(15);		//Edge triggerd
	low_redirection &= ~MYPOW(13);		//Active high
	low_redirection &= ~MYPOW(11);		//set physical ID
	low_redirection |= vector ;			//vector

	ApicWrite( apic, APIC_REDIRECTION_REG+index+1, high_redirection);
	ApicWrite( apic, APIC_REDIRECTION_REG+index, low_redirection);

	low_redirection = ApicRead(apic, APIC_REDIRECTION_REG+index );
	high_redirection = ApicRead(apic, APIC_REDIRECTION_REG+1+index);
	OALMSG(TRUE , (L"0x%08X_%08X\r\n",high_redirection,low_redirection));

};

void SetApic_pci( DWORD* apic, BYTE index, BYTE vector)
{
	//assume default settings
	DWORD high_redirection=0x0;
	DWORD low_redirection=0x00010000;

	//low_redirection = ApicRead(apic, APIC_REDIRECTION_REG+index );
	high_redirection = ApicRead(apic, APIC_REDIRECTION_REG+1+index);
	OALMSG(TRUE , (L"%d 0x%08X_%08X   ----> ",index/2, high_redirection,low_redirection));

    high_redirection &= ~MYPOW(31);
	high_redirection &= ~MYPOW(30);
    high_redirection &= ~MYPOW(29);
	high_redirection &= ~MYPOW(28);

	low_redirection &= ~MYPOW(16);		//unmasked
	low_redirection |= MYPOW(15);		//Level triggerd
	low_redirection |= MYPOW(13);		//Active Low
	low_redirection &= ~MYPOW(11);		//set physical ID
	low_redirection |= vector ;			//vector

	
	ApicWrite( apic, APIC_REDIRECTION_REG+index+1, high_redirection);
	ApicWrite( apic, APIC_REDIRECTION_REG+index, low_redirection);

	low_redirection = ApicRead(apic, APIC_REDIRECTION_REG+index );
	high_redirection = ApicRead(apic, APIC_REDIRECTION_REG+1+index);
	OALMSG(TRUE , (L"0x%08X_%08X\r\n",high_redirection,low_redirection));

};

void GetPciIRQ( BYTE PciMap[4])
{
	//How are the PCI interrupts mapped ?
	//check at offset 0x60 from the LPC register ()
	//LPC Register : Bus0, Device31,Function0

	//vars
	PciConfig_Reg pci_reg;
	char* ptr;

	//clearing structure
	memset(&pci_reg, '\0', sizeof(PciConfig_Reg));

	//get LPC register from PCI
	ULONG x = PCIReadBusData(0, 31, 0, ((void*)(&pci_reg)), 0, LPC_SIZE);

	ptr = (char*)(&pci_reg);

	PciMap[0] = *(ptr+0x60);
	PciMap[1] = *(ptr+0x61);	
	PciMap[2] = *(ptr+0x62);
	PciMap[3] = *(ptr+0x63);
	
}


void ConfigRedirection(InterruptEntry InterruptTable[25], BYTE PciMap[4],  DWORD* apic)
{
	int index=0;
	int vector =0;
	while(1)
	{
		//break case
		if(InterruptTable[index].IntType == 0 && InterruptTable[index].IntFlag == 0 && InterruptTable[index].SourceBusIRQ == 0 && InterruptTable[index].SourceBusID == 0){break;}
		//normal case
		if(InterruptTable[index].IntType == 0x0 || InterruptTable[index].IntType == 0x3  )
		{
			if(InterruptTable[index].IntFlag == 0x00) //fill in bus specification
			{
				if(InterruptTable[index].SourceBusID == 0x0 || InterruptTable[index].SourceBusID == 0x1 ) //pci settings
				{
					vector = VECTOR_OFFSET + g_IRQ_to_vector[ PciMap[(InterruptTable[index].SourceBusIRQ) & 0x03 ]];
					SetApic_pci( apic, InterruptTable[index].DestApicPin*2, vector);
				
				}
				else if( InterruptTable[index].SourceBusID == 0x2)	//isa settings
				{
					vector = VECTOR_OFFSET + g_IRQ_to_vector[InterruptTable[index].DestApicPin];
					SetApic_std( apic, InterruptTable[index].DestApicPin*2, vector);
					
				}
			}
			else if( InterruptTable[index].IntFlag == 0x0F) //fill pci settings
			{
				vector = VECTOR_OFFSET + g_IRQ_to_vector[ PciMap[(InterruptTable[index].SourceBusIRQ) & 0x03 ]];
				SetApic_pci( apic, InterruptTable[index].DestApicPin*2, vector);
			}
			else if( InterruptTable[index].IntFlag == 0x05) //fill ISA settings
			{
				vector = VECTOR_OFFSET + g_IRQ_to_vector[InterruptTable[index].DestApicPin];
				SetApic_std( apic, InterruptTable[index].DestApicPin*2, vector);
				
			}
			index++;
		}

	}
	
}



void EnableApic(void)
{
	//setting bit<0> Other Interrupt COntrol Register

	//first start enabling the IOAPIC
	//this is done by setting the Other Interrupt COntrol Register bit0
	//OIC address = RCBA + 0x31FF
	//rcba address : OFFFSET from LPC Register
	//LPC Register : Bus0, Device31,Function0

	//vars
	PciConfig_Reg pci_reg;
	DWORD dwsize = RCBA_SIZE;
	char* rcba = NULL;
	char* oic = NULL;

	//clearing structure
	memset(&pci_reg, '\0', sizeof(PciConfig_Reg));

	//get LPC register from PCI
	ULONG x = PCIReadBusData(0, 31, 0, ((void*)(&pci_reg)), 0, LPC_SIZE);

	//get rcba adress & memmory map this register.
	rcba = (char* )PhysicalAddressToVirtual(pci_reg.rcba, &dwsize);
	oic = rcba + OIC_OFFSET;
	OALMSG(TRUE, (L"OIC value : 0x%X\r\n",*oic));

	//enable IOAPIC & apic adress decode
	*oic |= 0x01;
	OALMSG(TRUE, (L"The internal IOxAPIC is enabeld (+address decode) 0x%X\r\n",*oic));
}




void print_mptabels (void )
{
	//parsing the complete mptabels --> get "ioapic->ApicAddr"
	//search in bios information for string _MP_
    const char szSig[] = "_MP_";
	DWORD pdwSize = 0x0FFFFF - 0x0E0000;	//from specs
	const char* psVirtStart = reinterpret_cast<const char *>(PhysicalAddressToVirtual(0x0E0000, &pdwSize));
	mpfloatstruct *ptr1 = NULL;

    // interate though the memory range looking for the _MP_ sig on 16byte boundries
    for (DWORD dwOffset = 0; dwOffset < pdwSize ; dwOffset += 16) 
    {
        if (0 == strncmp(psVirtStart + dwOffset, szSig, _countof(szSig) - 1))
		{
			OALMSG(TRUE , (L"	Found the MP floating structure.\r\n"));		
            ptr1 = (mpfloatstruct*)(psVirtStart + dwOffset);
			break;
		}
    }

	if(ptr1 != NULL)
	{
		OALMSG(TRUE , (L"	ptr->signature  :	%S\r\n", ptr1->signature  ));
		OALMSG(TRUE , (L"	ptr->physaddr   :	0x%X\r\n",ptr1->physaddr  ));
		OALMSG(TRUE , (L"	ptr->length		:	0x%X\r\n", ptr1->length  ));
		OALMSG(TRUE , (L"	ptr->spec_rev	:	0x%X\r\n", ptr1->spec_rev  ));
		OALMSG(TRUE , (L"	ptr->checksum	:	0x%X\r\n", ptr1->checksum  ));
		OALMSG(TRUE , (L"	feature1		:	0x%X\r\n", ptr1->feature1  ));
		OALMSG(TRUE , (L"	feature2		:	0x%X\r\n", ptr1->feature2  ));
		OALMSG(TRUE , (L"	feature3		:	0x%X\r\n", ptr1->feature3  ));
		OALMSG(TRUE , (L"	feature4		:	0x%X\r\n", ptr1->feature4  ));
		OALMSG(TRUE , (L"	feature5		:	0x%X\r\n", ptr1->feature5  ));
	}
	if( CheckSum((BYTE*)ptr1, ptr1->length*16) != TRUE )
	{
		OALMSG(TRUE , (L"CheckSum Failed !\r\n\r\n"));
	}
	else
	{
		OALMSG(TRUE , (L"CheckSum OK !\r\n\r\n"));
	};


	mpConfigHeader* ptr2 = NULL;
	pdwSize = sizeof(mpConfigHeader); 
	ptr2 = (mpConfigHeader*)(PhysicalAddressToVirtual(ptr1->physaddr, &pdwSize ));
	
	//now update with real length
	pdwSize = ptr2->Length; 
	ptr2 = (mpConfigHeader*)(PhysicalAddressToVirtual(ptr1->physaddr, &pdwSize ));


	OALMSG(TRUE , (L"	Found the MP Configuration Table Header.\r\n"));
	OALMSG(TRUE , (L"	ptr->Signature			:	%S\r\n", ptr2->Signature  ));
	OALMSG(TRUE , (L"	ptr->Length				:	0x%X\r\n", ptr2->Length  ));
	OALMSG(TRUE , (L"	ptr->Revision			:	0x%X\r\n", ptr2->Revision  ));
	OALMSG(TRUE , (L"	ptr->CheckSum			:	0x%X\r\n", ptr2->CheckSum  ));
	OALMSG(TRUE , (L"	ptr->OemId				:	%S\r\n", ptr2->OemID  ));
	OALMSG(TRUE , (L"	ptr->ProductID			:	%S\r\n", ptr2->ProductID  ));
	OALMSG(TRUE , (L"	ptr->OemTableptr		:	0x%X\r\n", ptr2->OemTablePointer  ));
	OALMSG(TRUE , (L"	ptr->OemTableSize		:	0x%X\r\n", ptr2->OemTableSize  ));
	OALMSG(TRUE , (L"	ptr->EntryCount			:	0x%X\r\n", ptr2->EntryCount  ));
	OALMSG(TRUE , (L"	ptr->localAPicAddr		:	0x%X\r\n", ptr2->LocalApicAddr  ));
	OALMSG(TRUE , (L"	ptr->ExTableLength		:	0x%X\r\n", ptr2->ExTableLength  ));
	OALMSG(TRUE , (L"	ptr->ExTableCheckSum	:	0x%X\r\n", ptr2->ExTableCheckSum ));
	
	if( CheckSum((BYTE*)ptr2, ptr2->Length) != TRUE )
	{
		OALMSG(TRUE , (L"CheckSum Failed !\r\n\r\n"));
	}
	else
	{
		OALMSG(TRUE , (L"CheckSum OK !\r\n\r\n"));
	};
	
	//cast ptr2 as char pointer and add mpconfigheader offset to it
	BYTE* ptr3 = (BYTE*)(((char*)(ptr2)) + sizeof(mpConfigHeader));

	DWORD address=0;

	OALMSG(TRUE , (L"Parsing the entry field !\r\n"));
	OALMSG(TRUE , (L"-----------------------------------\r\n"));
	for(int i =0; i< ptr2->EntryCount; i++)
	{
		if(*ptr3 == 0x00)
		{
			OALMSG(TRUE , (L"Found processor desciption\r\n"));
			ProcessorEntry* processor = (ProcessorEntry*)ptr3;
			OALMSG(TRUE , (L"	Entry type	:	0x%X\r\n",processor->EntryType));
			OALMSG(TRUE , (L"	LocalApicID	:	0x%X\r\n",processor->LocalApicID));
			OALMSG(TRUE , (L"	LocalApicVersion	:	0x%X\r\n",processor->LocalApicVersion));
			OALMSG(TRUE , (L"	CPU Flags	:	0x%X\r\n",processor->CPUFlags));
			OALMSG(TRUE , (L"	StepModel	:	0x%X\r\n",processor->StepModel));
			OALMSG(TRUE , (L"	Family	:	0x%X\r\n",processor->Family));
			OALMSG(TRUE , (L"	FeatureFlags	:	0x%X\r\n",processor->FeatureFlags));
			ptr3 += 20;
		}
		else if( *ptr3 == 0x01 )
		{
			OALMSG(TRUE , (L"Found bus desciption\r\n"));
			BusEntry* bus = (BusEntry*)ptr3;
			OALMSG(TRUE , (L"	Entry type	:	0x%X\r\n",bus->EntryType));
			OALMSG(TRUE , (L"	Bus ID	:	0x%X\r\n",bus->BusID));
			OALMSG(TRUE , (L"	Bus String	:	%S\r\n",bus->BusString));
			ptr3 += 8;
		}
		else if( *ptr3 == 0x02 )
		{
			OALMSG(TRUE , (L"Found IOAPIC desciption\r\n"));
			IOApicEntry* ioapic = (IOApicEntry*)ptr3;
			OALMSG(TRUE , (L"	Entry type	:	0x%X\r\n",ioapic->EntryType));
			OALMSG(TRUE , (L"	Apic ID		:	0x%X\r\n",ioapic->ApicID));
			OALMSG(TRUE , (L"	Version		:	0x%X\r\n",ioapic->Version));
			OALMSG(TRUE , (L"	Apic Flags	:	0x%X\r\n",ioapic->ApicFlags));
			OALMSG(TRUE , (L"	Apic Adrr	:	0x%X\r\n\r\n",ioapic->ApicAddr));
			address = ioapic->ApicAddr;
			ptr3 += 8;
		}
		else if( *ptr3 == 0x03 )
		{
			OALMSG(TRUE , (L"Found IO Interrupt assignment desciption\r\n"));
			InterruptEntry* IntEntry = (InterruptEntry*)ptr3;
			OALMSG(TRUE , (L"	Entry type		:	0x%X\r\n",IntEntry->EntryType));
			OALMSG(TRUE , (L"	Interrupt type		:	0x%X\r\n",IntEntry->IntType));
			OALMSG(TRUE , (L"	Interrupt Flag		:	0x%X\r\n",IntEntry->IntFlag));
			OALMSG(TRUE , (L"	Source Bus ID		:	0x%X\r\n",IntEntry->SourceBusID));
			OALMSG(TRUE , (L"	Source Bus IRQ		:	0x%X\r\n",IntEntry->SourceBusIRQ));
			OALMSG(TRUE , (L"	Destination ApicID  :	0x%X\r\n",IntEntry->DestApicID));
			OALMSG(TRUE , (L"	Destination ApicPin :	0x%X\r\n\r\n",IntEntry->DestApicPin));

			ptr3 += 8;
		}
		else if( *ptr3 == 0x04 )
		{
			OALMSG(TRUE , (L"Found Local Interrupt assignment desciption\r\n"));
			LocalInterrupt* localint = (LocalInterrupt*)ptr3;
			OALMSG(TRUE , (L"	Entry type	:	0x%X\r\n",localint->EntryType));
			OALMSG(TRUE , (L"	Interrupt type	:	0x%X\r\n",localint->InterruptType));
			OALMSG(TRUE , (L"	PO_EL	:	0x%X\r\n",localint->PO_EL));
			OALMSG(TRUE , (L"	SourceBusID	:	0x%X\r\n",localint->SourceBusID));
			OALMSG(TRUE , (L"	SourceBusIRQ	:	0x%X\r\n",localint->SourceBusIRQ));
			OALMSG(TRUE , (L"	DestLocalApicID	:	0x%X\r\n",localint->DestLocalApicID));
			OALMSG(TRUE , (L"	SourceBusID	:	0x%X\r\n",localint->LintIN));
			ptr3 += 8;
		}
	}
}











void store_mptabels ( IOApicEntry* IoapicTable, InterruptEntry InterruptTable[25]  )
{
	//parsing the complete mptabels --> get "ioapic->ApicAddr"
	//search in bios information for string _MP_
    const char szSig[] = "_MP_";
	DWORD pdwSize = 0x0FFFFF - 0x0E0000;	//from specs
	const char* psVirtStart = reinterpret_cast<const char *>(PhysicalAddressToVirtual(0x0E0000, &pdwSize));
	mpfloatstruct *ptr1 = NULL;

    // interate though the memory range looking for the _MP_ sig on 16byte boundries
    for (DWORD dwOffset = 0; dwOffset < pdwSize ; dwOffset += 16) 
    {
        if (0 == strncmp(psVirtStart + dwOffset, szSig, _countof(szSig) - 1))
		{
            ptr1 = (mpfloatstruct*)(psVirtStart + dwOffset);
			break;
		}
    }

	if( CheckSum((BYTE*)ptr1, ptr1->length*16) != TRUE )
	{
		OALMSG(TRUE , (L"CheckSum Failed !\r\n\r\n"));
	}
	
	mpConfigHeader* ptr2 = NULL;
	pdwSize = sizeof(mpConfigHeader); 
	ptr2 = (mpConfigHeader*)(PhysicalAddressToVirtual(ptr1->physaddr, &pdwSize ));
	
	//now update with real length
	pdwSize = ptr2->Length; 
	ptr2 = (mpConfigHeader*)(PhysicalAddressToVirtual(ptr1->physaddr, &pdwSize ));

	if( CheckSum((BYTE*)ptr2, ptr2->Length) != TRUE )
	{
		OALMSG(TRUE , (L"CheckSum Failed !\r\n\r\n"));
	}
	
	//cast ptr2 as char pointer and add mpconfigheader offset to it
	BYTE* ptr3 = (BYTE*)(((char*)(ptr2)) + sizeof(mpConfigHeader));

	DWORD address=0;

	int INT_index=0;
	for(int i =0; i< ptr2->EntryCount; i++)
	{
		if(*ptr3 == 0x00)
		{
			ProcessorEntry* processor = (ProcessorEntry*)ptr3;
			ptr3 += 20;
		}
		else if( *ptr3 == 0x01 )
		{
			
			BusEntry* bus = (BusEntry*)ptr3;
			ptr3 += 8;
		}
		else if( *ptr3 == 0x02 )
		{
			
			IOApicEntry* ioapic = (IOApicEntry*)ptr3;
			
			IoapicTable->EntryType = ioapic->EntryType;
			IoapicTable->ApicID = ioapic->ApicID;
			IoapicTable->Version = ioapic->Version;
			IoapicTable->ApicFlags = ioapic->ApicFlags;
			IoapicTable->ApicAddr = ioapic->ApicAddr;



			ptr3 += 8;
		}
		else if( *ptr3 == 0x03 )
		{
			InterruptEntry* IntEntry = (InterruptEntry*)ptr3;
			
			InterruptTable[INT_index].EntryType = IntEntry->EntryType;
			InterruptTable[INT_index].IntType = IntEntry->IntType;
			InterruptTable[INT_index].IntFlag = IntEntry->IntFlag;
			InterruptTable[INT_index].SourceBusID = IntEntry->SourceBusID;
			InterruptTable[INT_index].SourceBusIRQ = IntEntry->SourceBusIRQ;
			InterruptTable[INT_index].DestApicID = IntEntry->DestApicID;
			InterruptTable[INT_index].DestApicPin = IntEntry->DestApicPin;
			INT_index++;

			ptr3 += 8;
		}
		else if( *ptr3 == 0x04 )
		{
			
			LocalInterrupt* localint = (LocalInterrupt*)ptr3;
			ptr3 += 8;
		}
	}
}


	

void print_debug(void)
{
	OALMSG(TRUE, (L"----------------------------------------------\r\n"));
	for (int i = 0; i < SYSINTR_MAXIMUM; i++)
    {
		OALMSG(TRUE, (L"	SYSINTR(%d) to IRQ(%d)\r\n",i,g_oalSysIntr2Irq[i]));
	}
	OALMSG(TRUE, (L"----------------------------------------------\r\n"));
    for (int i = 0; i < OAL_INTR_IRQ_MAXIMUM; i++)
    {
		OALMSG(TRUE, (L"	IRQ(%d) to SYSINTR(%d)\r\n",i,g_oalIrq2SysIntr[i]));
    }
	OALMSG(TRUE, (L"----------------------------------------------\r\n"));
}



static ULONG pgxISR()
{
	OALMSG(TRUE , (L"------------------ ISR --------------------------\r\n"));
	return 0;
}


Re: IOApic & Lapic issues

Posted: Fri Nov 09, 2012 1:38 am
by Brendan
Hi,

This is too long and confusing (e.g. I have no idea what "19 0x00000000_00010000 ----> 0x00000000_0000A0B1" is meant to mean and I don't necessarily want to trawl through many lines of code looking for many "needles in a haystack").

First, it is safe to assume that the tutorial you're using is completely broken and unusable. It's incomplete (e.g. doesn't mention MP or ACPI at all, doesn't explain APIC IDs, etc), relies on many bad assumptions (e.g. assumes there's only one IO APIC with 24 inputs, makes assumptions about what is connected to each IO APIC input, etc), has some completely wrong information (e.g. how PCI interrupts work) and suggests some completely stupid things (e.g. probing the chipset to determine IRQ routing info, messing with an IMCR that probably doesn't exist, etc).

Second, forget about touching any hardware until you've extracted all information from ACPI's "MADT/APIC" table or the MP Spec table; and displayed this information and verified its correctness. This includes information about which APIC IDs correspond to what, what the local APIC's physical address is, how many IO APICs exist, what each IO APIC input is used for (if/where possible), etc. Note that the MP Spec table is mostly obsolete (and may be either missing or wrong on some motherboards) and that you should't attempt to look at the MP Spec table unless ACPI's "MADT/APIC" table doesn't exist.

Third, disable all sources of IRQs from the PIC (if you haven't already) and all IO APICs.

Fourth, forget about the IO APIC's IRQs until after you've got IPIs working. If you know IPIs work, then you know that local APIC/s are configured correctly and that your APIC IDs are sane and that you haven't done something silly (like leaving interrupts disabled in EFLAGS).

Fifth, only worry about configuring individual IO APIC IRQs when you require them. For example, whichever IO APIC input that happens to correspond to the serial ports (or the floppy controller or parallel ports or hard disk controller or whatever) should remain disabled until your OS is actually starting a device driver for the serial ports (or the floppy controller or ...).


Cheers,

Brendan

Re: IOApic & Lapic issues

Posted: Fri Nov 09, 2012 9:27 am
by Anon5710
Alright, i'm glad you answered Brendan, you seem to know allot about the apic ;)

My MADT tabels output:

Code: Select all

-------------------Printing APIC description table @ adress a0044880 --------------------------
                   signature = APIC
                   dwLength  = 104
                   Checksum  = 201
                   OemId  = IntelRAWRDACPI1.0BAWRD   //my debug printer is messing these strings up the checksum is okey !
                   ManufacturerId  = AWRDACPI1.0BAWRD
                   OemRev  = 42302e31
                   Create  = AWRD
                   CreateRev = 0
------------------------------------------------------------------------------------------
Apic Local Interrupt controller Address : 0xFEE00000
Apic Flags : 0x00000001

-------------------Printing Processor Local APIC description table @ adress a00448ac --------------------------
        ACPI Processor ID : 0x0
        APIC ID : 0x0
        Flags: 0x1
------------------------------------------------------------------------------------------

-------------------Printing Processor Local APIC description table @ adress a00448b4 --------------------------
        ACPI Processor ID : 0x1
        APIC ID : 0x1
        Flags: 0x0
------------------------------------------------------------------------------------------

-------------------Printing IO APIC description table @ adress a00448bc --------------------------
        IO APIC ID : 0x2
        IO APIC address : 0xFEC00000
        Global System Interrupt Base: 0x0
------------------------------------------------------------------------------------------

-------------------Printing Interrupt Source Override description table @ adress a00448c8 --------------------------
        Bus : 0x0
        Source : 0x0
        Global System Interrupt: 0x2
        Flags: 0x0
------------------------------------------------------------------------------------------

-------------------Printing Interrupt Source Override description table @ adress a00448d2 --------------------------
        Bus : 0x0
        Source : 0x9
        Global System Interrupt: 0x9
        Flags: 0xD
------------------------------------------------------------------------------------------

-------------------Printing Local APIC NMI description table @ adress a00448dc --------------------------
        ACPI Processor ID : 0x0
        Flags : 0x100
        Local Apic LINT# : 0x4
------------------------------------------------------------------------------------------

-------------------Printing Local APIC NMI description table @ adress a00448e2 --------------------------
        ACPI Processor ID : 0x1
        Flags : 0x100
        Local Apic LINT# : 0xFF
------------------------------------------------------------------------------------------
Can you explain the Local APIC NMI settings for me ?
I think i understand the rest :)

Regards Anon5710

Re: IOApic & Lapic issues

Posted: Fri Nov 09, 2012 12:42 pm
by Brendan
Anon5710 wrote:My MADT tabels output:

Code: Select all

-------------------Printing Local APIC NMI description table @ adress a00448dc --------------------------
        ACPI Processor ID : 0x0
        Flags : 0x100
        Local Apic LINT# : 0x4
------------------------------------------------------------------------------------------

-------------------Printing Local APIC NMI description table @ adress a00448e2 --------------------------
        ACPI Processor ID : 0x1
        Flags : 0x100
        Local Apic LINT# : 0xFF
------------------------------------------------------------------------------------------
Can you explain the Local APIC NMI settings for me ?
They look broken (and not just because several fields are much smaller than they should be - e.g. 4-bit APIC IDs and 12-bit flags).

According to the spec for ACPI 5.0, bits 4 to 15 of flags are still "reserved, must be zero" so they're both likely to be wrong. For the first entry "LINT# : 0x4" doesn't exist (there are only 2 "LINT#" inputs). For the second "LINT# : 0xFF" doesn't exist either (and 0xFF is not short-hand for "all of them").

I would assume that the offsets for the fields are off by one. For example, the value 0x04 from "LINT# : 0x4" is the first byte of the next entry and the value 0xFF from "LINT#: 0xFF" is the byte after the end of the MADT/APIC table. This would imply that the flags were actually meant to be 0x00?? in both cases, and that "LINT#" was meant to be 0x01 in both cases.


Cheers,

Brendan

Re: IOApic & Lapic issues

Posted: Fri Nov 09, 2012 2:49 pm
by gerryg400
I suspect that you are not 'packing' the structures in your code. I don't know which compiler you use but usually a "#pragma packed" or an "__attribute__ ((packed))" is required when your structures need to map perfectly onto external data or hardware.

Note that often leaving this out won't matter but in the case of the Local APIC NMI structure it will matters because the structure defined by the ACPI spec. does not follow 'standard' C packing rules.

Re: IOApic & Lapic issues

Posted: Mon Nov 12, 2012 6:38 am
by Anon5710
gerryg400 wrote:I suspect that you are not 'packing' the structures in your code. I don't know which compiler you use but usually a "#pragma packed" or an "__attribute__ ((packed))" is required when your structures need to map perfectly onto external data or hardware.
You were right, some structure's were 2 bytes to large ! This resulted in other NMI structures.

Code: Select all

4294767296 PID:0 TID:2 -------------------Printing Local APIC NMI description table @ adress a00648dc --------------------------
4294767296 PID:0 TID:2 	ACPI Processor ID : 0x0
4294767296 PID:0 TID:2 	Flags : 0x100
4294767296 PID:0 TID:2 	Local Apic LINT# : 0x4
4294767296 PID:0 TID:2 ------------------------------------------------------------------------------------------

4294767296 PID:0 TID:2 -------------------Printing Local APIC NMI description table @ adress a00648e2 --------------------------
4294767296 PID:0 TID:2 	ACPI Processor ID : 0x1
4294767296 PID:0 TID:2 	Flags : 0x100
4294767296 PID:0 TID:2 	Local Apic LINT# : 0x0
4294767296 PID:0 TID:2 ------------------------------------------------------------------------------------------
I still don't get what i'm suposed to do with this information.

edit: does this mean i have to write a ~(masking vector) to the "LVT LINT0 Register (FEE0 0350H)" since my local apic 0x0 is connected to the "Local Apic LINT# : 0x4"
(this is just based upon the fact that LVT LINT0 is the 4 entry in my datasheets)

What about the second entry ? I think this is strange since the cpu entries tell me not to use the second cpu. (flag = 0 )

About ipis,
How do they relate to a single core platform ? (whats there function ?)
How can i verify that i can send ipi's? (to itself ?)

Re: IOApic & Lapic issues

Posted: Mon Nov 12, 2012 7:20 am
by Brendan
Hi,
Anon5710 wrote:You were right, some structure's were 2 bytes to large ! This resulted in other NMI structures.
And these new structures look just as wrong as the old ones (reserved flags that should be zero are set, and local APIC inputs that don't exist). Post the structures you're using and/or a hex dump of the raw bytes at 0xA00648DC.
Anon5710 wrote:I still don't get what i'm suposed to do with this information.
The structures probably should be telling you that the second local APIC input (LINT# : 0x01) of each CPU are connected to NMI and need to be configured as such.
Anon5710 wrote:About ipis,
How do they relate to a single core platform ? (whats there function ?)
IPIs aren't as useful on single-CPU; but can still be used for some things. For example; they could be used for deferred procedure calls; where instead of executing something you send an IPI to yourself and execute something when the IPI is received.
Anon5710 wrote:How can i verify that i can send ipi's? (to itself ?)
By sending an IPI to yourself and checking that the corresponding interrupt handler is invoked.


Cheers,

Brendan

Re: IOApic & Lapic issues

Posted: Mon Nov 12, 2012 9:00 am
by Anon5710
My bad, fixed it !

Code: Select all

4294767296 PID:0 TID:2 -------------------Printing Local APIC NMI description table @ adress a00648dc --------------------------
4294767296 PID:0 TID:2 	ACPI Processor ID : 0x0
4294767296 PID:0 TID:2 	Flags : 0x5
4294767296 PID:0 TID:2 	Local Apic LINT# : 0x1
4294767296 PID:0 TID:2 ------------------------------------------------------------------------------------------

4294767296 PID:0 TID:2 -------------------Printing Local APIC NMI description table @ adress a00648e2 --------------------------
4294767296 PID:0 TID:2 	ACPI Processor ID : 0x1
4294767296 PID:0 TID:2 	Flags : 0x5
4294767296 PID:0 TID:2 	Local Apic LINT# : 0x1
4294767296 PID:0 TID:2 ------------------------------------------------------------------------------------------
i'am confused as how an ioapic interrupt gets handled.
I assumed that each ioapic input has its own vector (so each redirection entry in the ioapic has a diffrent vector) and the lapic would send this vector to the cpu.

OR

As i understand it right now, all the IOAPIC interrupts are delivered in the localAPIC LINT 0x1. So i should setup a vector in LVT1 entry (say 0x60).
Hook only this interrupt with the kernel with HookInterrupt() (i guess this is windows specific). So this means that i have to check (in my interrupt handler) which interrupt has fired.
But what do i set for vectors in the IOAPIC redirection fields ?

As for delivery mode, i suppose that 000 (fixed) is best used ?

Re: IOApic & Lapic issues

Posted: Mon Nov 12, 2012 10:42 am
by Brendan
Hi,
Anon5710 wrote:i'am confused as how an ioapic interrupt gets handled.
I assumed that each ioapic input has its own vector (so each redirection entry in the ioapic has a diffrent vector) and the lapic would send this vector to the cpu.
Yes. More specifically, when the IO APIC detects an IRQ has occurred it sends a message/packet to all local APICs. The message/packet contains various information (if it's level/edge triggered, which vector, which destination, etc). Each local APIC receives this message/packet and determines if it should accept it or not (based on the destination and other information). If the local APIC accepts the message/packet; it sets the corresponding flag in it's "Interrupt received register" (IRR). When the local APIC can (which depends on TPR, other pending interrupts, etc), it clears the flag in the IRR and sets the corresponding flag in the "In server register" (ISR) and tells the CPU about it.
Anon5710 wrote:As i understand it right now, all the IOAPIC interrupts are delivered in the localAPIC LINT 0x1. So i should setup a vector in LVT1 entry (say 0x60).
No. LINT 0x01 is typically used for NMI, and NMIs are completely separate and have nothing to do with normal IRQs.

For some motherboards, LINT 0x00 is used for "extInt", which is a connection to the legacy PIC chips. In this case when the legacy PIC chips send an interrupt the local APIC notices (via. LINT 0x00) and waits for the PIC chip to send the vector, then delivers the interrupt to the CPU. Of course some motherboards do it differently (e.g. the PIC chips could be connected via. an IO APIC input instead).
Anon5710 wrote:Hook only this interrupt with the kernel with HookInterrupt() (i guess this is windows specific). So this means that i have to check (in my interrupt handler) which interrupt has fired.
No. There's (up to) 224 separate interrupt handlers - one for each vector. Only one interrupt source should use an interrupt vector, therefore the interrupt handler doesn't need to check anything to know which interrupt source triggered the interrupt. Of course (e.g. for PCI) the interrupt source might be the PCI host bridge, and the PCI host bridge may have allowed multiple PCI devices to share the same "PCI interrupt", so in that case you do need to ask each device driver that shares the "PCI interrupt" if its device was responsible.
Anon5710 wrote:But what do i set for vectors in the IOAPIC redirection fields ?
The vector you use determines the priority of the IRQ. Therefore for interrupts where latency isn't very important (e.g. floppy disk) you'd want to use lower interrupt vectors, and for interrupts where latency is important (e.g. network cards) you'd want to use higher interrupt vectors.

Interrupt vectors should be treated as a resource. What I mean is that you should be able to allocate them and free them. For example; a device driver might ask to allocate an interrupt vector with "priority n", and your "interrupt vector manager" would search for a free interrupt vector that is closest to the requested priority and allocate it. Note: once you start looking at MSI you'll also need to allocate/free sequential groups of interrupt vectors - e.g. a device driver might ask for a group of 8 sequential interrupt vectors.
Anon5710 wrote:As for delivery mode, i suppose that 000 (fixed) is best used ?
That depends on what it's used for. For normal IRQs, I like "send to lowest priority" as it can help to reduce IRQ latency (but may be impractical in some cases).


Cheers,

Brendan

Re: IOApic & Lapic issues

Posted: Tue Nov 13, 2012 10:32 am
by Anon5710
Hello again,
Brendan wrote:By sending an IPI to yourself and checking that the corresponding interrupt handler is invoked.
I've been reading about multi processor support in windows 7 and it seems i'm in luck. It's already supported !
When i send a message to "IPI_TYPE_ALL_INCLUDE_SELF" my "IpiInterruptHandler" is called and executed.
Does this mean my lapic is initialized and configured correctly ? Or is is there some more work to be done ?
Brendan wrote: More specifically, when the IO APIC detects an IRQ has occurred it sends a message/packet to all local APICs. The message/packet contains various information (if it's level/edge triggered, which vector, which destination, etc).
Do these messages get send automatically whenever an interrupt in received on an IOAPIC pin ? Or is there an bit/setting that needs to be set/cleared before messages are send?
(i already had to enable bit<0> in the Other interrupt controle register (OIC Reg) (ICH7))
Brendan wrote: Each local APIC receives this message/packet and determines if it should accept it or not (based on the destination and other information).
What exactly is that "other information", i'm guessing you mean edge- or level-interrupt and ActiveLow or ActiveHigh.
Or do you mean the Task Priority register, and others ?
Brendan wrote: If the local APIC accepts the message/packet; it sets the corresponding flag in it's "Interrupt received register" (IRR). When the local APIC can (which depends on TPR, other pending interrupts, etc), it clears the flag in the IRR and sets the corresponding flag in the "In server register" (ISR) and tells the CPU about it.
This does happen in hardware right ?


Now i believe i have configured my IOAPIC correctly.
  • 1) Enable IOAPIC in the OIC register
    2) write the APIC ID (from MADT tables) to the IOAPIC ID Register
    3) Write the redirection entries 0-23, using information from the MADT tables
    4) unmask wanted redirection entries
For redirection entries that were not listed in the MADT tables, i set:
(entry 0-15 ) : Active High, edge triggered (ISA?)
(entry 16-23) : Active Low, level triggered (PCI)

I set each entry to physical destination with a fixed delivery mode, with my APICID = 0 (madt tables)

Resulting in these entries:

Code: Select all

4294767296 PID:0 TID:2 redirection entry 0: 0x00000000_00000021
4294767296 PID:0 TID:2 redirection entry 1: 0x00000000_00000022
4294767296 PID:0 TID:2 redirection entry 2: 0x00000000_00000023
4294767296 PID:0 TID:2 redirection entry 3: 0x00000000_00000024
.... //always increase vector with +1
4294767296 PID:0 TID:2 redirection entry 21: 0x00000000_0000A036
4294767296 PID:0 TID:2 redirection entry 22: 0x00000000_0000A037
4294767296 PID:0 TID:2 redirection entry 23: 0x00000000_0000A038

//special info in madt table for 2 below
4294767296 PID:0 TID:2 redirection entry 2: 0x00000000_0000A023
4294767296 PID:0 TID:2 redirection entry 9: 0x00000000_0000802A
I assume that these are good settings and that the IOAPIC automatically sends messages on the bus whenever an IOAPIC pin is triggerd.
If you see any flaws, please tell me :)


This is how the apic is setup after windows initializes MP support, i checked the registers and they don't seem to mask or disable the interrupts.
(task priority, spurious vector, arbitration, etc....)

Code: Select all

4294767296 PID:0 TID:2 ------------------ Printing local APIC Registers ------------------ 
4294767296 PID:0 TID:2 	id								: 0x00000000
4294767296 PID:0 TID:2 	Version							: 0x00050014
4294767296 PID:0 TID:2 	Task Prio Register				: 0x00000000
4294767296 PID:0 TID:2 	Arbitration prio Register		: 0x00000000
4294767296 PID:0 TID:2 	Processor prio Register			: 0x00000000
4294767296 PID:0 TID:2 	Local destination	: 0x00000000
4294767296 PID:0 TID:2 	Destination format Register		: 0xFFFFFFFF
4294767296 PID:0 TID:2 	Spurious Int vector Register	: 0x0000010F
4294767296 PID:0 TID:2 	Error status Register			: 0x00000000
4294767296 PID:0 TID:2 	Interrupt Command Register		: 0x00000000_000C0699
4294767296 PID:0 TID:2 	LVT Timer Register				: 0x00010000
4294767296 PID:0 TID:2 	LVT Thermal Register			: 0x00010000
4294767296 PID:0 TID:2 	LVT Performance Count			: 0x00010000
4294767296 PID:0 TID:2 	LVT Lint0 Register				: 0x00001700
4294767296 PID:0 TID:2 	LVT Lint1 Register				: 0x00000400
4294767296 PID:0 TID:2 	LVT Error Register				: 0x00010000
4294767296 PID:0 TID:2 	Timer Init Counter				: 0x00000000
4294767296 PID:0 TID:2 	Timer Curr Counter				: 0x00000000
4294767296 PID:0 TID:2 	Devide config Register			: 0x00000000
4294767296 PID:0 TID:2 --------------------------------------------------------------------- 

Last question,
On page(371) 10-30 chapter 10.8.4 Interrupt acceptance for fixed interrupts of the intel64 -IA32architecture

It says :
If the local APIC receives an interrupt with an interrupt-priority class higher than that of the interrupt currently in
service, and interrupts are enabled in the processor core, the local APIC dispatches the higher priority interrupt to
the processor immediately (without waiting for a write to the EOI register). The currently executing interrupt
handler is then interrupted so the higher-priority interrupt can be handled. When the handling of the higher-priority
interrupt has been completed, the servicing of the interrupted interrupt is resumed.
So i need to enable interrupts in the processor core as-well ? (cli and ... )


Anyways that's it for now, thanks for your help ! =D>

Re: IOApic & Lapic issues

Posted: Tue Nov 13, 2012 11:31 am
by Brendan
H,
Anon5710 wrote:
Brendan wrote:By sending an IPI to yourself and checking that the corresponding interrupt handler is invoked.
I've been reading about multi processor support in windows 7 and it seems i'm in luck. It's already supported !
When i send a message to "IPI_TYPE_ALL_INCLUDE_SELF" my "IpiInterruptHandler" is called and executed.
Does this mean my lapic is initialized and configured correctly ? Or is is there some more work to be done ?
If that works then you can be sure that most of it is right. It's still possible for TPR to be slightly wrong (e.g. only allowing higher priority interrupts), and for the Logical Destination Register and Destination Format Register to be unset (but if you're only using fixed delivery then these don't matter).
Anon5710 wrote:
Brendan wrote:More specifically, when the IO APIC detects an IRQ has occurred it sends a message/packet to all local APICs. The message/packet contains various information (if it's level/edge triggered, which vector, which destination, etc).
Do these messages get send automatically whenever an interrupt in received on an IOAPIC pin ? Or is there an bit/setting that needs to be set/cleared before messages are send?
(i already had to enable bit<0> in the Other interrupt controle register (OIC Reg) (ICH7))
IO APIC would detect the IRQ, then look at the redirection entry's enabled/disable flag, then use the rest of the information in the redirection entry to determine what/how to send, and then send the message.
Anon5710 wrote:
Brendan wrote: Each local APIC receives this message/packet and determines if it should accept it or not (based on the destination and other information).
What exactly is that "other information", i'm guessing you mean edge- or level-interrupt and ActiveLow or ActiveHigh.
Or do you mean the Task Priority register, and others ?
For older CPUs (Pentium and P6) there's a full description of "APIC bus message formats" in an appendix of Intel's programmer's manual. I'd assume that more recent CPUs use the same information (just using the main CPU bus with different format messages, rather than a special APIC bus).
Anon5710 wrote:
Brendan wrote:If the local APIC accepts the message/packet; it sets the corresponding flag in it's "Interrupt received register" (IRR). When the local APIC can (which depends on TPR, other pending interrupts, etc), it clears the flag in the IRR and sets the corresponding flag in the "In server register" (ISR) and tells the CPU about it.
This does happen in hardware right ?
Yes.
Anon5710 wrote:Now i believe i have configured my IOAPIC correctly.
  • 1) Enable IOAPIC in the OIC register
    2) write the APIC ID (from MADT tables) to the IOAPIC ID Register
    3) Write the redirection entries 0-23, using information from the MADT tables
    4) unmask wanted redirection entries
You shouldn't need to write the APIC ID to the IO APIC ID Register.
Anon5710 wrote:For redirection entries that were not listed in the MADT tables, i set:
(entry 0-15 ) : Active High, edge triggered (ISA?)
(entry 16-23) : Active Low, level triggered (PCI)
Before parsing the MADT you should begin by assuming that redirection entries 0 to 15 are used for ISA IRQs 0 to 15. The MADT's "Interrupt Source Override Structures" will tell you when this initial/default assumption is wrong. For example, the MADT might tell you that ISA IRQ 9 is connected to IO APIC 44 and is level triggered; and (in this case) it'd be silly to assume that ISA IRQ 9 is also connected to IO APIC input 9 just because IO APIC input 9 is not listed.

For PCI IRQs, the MADT tells you nothing and you can't assume anything at all. Sadly, you have to interpret the ACPI AML to determine how PCI IRQs are connected to IO APIC inputs (or find some other work-around; like implementing a motherboard driver for each different motherboard, or some complex auto-detection scheme, or just configure PCI devices to use MSI instead).

Also, please stop *assuming* that there's one IO APIC with 24 inputs. This is a bad assumption. For example, one of the computers I have here has 2 separate IO APICs that both have 16 inputs - if you assume there's only one IO APIC then none of the PCI IRQs will ever work, because they're all connected to a completely different IO APIC.
Anon5710 wrote:Resulting in these entries:

Code: Select all

4294767296 PID:0 TID:2 redirection entry 0: 0x00000000_00000021
4294767296 PID:0 TID:2 redirection entry 1: 0x00000000_00000022
4294767296 PID:0 TID:2 redirection entry 2: 0x00000000_00000023
4294767296 PID:0 TID:2 redirection entry 3: 0x00000000_00000024
.... //always increase vector with +1
4294767296 PID:0 TID:2 redirection entry 21: 0x00000000_0000A036
4294767296 PID:0 TID:2 redirection entry 22: 0x00000000_0000A037
4294767296 PID:0 TID:2 redirection entry 23: 0x00000000_0000A038[/quote]

So you're saying that the all ISA IRQs have almost the same priority (and the PIT chip's IRQ is less important than the floppy controller's IRQ); and IRQs that come from unknown places are more important than IRQs that come from ISA? I also don't think it's likely that ISA IRQ 2 exists.

[quote="Anon5710"]//special info in madt table for 2 below
4294767296 PID:0 TID:2 redirection entry 2: 0x00000000_0000A023
4294767296 PID:0 TID:2 redirection entry 9: 0x00000000_0000802A
Ok. Based on your earlier MADT table output; I'd assume that ISA IRQ 0 is connected to IO APIC input 2 (and not IO APIC input 0); and that ISA IRQ 9 is level-triggered active high. If you have a look at the ACPI FADT (the "SCI_INT" field at offset 46) I think you'll find that ISA IRQ 9 is the SCI ("System Control Interrupt" - used by ACPI to send events to the OS/AML) and isn't a normal ISA IRQ at all.

I'll go through this in my next post.
Anon5710 wrote:So i need to enable interrupts in the processor core as-well ? (cli and ... )
STI (not CLI), and yes.. ;)


Cheers,

Brendan

Re: IOApic & Lapic issues

Posted: Tue Nov 13, 2012 12:19 pm
by Brendan
Hi again,

I'm going to go through everything (from MADT parsing to IO APIC redirection entry configuration) again, in nice clean logical steps.. :)

First (before you even look at the MADT) build a table/array in RAM that shows how ISA IRQs are mapped to "global system interrupts" and their attributes. This is just an initial default, and should look like this:

Code: Select all

IRQ IRQ 0 -> global system interrupt 0, edge triggered, active high
IRQ IRQ 1 -> global system interrupt 1, edge triggered, active high
IRQ IRQ 2 -> *unknown*
IRQ IRQ 3 -> global system interrupt 3, edge triggered, active high
IRQ IRQ 4 -> global system interrupt 4, edge triggered, active high
IRQ IRQ 5 -> global system interrupt 5, edge triggered, active high
IRQ IRQ 6 -> global system interrupt 6, edge triggered, active high
IRQ IRQ 7 -> global system interrupt 7, edge triggered, active high
IRQ IRQ 8 -> global system interrupt 8, edge triggered, active high
IRQ IRQ 9 -> global system interrupt 9, edge triggered, active high
IRQ IRQ 10 -> global system interrupt 10, edge triggered, active high
IRQ IRQ 11 -> global system interrupt 11, edge triggered, active high
IRQ IRQ 12 -> global system interrupt 12, edge triggered, active high
IRQ IRQ 13 -> global system interrupt 13, edge triggered, active high
IRQ IRQ 14 -> global system interrupt 14, edge triggered, active high
IRQ IRQ 15 -> global system interrupt 15, edge triggered, active high
After you've done this; you parse the MADT and find those "Interrupt Source Override Structures". For your specific case (as far as I know) there are 2 of them.

The first tells you that ISA IRQ 0 is actually connected to global system interrupt 2 (and is edge triggered, active high); so you change the entry for ISA IRQ 0 in your table to this:

Code: Select all

IRQ IRQ 0 -> global system interrupt 2, edge triggered, active high
The second "Interrupt Source Override Structure" tells you that ISA IRQ 9 is connected to global system interrupt 9 and is level-triggered active high, so you change the entry for ISA IRQ 9 in your table.

The final table ends up like this:

Code: Select all

IRQ IRQ 0 -> global system interrupt 2, edge triggered, active high
IRQ IRQ 1 -> global system interrupt 1, edge triggered, active high
IRQ IRQ 2 -> *unknown*
IRQ IRQ 3 -> global system interrupt 3, edge triggered, active high
IRQ IRQ 4 -> global system interrupt 4, edge triggered, active high
IRQ IRQ 5 -> global system interrupt 5, edge triggered, active high
IRQ IRQ 6 -> global system interrupt 6, edge triggered, active high
IRQ IRQ 7 -> global system interrupt 7, edge triggered, active high
IRQ IRQ 8 -> global system interrupt 8, edge triggered, active high
IRQ IRQ 9 -> global system interrupt 9, level triggered, active high
IRQ IRQ 10 -> global system interrupt 10, edge triggered, active high
IRQ IRQ 11 -> global system interrupt 11, edge triggered, active high
IRQ IRQ 12 -> global system interrupt 12, edge triggered, active high
IRQ IRQ 13 -> global system interrupt 13, edge triggered, active high
IRQ IRQ 14 -> global system interrupt 14, edge triggered, active high
IRQ IRQ 15 -> global system interrupt 15, edge triggered, active high
Next; you create a second table of global system interrupts. For this table you can just assume that there's a maximum of 256 global system interrupts; and start by setting every one of them to "unknown". After you've done that you go through the ISA IRQ table and transfer the information from that into the right places of the global system interrupt table. The result will be like this:

Code: Select all

Global system interrupt 0 -> *unused*
Global system interrupt 1 -> IRQ IRQ 1, edge triggered, active high
Global system interrupt 2 -> IRQ IRQ 0, edge triggered, active high
Global system interrupt 3 -> IRQ IRQ 3, edge triggered, active high
Global system interrupt 4 -> IRQ IRQ 4, edge triggered, active high
Global system interrupt 5 -> IRQ IRQ 5, edge triggered, active high
Global system interrupt 6 -> IRQ IRQ 6, edge triggered, active high
Global system interrupt 7 -> IRQ IRQ 7, edge triggered, active high
Global system interrupt 8 -> IRQ IRQ 8, edge triggered, active high
Global system interrupt 9 -> IRQ IRQ 9, level triggered, active high
Global system interrupt 10 -> IRQ IRQ 10, edge triggered, active high
Global system interrupt 11 -> IRQ IRQ 11, edge triggered, active high
Global system interrupt 12 -> IRQ IRQ 12, edge triggered, active high
Global system interrupt 13 -> IRQ IRQ 13, edge triggered, active high
Global system interrupt 14 -> IRQ IRQ 14, edge triggered, active high
Global system interrupt 15 -> IRQ IRQ 15, edge triggered, active high
Global system interrupt 16 -> *unused*
Global system interrupt 17 -> *unused*
...
Global system interrupt 255 -> *unused*
Next, you figure out which global system interrupts are connected to which IO APIC inputs. To do this you parse the MADT and find any "I/O APIC Structures". For each structure you will get an APIC ID for the IO APIC, the physical address of the IO APIC, and a "starting global system interrupt". You use the physical address of the IO APIC to find out how many inputs the IO APIC has. Then you use this information to fill out your global system interrupt table some more, using something like this:

Code: Select all

    for(input = 0; input < totalInputs; input++) {
        GSI = input + startingGSI;
        GSI_table[GSI].APIC_ID = APIC_ID;
        GSI_table[GSI].input = input;
    }
For my "dual IO APIC" computer, the global system interrupt table might end up looking like this:

Code: Select all

Global system interrupt 0 (IO APIC ID 0x0D, input 0) -> *unused*
Global system interrupt 1 (IO APIC ID 0x0D, input 1) -> IRQ IRQ 1, edge triggered, active high
Global system interrupt 2 (IO APIC ID 0x0D, input 2) -> IRQ IRQ 0, edge triggered, active high
Global system interrupt 3 (IO APIC ID 0x0D, input 3) -> IRQ IRQ 3, edge triggered, active high
Global system interrupt 4 (IO APIC ID 0x0D, input 4) -> IRQ IRQ 4, edge triggered, active high
Global system interrupt 5 (IO APIC ID 0x0D, input 5) -> IRQ IRQ 5, edge triggered, active high
Global system interrupt 6 (IO APIC ID 0x0D, input 6) -> IRQ IRQ 6, edge triggered, active high
Global system interrupt 7 (IO APIC ID 0x0D, input 7) -> IRQ IRQ 7, edge triggered, active high
Global system interrupt 8 (IO APIC ID 0x0D, input 8) -> IRQ IRQ 8, edge triggered, active high
Global system interrupt 9 (IO APIC ID 0x0D, input 9) -> IRQ IRQ 9, level triggered, active high
Global system interrupt 10 (IO APIC ID 0x0D, input 10) -> IRQ IRQ 10, edge triggered, active high
Global system interrupt 11 (IO APIC ID 0x0D, input 11) -> IRQ IRQ 11, edge triggered, active high
Global system interrupt 12 (IO APIC ID 0x0D, input 12) -> IRQ IRQ 12, edge triggered, active high
Global system interrupt 13 (IO APIC ID 0x0D, input 13) -> IRQ IRQ 13, edge triggered, active high
Global system interrupt 14 (IO APIC ID 0x0D, input 14) -> IRQ IRQ 14, edge triggered, active high
Global system interrupt 15 (IO APIC ID 0x0D, input 15) -> IRQ IRQ 15, edge triggered, active high
Global system interrupt 16 (IO APIC ID 0x0E, input 0) -> *unused*
Global system interrupt 17 (IO APIC ID 0x0E, input 1) -> *unused*
...
Global system interrupt 30 (IO APIC ID 0x0E, input 14) -> *unused*
Global system interrupt 31 (IO APIC ID 0x0E, input 15) -> *unused*
Global system interrupt 32 (no IO APIC!) -> *unused*
Global system interrupt 33 (no IO APIC!) -> *unused*
...
Global system interrupt 255 (no IO APIC!) -> *unused*
When you're parsing the MADT's "I/O APIC Structures", it's also a good idea to disable/mask every input in every IO APIC input's redirection entry.

Once you've done that; you've finished initialising the IO APICs.

Later on, you might be starting a floppy driver. The floppy driver might initialise the floppy controller, and then ask the kernel for an interrupt vector. It would tell the kernel something like "my device uses ISA IRQ 6, and it only needs to be a low priority vector". The kernel would search the IDT looking for a vector that is still free and it might find that vector 0x24 is free (and has a suitable priority), and create the IDT entry (so now the floppy driver gets told whenever any "interrupt 0x24" occurs). Then the kernel would look at the ISA IRQ table (from the beginning) and find out that ISA IRQ 6 corresponds to global interrupt 6; then it would lookup global interrupt 6 in the global system interrupt table and find out that it's "IO APIC ID 0x0D, input 6, edge triggered, active high", and the kernel would program the corresponding IO APIC's input (set it to edge triggered active high for vector 0x24, and enable it).

After that you might start a hard disk driver, which asks the kernel something like "my device uses ISA IRQ 14, and needs to be high priority". The kernel searches the IDT and decides to allocate vector 0xC8 and creates the IDT entry; then it looks up the info in the ISA IRQ table and looks up more info in the global system interrupt table; then programs the corresponding IO APIC input (edge triggered active high, vector 0xC8, enabled).

For PCI devices it's more complicated. The driver might ask the kernel for an interrupt vector (at a certain priority), and the kernel might have to ask ACPI AML to figure out which global system interrupt to use (because the kernel can't use the "ISA IRQ" table for PCI IRQs) and how to program it, and then the kernel can add this information to its "global system interrupt table" (in case another driver ends up sharing the same PCI IRQ) and configure the corresponding IO APIC input. Alternatively, the driver might ask for a range of interrupt vectors (at a certain priority) and then configure the device to use MSI (without using any IO APIC input/s at all). In both cases the kernel still needs to find/allocate/configure suitable interrupt vector/s and setup IDT entries.


Cheers

Brendan

Re: IOApic & Lapic issues

Posted: Tue Nov 13, 2012 1:14 pm
by rdos
Brendan wrote:For PCI devices it's more complicated. The driver might ask the kernel for an interrupt vector (at a certain priority), and the kernel might have to ask ACPI AML to figure out which global system interrupt to use (because the kernel can't use the "ISA IRQ" table for PCI IRQs) and how to program it, and then the kernel can add this information to its "global system interrupt table" (in case another driver ends up sharing the same PCI IRQ) and configure the corresponding IO APIC input. Alternatively, the driver might ask for a range of interrupt vectors (at a certain priority) and then configure the device to use MSI (without using any IO APIC input/s at all). In both cases the kernel still needs to find/allocate/configure suitable interrupt vector/s and setup IDT entries.
Yes, PCI is really messy. If it was as simple as to use MSI for PCI interrupts, I would always do it that way, but many PCI devices don't support MSI, so that is not an option for all PCI devices. Additionally, MSI comes in different variants, which means there are at least two different MSI structures. Some devices also have a possibility to use more than one MSI interrupt (notably AHCI devices), which means the driver must know how many MSI interrupts it wants to allocate for a device, and their function. Since the MSI interrupts come in clusters, you also need a function to allocate multiple interrupt vectors with a particular alignment in order to utilize that function.

And figuring out the APIC IRQs for PCI is a multistep procedure that requires AML support. I've managed to implement this, and it seems to be consistent with what Windows reports, but it's complex to do even with ACPICA available.

Anyway, I'd prioritize the procedure like this:
1. Check for MSI and MSI-X functionality, and use it if available
2. Try to get the IRQ by use of ACPI, and use it if available
3. Use a timer and regularly poll the device instead (useful for low-speed USB)

BTW, my AMD machine has two IO-APICs. One of them has 24 inputs, while the other has 32 inputs, so it is hardly unusual for modern motherboards two have more than one.

Re: IOApic & Lapic issues

Posted: Thu Nov 15, 2012 1:52 am
by Anon5710
Brendan wrote:H,
Also, please stop *assuming* that there's one IO APIC with 24 inputs. This is a bad assumption. For example, one of the computers I have here has 2 separate IO APICs that both have 16 inputs - if you assume there's only one IO APIC then none of the PCI IRQs will ever work, because they're all connected to a completely different IO APIC.
I am well aware of this issue. But i chose to ignore this for now, i rather spend my time getting the hang of embedded computing (I'm just done with school :) ) then programming an elaborate handling mechanism for different motherboards. I'll probably do that when i've got a basic working set :)

As i don't want to develop a IRQ driver right now, i'll be inputting all the information, ISA and PCI info into the GIS table.
An IRQ driver would require allot of editing in the windows kernel, something i'm not quite ready for :)


Now, i (almost)followed your suggestion and resulted with this :

ISA tabel after adding Interrupt redirect & IOApic structers.

Code: Select all

4294767296 PID:0 TID:2 IRQ 0 -> global system interrupt 2, edge triggered, active high
4294767296 PID:0 TID:2 IRQ 1 -> global system interrupt 1, edge triggered, active high
4294767296 PID:0 TID:2 IRQ 2 -> *unknown* 
4294767296 PID:0 TID:2 IRQ 3 -> global system interrupt 3, edge triggered, active high
4294767296 PID:0 TID:2 IRQ 4 -> global system interrupt 4, edge triggered, active high
4294767296 PID:0 TID:2 IRQ 5 -> global system interrupt 5, edge triggered, active high
4294767296 PID:0 TID:2 IRQ 6 -> global system interrupt 6, edge triggered, active high
4294767296 PID:0 TID:2 IRQ 7 -> global system interrupt 7, edge triggered, active high
4294767296 PID:0 TID:2 IRQ 8 -> global system interrupt 8, edge triggered, active high
4294767296 PID:0 TID:2 IRQ 9 -> global system interrupt 9, level triggered, active high
4294767296 PID:0 TID:2 IRQ 10 -> global system interrupt 10, edge triggered, active high
4294767296 PID:0 TID:2 IRQ 11 -> global system interrupt 11, edge triggered, active high
4294767296 PID:0 TID:2 IRQ 12 -> global system interrupt 12, edge triggered, active high
4294767296 PID:0 TID:2 IRQ 13 -> global system interrupt 13, edge triggered, active high
4294767296 PID:0 TID:2 IRQ 14 -> global system interrupt 14, edge triggered, active high
4294767296 PID:0 TID:2 IRQ 15 -> global system interrupt 15, edge triggered, active high
the GIS table with ISA table info

Code: Select all

4294767296 PID:0 TID:2 Global System Interrupt 0 (IO Apic ID 0x02) -> *unknown* 
4294767296 PID:0 TID:2 Global System Interrupt 1 (IO Apic ID 0x02, input 1) -> IRQ 1, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 2 (IO Apic ID 0x02, input 2) -> IRQ 0, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 3 (IO Apic ID 0x02, input 3) -> IRQ 3, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 4 (IO Apic ID 0x02, input 4) -> IRQ 4, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 5 (IO Apic ID 0x02, input 5) -> IRQ 5, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 6 (IO Apic ID 0x02, input 6) -> IRQ 6, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 7 (IO Apic ID 0x02, input 7) -> IRQ 7, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 8 (IO Apic ID 0x02, input 8) -> IRQ 8, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 9 (IO Apic ID 0x02, input 9) -> IRQ 9, level triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 10 (IO Apic ID 0x02, input 10) -> IRQ 10, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 11 (IO Apic ID 0x02, input 11) -> IRQ 11, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 12 (IO Apic ID 0x02, input 12) -> IRQ 12, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 13 (IO Apic ID 0x02, input 13) -> IRQ 13, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 14 (IO Apic ID 0x02, input 14) -> IRQ 14, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 15 (IO Apic ID 0x02, input 15) -> IRQ 15, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 16 (IO Apic ID 0x02) -> *unknown* 
4294767296 PID:0 TID:2 Global System Interrupt 17 (IO Apic ID 0x02) -> *unknown* 
4294767296 PID:0 TID:2 Global System Interrupt 18 (IO Apic ID 0x02) -> *unknown* 
4294767296 PID:0 TID:2 Global System Interrupt 19 (IO Apic ID 0x02) -> *unknown* 
4294767296 PID:0 TID:2 Global System Interrupt 20 (IO Apic ID 0x02) -> *unknown* 
4294767296 PID:0 TID:2 Global System Interrupt 21 (IO Apic ID 0x02) -> *unknown* 
4294767296 PID:0 TID:2 Global System Interrupt 22 (IO Apic ID 0x02) -> *unknown* 
4294767296 PID:0 TID:2 Global System Interrupt 23 (IO Apic ID 0x02) -> *unknown* 
And the GIS table after the PCI entries (i got these from the MPtabels, luckily I didn't have to implement aml !)

Code: Select all

4294767296 PID:0 TID:2 Global System Interrupt 0 (IO Apic ID 0x02) -> *unknown* 
4294767296 PID:0 TID:2 Global System Interrupt 1 (IO Apic ID 0x02, input 1) -> IRQ 1, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 2 (IO Apic ID 0x02, input 2) -> IRQ 0, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 3 (IO Apic ID 0x02, input 3) -> IRQ 3, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 4 (IO Apic ID 0x02, input 4) -> IRQ 4, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 5 (IO Apic ID 0x02, input 5) -> IRQ 5, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 6 (IO Apic ID 0x02, input 6) -> IRQ 6, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 7 (IO Apic ID 0x02, input 7) -> IRQ 7, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 8 (IO Apic ID 0x02, input 8) -> IRQ 8, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 9 (IO Apic ID 0x02, input 9) -> IRQ 9, level triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 10 (IO Apic ID 0x02, input 10) -> IRQ 10, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 11 (IO Apic ID 0x02, input 11) -> IRQ 11, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 12 (IO Apic ID 0x02, input 12) -> IRQ 12, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 13 (IO Apic ID 0x02, input 13) -> IRQ 13, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 14 (IO Apic ID 0x02, input 14) -> IRQ 14, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 15 (IO Apic ID 0x02, input 15) -> IRQ 15, edge triggered, active high
4294767296 PID:0 TID:2 Global System Interrupt 16 (IO Apic ID 0x02, input 16) -> IRQ 16, level triggered, active low 
4294767296 PID:0 TID:2 Global System Interrupt 17 (IO Apic ID 0x02) -> *unknown* 
4294767296 PID:0 TID:2 Global System Interrupt 18 (IO Apic ID 0x02, input 18) -> IRQ 18, level triggered, active low 
4294767296 PID:0 TID:2 Global System Interrupt 19 (IO Apic ID 0x02, input 19) -> IRQ 19, level triggered, active low 
4294767296 PID:0 TID:2 Global System Interrupt 20 (IO Apic ID 0x02) -> *unknown* 
4294767296 PID:0 TID:2 Global System Interrupt 21 (IO Apic ID 0x02) -> *unknown* 
4294767296 PID:0 TID:2 Global System Interrupt 22 (IO Apic ID 0x02) -> *unknown* 
4294767296 PID:0 TID:2 Global System Interrupt 23 (IO Apic ID 0x02, input 23) -> IRQ 23, level triggered, active low 
Next up, getting this information in the IOredirection entries :)


As a follow up question, to enable the processor interrupts would this be enough ?

Code: Select all

 _asm 
	 {
		sti //aparently sti is only enabeld after the next instruction, so --> nop
		nop
	 }

@brendan, thanks allot man ! =D> =D> =D>