Page 1 of 2

ACPI tables for MP identification

Posted: Sun Feb 01, 2009 4:16 pm
by worldsapart
Hi,

wel.. I'm trying to parse the ACPI tables to identify the CPUs.. wel.. It's been a while since i coded in C n I'm kinda rusted... It would be great if some1 could help me correct this code.. it tries to look for the RSDP structure... i know it's somethin wrong wit my usage of pointers or my jus my ignorance.. your help will be appreciated..

Code: Select all

struct RSDPDescriptor {
 char Signature[8];
 char Checksum;
 char OEMID[6];
 char Revision;
 unsigned int RsdtAddress;

 unsigned int Length;
 double XsdtAddress;
 char ExtendedChecksum;
 char reserved[3];
};

k_main() // like main in a normal C program
{
	
	int i =0;
        char *address = (char *)0x0000040E;

	struct RSDPDescriptor *rsdp;

	for(i=0;i<1024;i+=16)
	{
		rsdp = (struct RSDPDescriptor *)(address + i);
		if(mystrcmp((*rsdp).Signature,"RSD PTR ") == 0)
		{
			k_printf("Found RSDP1",8);
		}
	}

	char *address2 = (char *)0x0E0000;

	while(address2<=0x0FFFFF)
	{
		rsdp = (struct RSDPDescriptor *)address2;
		if((*rsdp).Signature == "RSD PTR ")
		{
			k_printf("Found RSDP2",9);
		}
		address2 += 16;
	}
	
	k_printf("Not Found",10);
	
}

Wel... the prob is.... It doesnt find the RSDP... PLease let me know if u want more details abt my implementation...

Re: ACPI tables for MP identification

Posted: Sun Feb 01, 2009 4:58 pm
by worldsapart
oh yeah.. i know that.. it was corrected.. thanks.. am sorry for posting the wrong code... anythin else that could be possibly wrong?

Does any1 have any code that I could refer to? Would be really helpful....

Re: ACPI tables for MP identification

Posted: Sun Feb 01, 2009 5:54 pm
by JohnnyTheDon
Yep. From my OS:

acpi.h

Code: Select all

#ifndef ACPI_H
#define ACPI_H

#pragma pack(push, 1)

typedef struct _RSDP
{
	char Signature[8];
 	uint8 Checksum;
 	char OemId[6];
 	uint8 Revision;
 	uint32 RsdtAddress;
} RSDP_t;

typedef struct _RSDT
{
	char Signature[4];
	uint32 Length;
	uint8 Revision;
	uint8 Checksum;
	char OemId[6];
	char OemTableId[8];
	uint32 OemRevision;
	char CreatorId[4];
	uint32 CreatorRevision;
} RSDT_t;

typedef struct _MADT
{
	char Signature[4];
	uint32 Length;
	uint8 Revision;
	uint8 Checksum;
	char OemId[6];
	char OemTableId[8];
	uint32 OemRevision;
	char CreatorId[4];
	uint32 CreatorRevision;
	uint32 LapicAddress;
	uint32 Flags;
} MADT_t;

typedef struct _MADTSubtable
{
	uint8 Type;
	uint8 Length;
} MADTSubtable_t;

typedef struct _AcpiLapic
{
	uint8 Type;
	uint8 Length;
	uint8 AcpiId;
	uint8 LapicId;
	uint32 Flags;
} AcpiLapic_t;

typedef struct _AcpiIoApic
{
	uint8 Type;
	uint8 Length;
	uint8 IoApicId;
	uint8 Reserved;
	uint32 Address;
	uint8 IRQBase;
} AcpiIoApic_t;

#pragma pack(pop)

#define MADT_LAPIC 0
#define MADT_IOAPIC 1

bool inline AcpiChecksum(void* ptr, int size)
{
	uint8 accum = 0;
	for(int i = 0; i < size; i++)
	{
		accum += ((uint8*)ptr)[i];
	}
	return (accum == 0);
}

#endif
Excerpt from my SMP header (mp.h):

Code: Select all

#ifndef MP_H
#define MP_H

// Processor Descriptor
typedef struct _Processor
{
	PML4 Pml4; // Processor Specific PML4
	IDT Idt; // Interupt Descriptor Table
	TSS_t* Tss; // System TSS
	virt_addr Stack; // Address of stack for this processor
	CpuFeatures_t Features; // Features retrieved from CPUID
	uint8 LapicId; // Local APIC ID of this processor
	int8 Stage; // Processor Initialization Stage
} Processor_t;

EXTERN Processor_t* MpProcessorTable;
EXTERN uint32 MpProcessorCount;

#endif

Excerpt from my SMP code (mp.c):

Code: Select all

static bool MpGetInfo()
{
	// Try to find the RSDP in the BIOS
	void* i;
	for(i = PTOV(0xE0000); i < PTOV(0xFFFFF); i += 16)
	{
		if(memcmp(i, "RSD PTR", 7) == 0)
		{
			if(!AcpiChecksum(i, sizeof(RSDP_t)))
				continue;
			return MpGetInfoAcpi(i);
		}
	}
	
	// Try to find the RSDP in the EBDA
	uint16 ebdaSegment = *((uint16*)PTOV(0x040E));
	for(i = PTOV(ebdaSegment<<4); i < PTOV((ebdaSegment<<4)+0x400); i += 16)
	{
		if(memcmp(i, "RSD PTR", 7) == 0)
		{
			if(!AcpiChecksum(i, sizeof(RSDP_t)))
				continue;
			return MpGetInfoAcpi(i);
		}
	}
	
	return false;
}
static bool MpGetInfoAcpi(RSDP_t* rsdp)
{
	// Use the RSDP to find the RSDT
	RSDT_t* rsdt = (RSDT_t*)PTOV(rsdp->RsdtAddress);
	if(!AcpiChecksum(rsdt, rsdt->Length))
		return false;
	
	// Iterate through the tables to find the MADT
	MADT_t* madt;
	int tableCount = (rsdt->Length-sizeof(RSDT_t))/4;
	int i;
	for(i = 0; i < tableCount; i++)
	{
		madt = (MADT_t*)PTOV(*(((uint32*)(rsdt+1))+i));
		if(memcmp(madt, "APIC", 4) == 0)
			break;
	}
	if(i == tableCount) // If we didn't find the MADT, something screwed up
		return false;
	if(!AcpiChecksum(madt, madt->Length)) // Checksum the MADT
		return false;
	
	// Get the number of processors and IO APICs
	MpProcessorCount = 0;
	for(MADTSubtable_t* sub = (MADTSubtable_t*)(madt+1); (void*)sub < ((void*)madt)+madt->Length; sub = (MADTSubtable_t*)(((void*)sub)+sub->Length))
	{
		if(sub->Type == MADT_LAPIC)
			MpProcessorCount++;
	}
	if(MpProcessorCount < 1) // There needs to be at least one processor and one IO APIC
		return false;
		
	// Allocate processor table
	MpProcessorTable = (Processor_t*)MemoryAllocPages(ALIGN_SMALL(sizeof(Processor_t)*MpProcessorCount)/PAGE_SIZE_SMALL, VALLOC_MASK_FLAG_WRITABLE);
	if((virt_addr)MpProcessorTable == INVALID_VIRTUAL_ADDRESS)
		return false;
	
	// Fill the processor and IO APIC tables
	i = 0;
	for(MADTSubtable_t* sub = (MADTSubtable_t*)(madt+1); (void*)sub < ((void*)madt)+madt->Length; sub = (MADTSubtable_t*)(((void*)sub)+sub->Length))
	{
		if(sub->Type == MADT_LAPIC)
		{
			MpProcessorTable[i].LapicId = ((AcpiLapic_t*)sub)->LapicId;
			i++;
		}
	}
	
	return true;
}
PTOV is a macro for converting from virtual to physical addresses, you will probably want to remove it. There is some other stuff in there that is tied to my OS, but most of it should make sense.

Re: ACPI tables for MP identification

Posted: Sun Feb 01, 2009 6:47 pm
by worldsapart
Thanks a lot man... that should answer my doubts for now.. :)

Re: ACPI tables for MP identification

Posted: Mon Feb 02, 2009 3:24 am
by worldsapart
hi Johnnythedon,

your code was really helpful... was able to clean up my code... n correct a few bugs..

wel, I found the RSDP... n checksumd it, n it was fine.. But then I went on to check the RSDT... there was a signature match... but when i did a checksum, it failed!! ... any idea y this could be??? btw... I'm using Qemu to test my code...

If any1 else has had a similar prob and was able to solve it pls do share it wit me... Thanks...

Re: ACPI tables for MP identification

Posted: Mon Feb 02, 2009 5:00 am
by 01000101
I haven't coded for the ACPI tables (only MP) so far.

if it's reporting an incorrect checksum, check it's supposed address and see what the alignment is, if it's not aligned properly, it's an incorrect structure anyways.
I noticed that you're not searching on an alignment offset (i++, and not i+=16 or similar) for the initial structure, this can lead to issues (if it's like the MP tables).

Also, what's the point of this in the checksum function:

Code: Select all

return (accum == 0);
why not just return (accum)?

Re: ACPI tables for MP identification

Posted: Mon Feb 02, 2009 9:44 am
by JohnnyTheDon
01000101 wrote: I noticed that you're not searching on an alignment offset (i++, and not i+=16 or similar) for the initial structure, this can lead to issues (if it's like the MP tables).
Uh, my code does do that:

Code: Select all

for(i = PTOV(0xE0000); i < PTOV(0xFFFFF); [b]i += 16[/b])
01000101 wrote: Also, what's the point of this in the checksum function:

Code: Select all

return (accum == 0);
why not just return (accum)?
Because its a boolean value, and is tested as such. So

Code: Select all

if(!AcpiChecksum(rsdt, rsdt->Length))
      return false;
Would need to be

Code: Select all

if(AcpiChecksum(rsdt, rsdt->Length) == 0)
     return false;
Its just defering the checking.
wel, I found the RSDP... n checksumd it, n it was fine.. But then I went on to check the RSDT... there was a signature match... but when i did a checksum, it failed!! ... any idea y this could be??? btw... I'm using Qemu to test my code...
Make sure you are using Length when checksumming. IIRC the whole table is checksummed, not just the header. So you would need to write something like:

Code: Select all

if(!AcpiChecksum(rsdt, rsdt->Length))
     return false;
If you use sizeof(RSDT_t) instead of rsdt->Length it won't work.

Re: ACPI tables for MP identification

Posted: Mon Feb 02, 2009 12:13 pm
by worldsapart
Hi... yeah I did use Length, cos I know that the Rsdt is bigger than the structure..... wel.. I'll jus post my code so you can see wat mayb wrong..

my RSDT structure

Code: Select all

typedef struct RSDTable
{
   	char Signature[4];
   	int Length;
   	char Revision;
   	char Checksum;
   	char OemId[6];
   	char OemTableId[8];
   	char OemRevision[4];
   	char CreatorId[4];
   	int CreatorRevision;
}RSDT_t;
my code so far:

Code: Select all

RSDP_t *rsdp;	// Structure to hold the RSDP table, defined in k_acpi_tables.h	
	RSDT_t *rsdt;	// Structure to hold the RSDT structure

	// Searching for RSDP in 16 byte boundaries
	if((rsdp = acpi_find_rsdp())==0)
		k_printf("No multiple CPUs", 7);
	else
	{
		// So RSDP has been found.. now what? --> checksum!!
		
		
		if(acpi_checksum(rsdp,rsdp->Length)!=1)
		{
			k_printf("RSDP checksum failed..",7);
		}
		else
		{
			k_printf("RSDP found!",7);

			// parse RSDT.. but first checksum!
			rsdt = (RSDT_t*)(rsdp->RsdtAddress);

			if(mystrcmp((char*)rsdt,"RSDT",4)== 1)
				k_printf("Signature Match",8);

			if(acpi_checksum(rsdt,rsdt->Length)!=1)
				k_printf("RSDT checksum failed",9);
			else
			{
				k_printf("RSDT Found!",9);
			}				
			
		}
		
	}		

the checksum function is the same as yours, except that returns a integer value, cos I dont have a bool type....

Also, I've read a few bug issues related to the Bochs BIOS that Qemu uses, regarding ACPI issues... could that be the prob??? or is there somethin terribly wrong in my code?

Re: ACPI tables for MP identification

Posted: Mon Feb 02, 2009 4:48 pm
by JohnnyTheDon
A few ideas,

1. Change all your chars to unsigned char (including the checksumming function. It should use unsigned char or you may have issues). While you're at it, make everything unsigned.
2. Make sure int is 32-bits in your compiler.
3. Check what length is.

Re: ACPI tables for MP identification

Posted: Mon Feb 02, 2009 5:44 pm
by worldsapart
i changed al the chars to unsigned... still didnt work.. :( n... in the checksum wher is the char that u were talkin abt??

also, the size of int is 32bits.... and I am workin on the k_printf function to print integers.. wil check the length n let u know.. but meanwhile.. any more ideas??

Re: ACPI tables for MP identification

Posted: Tue Feb 03, 2009 2:31 am
by worldsapart
i did some more testing n debuggin .. but to no avail... wel.. I have some more info..

The rsdp was found at 0x000FBBC0
This was checksummed and was validated..

the rsdt was found at 0x07FF0000
and the length was 52 ...

but the checksum failed.. could it b a prob cos my bochs bios isnt updated wit bug fixes?

Re: ACPI tables for MP identification

Posted: Tue Feb 03, 2009 8:16 am
by jal
worldsapart wrote:but the checksum failed.. could it b a prob cos my bochs bios isnt updated wit bug fixes?
Never blame the emulator, unless you are really, really sure it's at fault. Did you, for example, Google to find out if anyone else has had this problem with Bochs? If not, you can be pretty sure it's not at fault.


JAL

Re: ACPI tables for MP identification

Posted: Tue Feb 03, 2009 12:07 pm
by worldsapart
I am very sorry.. I know that was pretty lame... It was a small bug in my checksum function. :( .. corrected that... Am sorry again guys.

:| Lesson Learnt!

Re: ACPI tables for MP identification

Posted: Tue Feb 03, 2009 1:56 pm
by jal
worldsapart wrote:Lesson Learnt!
Glad to hear it, and show of character for admitting it :). Good luck with the ACPI stuff.


JAL

Re: ACPI tables for MP identification

Posted: Tue Feb 03, 2009 4:40 pm
by JohnnyTheDon
If you want the use ACPI for more than just finding processors, you may want to look at ACPICA ( http://www.acpica.org ). You have to implement around 30 functions for it to use (basic things MemoryAlloc and WritePort) and it will not only find ACPI tables for you, but also do all your power management and give you all the information you need about your devices. Its kind of big (adds something like 200kb to the size of your kernel), but it gets the job done.