[50% SOLVED] Unable to find the multi processor table

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
Bietje
Member
Member
Posts: 100
Joined: Wed Apr 20, 2011 6:57 am

[50% SOLVED] Unable to find the multi processor table

Post by Bietje »

Hi,

I'm working on an APIC subsystem so I want to use the multiprocessor specification. I implemented some test code to find the MP Floating Pointer Structure. I can't find it at the specified locations (on a real, multiprocessor system). Possible locations:
a. In the first kilobyte of Extended BIOS Data Area (EBDA), or
b. Within the last kilobyte of system base memory (e.g., 639K-640K for systems with 640
KB of base memory or 511K-512K for systems with 512 KB of base memory) if the
EBDA segment is undefined, or
c. In the BIOS ROM address space between 0F0000h and 0FFFFFh.
My code:

Code: Select all

static void *
ol_cpu_mp_search_config_table(void * base, uint16_t i /* amount of tries */)
{        
        while(i--)
        {
                if( *((uint32_t *)base) == OL_CPU_MP_FPS_SIGNATURE)
                {
                        return base;
                }
                else
                {
                        printnum(*((uint32_t *)base), 16, 0, 0);
                        putc(0xa);
                        base += 1;
                }
        }
        return NULL;
}
How I get the EBDA pointer:

Code: Select all

(void*)(*((uint16_t *)0x40e)<<4)
The exact starting address of the EBDA segment for EISA or MCA systems can be found in a two-
byte location (40:0Eh) of the BIOS data area.
Is it likely that my PC doesn't support the MP spec? Or does someone see a bug in my code, since it always returns a NULL-pointer.

Greets,
Bietje
Last edited by Bietje on Fri Sep 02, 2011 5:09 pm, edited 1 time in total.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Unable to find the multi processor table

Post by Brendan »

Hi,

If the word at address 0x0000040E (in the BDA) contains 0x8765, then would "(*((uint16_t *)0x40e)<<4)" equal "0x7650" or "0x00087650"?

The structure must be aligned on a 16-byte boundary, so you don't need to check at every byte (e.g. you should be doing "base += 16" rather than "base += 1"). If the signature is found in four different places but the checksum fails for the first 3 places, then only the fourth is valid.

I've got no idea which areas you're actually checking, or if "OL_CPU_MP_FPS_SIGNATURE" is defined correctly or if you're using paging (and if so, how it's setup), or if segment bases are zero.

Also, for performance, these types of searches can be a serious problem due to the "cache thrashing" nature of them. If you search for the MP table, then search for the ACPI tables, then search for SMBIOS tables; the total time taken can be a few seconds on some computers because of the huge number of cache misses. To avoid that you want to search for things in parallel (e.g. one search, that checks for MP tables and ACPI tables and SMBIOS tables at each 16-byte boundary). In this case you'd have a generic "search for all tables" function that isn't part of the MP spec code itself.


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.
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: Unable to find the multi processor table

Post by Owen »

Also: Yes, you can expect (more recent) SMP machines with no MP table. You can also expect some machines with broken MP tables (but working ACPI tables).

The general rule that decides whether a piece of PC firmware functionality works is
  • Does Windows use it? If so, it will work in the way Windows uses it
  • If Windows doesn't use it, did it once use it? Will it have needed changes since then?
Take, for example, that many motherboards advertise PCI-Express device power down wakeup signaling support yet don't actually have the necessary traces for this to work. The manufacturer tested against Windows, and Windows worked because it doesn't put devices into a low power state...
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Unable to find the multi processor table

Post by gerryg400 »

Some of the newer computers I've tested on don't have MP tables. If you have a Linux boot disk you can boot from it and check the DMSG log to see what Linux found.
If a trainstation is where trains stop, what is a workstation ?
Bietje
Member
Member
Posts: 100
Joined: Wed Apr 20, 2011 6:57 am

Re: Unable to find the multi processor table

Post by Bietje »

Brendan wrote:Hi,

If the word at address 0x0000040E (in the BDA) contains 0x8765, then would "(*((uint16_t *)0x40e)<<4)" equal "0x7650" or "0x00087650"?

The structure must be aligned on a 16-byte boundary, so you don't need to check at every byte (e.g. you should be doing "base += 16" rather than "base += 1"). If the signature is found in four different places but the checksum fails for the first 3 places, then only the fourth is valid.

I've got no idea which areas you're actually checking, or if "OL_CPU_MP_FPS_SIGNATURE" is defined correctly or if you're using paging (and if so, how it's setup), or if segment bases are zero.

Also, for performance, these types of searches can be a serious problem due to the "cache thrashing" nature of them. If you search for the MP table, then search for the ACPI tables, then search for SMBIOS tables; the total time taken can be a few seconds on some computers because of the huge number of cache misses. To avoid that you want to search for things in parallel (e.g. one search, that checks for MP tables and ACPI tables and SMBIOS tables at each 16-byte boundary). In this case you'd have a generic "search for all tables" function that isn't part of the MP spec code itself.


Cheers,

Brendan
"(*((uint16_t *)0x40e)<<4)" should translate to 0x87650. Or am I wrong here and should I add another pair of braces:
"((*((uint16_t *)0x40e))<<4)"

"OL_CPU_MP_FPS_SIGNATURE" is copied literally from the specification:

Code: Select all

#define OL_CPU_MP_FPS_SIGNATURE 0x5f4d505f /* _MP_ */
Also I do not have paging enabled. I did check on a 16-byte boundary before, but I didn't find anything, so changed to byte checking. I will change it back!

Currently I'm reading some parts on the ACPI tables, to see where I can get the information I need without the MPS (looking for where I can find the madt).
Oh and, thanks allot for your last tip!

---
gerryg400 wrote:Some of the newer computers I've tested on don't have MP tables. If you have a Linux boot disk you can boot from it and check the DMSG log to see what Linux found.
The computer is very new (very new, in the way of only a few weeks old), so I will check the DMSG log.

Greets,
Bietje

PS: Does anyone know what bochs and/or qemu/kvm does with ACPI and MP tables?
Last edited by Bietje on Fri Sep 02, 2011 8:03 am, edited 1 time in total.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Unable to find the multi processor table

Post by Brendan »

Hi,
Bietje wrote:"(*((uint16_t *)0x40e)<<4)" should translate to 0x87650. Or am I wrong here and should I add another pair of braces:
"((*((uint16_t *)0x40e))<<4)"
I'm an assembly language programmer, partly because I like assembly because everything is explicit - no guesswork and no silly rules needed to guess what gets promoted to what and how, and when. For C, I'm too lazy to remember all the rules and would have put an explicit cast to "uint32_t" to make sure the compiler doesn't decide to shift a 16-bit integer and lose the higher bits (because an explicit cast is easier than searching for the relevant section of the C specs ;) ). I *think* it might be right as it was (as the "4" is an "int" causing the entire expression to be promoted to "int"), but it's nice to check and make sure.
Bietje wrote:"OL_CPU_MP_FPS_SIGNATURE" is copied literally from the specification:

Code: Select all

#define OL_CPU_MP_FPS_SIGNATURE 0x5f4d505f /* _MP_ */
That looks wrong to me. It should be "0x5F504D5F" or maybe "'_' || ('M' << 8 ) || ('P' << 16) || ('_' << 24)".
Bietje wrote:PS: Does anyone know what bochs and/or qemu/kvm does ACPI and MP tables?
Bochs and Qemu have both ACPI and MP tables. However, for Bochs (if I remember right) the MP tables may not be present if the emulator is configured to only emulating a single-CPU machine and the IO APIC is disabled.


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.
Bietje
Member
Member
Posts: 100
Joined: Wed Apr 20, 2011 6:57 am

Re: Unable to find the multi processor table

Post by Bietje »

Hi,
I'm an assembly language programmer, partly because I like assembly because everything is explicit - no guesswork and no silly rules needed to guess what gets promoted to what and how, and when. For C, I'm too lazy to remember all the rules and would have put an explicit cast to "uint32_t" to make sure the compiler doesn't decide to shift a 16-bit integer and lose the higher bits (because an explicit cast is easier than searching for the relevant section of the C specs ). I *think* it might be right as it was (as the "4" is an "int" causing the entire expression to be promoted to "int"), but it's nice to check and make sure.
In C you can be very explicit too, but you should use the correct keywords such as volatile. Indeed - assembly is (much) more explicit in C. I like assembly too, but for portability sake do I program most of my OS in C.

An information update.. I tried to search for the RSDP too, well I didn't find it - so there has to be a bug in my code. I changed the approach a bit so I'll repost my code:

Code: Select all

int
ol_get_mp_config_header()
{
        /* check the first byte of the extended bios data area */
        if(ol_cpu_mp_search_config_table((void*)0xe0000, (void*)0xfffff) != NULL)
        {
                putc(0x42);
                return 0;
        }
        
        uint16_t ebda = *((uint16_t*) ((uint32_t)(0x040E)));
        if(ol_cpu_mp_search_config_table((void*)(ebda<<4), (void*)((ebda<<4)+0x400)) 
                != NULL)
        {
                putc(0x42);
                return 0;
        }
        return 1;                
}

Code: Select all

static void *
ol_cpu_mp_search_config_table(void * base, void* end)
{
        void* i = base;
        for(; i < end; i+=16)
        {
                if(memcmp(i, "RSD PTR ", 8) == 0) return base;
                putc(0x41);
                putc(0xa);
        }
        return NULL;
}
And the memcmp:

Code: Select all

int 
memcmp(void *ptr1, void* ptr2, size_t count)
{
#ifndef X86 
  if( diff >= 8)
    while(count >= 8)
    {
      if(*(unsigned long long*)ptr1 - *(unsigned long long*)ptr2)
        return 1;
      ptr1 += 8;
      ptr2 += 8;
      count -= 8;
    }
  if(count >= 4)
  {
    if(*(unsigned long long*)ptr1 - *(unsigned long long*)ptr2)
      return 1;
    ptr1 += 4;
    ptr2 += 4;
    count -= 4;
  }
#else
  while(count >= 4)
  {
    if(*(unsigned int*)ptr1 - *(unsigned int*)ptr2)
      return 1;
    ptr1 += 4;
    ptr2 += 4;
    count -= 4;
  }
#endif
  if(count >= 2)
  {
    if(*(unsigned long long*)ptr1 - *(unsigned long long*)ptr2)
      return 1;
    ptr1 += 2;
    ptr2 += 2;
    count -= 2;
  }
  if( (count = 1) && (*(unsigned long long*)ptr1 - *(unsigned long long*)ptr2) )
  {
      return 1;
  }
  return 0;
}
So what am I doing (or better, what do I intent to do?
I feed some begin and end pointers to a test function, which will test every first byte on a 16B boundary with the function memcmp.

I'm looking in the freebsd sources for the ACPI sources to find any clues. I tested my code on bochs, kvm and 2 real pc's (one 4 weeks old, the other one over 4 years).

I'm just posting this in the case someone might see anything what could lead to the problem. If you need more information just give a yell!

Greets,
Bietje
Last edited by Bietje on Fri Sep 02, 2011 5:06 pm, edited 1 time in total.
Bietje
Member
Member
Posts: 100
Joined: Wed Apr 20, 2011 6:57 am

Re: Unable to find the multi processor table

Post by Bietje »

berkus wrote:

Code: Select all

uint16_t ebda = *((uint16_t*) ((uint32_t)(0x040E)));
I believe you're doing something wrong.

Have you verified the bytes at [0x040e] and compared to the value you get in ebda?

Edit: actually type conversion above seems to work out ok.

Have you tried using a simple version of memcmp?
Not yet, I ripped this memcmp from the OS project of a friend of mine. I will write something simple, to be sure. Also I should perhaps check the bochs configuration docs to see what they say about the ACPI tables and the mps.

Just started to try random if-else constructions - this one seems to do the job, now I'm going to figure out why the others don't seem to work..

Code: Select all

if (mem[0] == '_' && mem[1] == 'M' && mem[2] == 'P' && mem[3] == '_') {
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: [50% SOLVED] Unable to find the multi processor table

Post by gerryg400 »

I think the memcmp is bogus

Code: Select all

  if( (count = 1) && (*(unsigned long long*)ptr1 - *(unsigned long long*)ptr2) )
That '(count = 1)' should probably be '(count == 1)' and I'm concerned about the (unsigned long long*). It seems that you would be testing bytes after the end of the string. Write yourself a simple memcmp in userspace and unit test it. Then use it in your kernel when you are 100% confident in it.
If a trainstation is where trains stop, what is a workstation ?
User avatar
Chandra
Member
Member
Posts: 487
Joined: Sat Jul 17, 2010 12:45 am

Re: [50% SOLVED] Unable to find the multi processor table

Post by Chandra »

gerryg400 wrote:I think the memcmp is bogus

Code: Select all

  if( (count = 1) && (*(unsigned long long*)ptr1 - *(unsigned long long*)ptr2) )
That '(count = 1)' should probably be '(count == 1)' and I'm concerned about the (unsigned long long*). It seems that you would be testing bytes after the end of the string. Write yourself a simple memcmp in userspace and unit test it. Then use it in your kernel when you are 100% confident in it.
+1
Programming is not about using a language to solve a problem, it's about using logic to find a solution !
Post Reply