SMP table scanning

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
User avatar
salil_bhagurkar
Member
Member
Posts: 261
Joined: Mon Feb 19, 2007 10:40 am
Location: India

SMP table scanning

Post by salil_bhagurkar »

Hello all! Heres my smp code, which scans for the mp floating pointer and the mp config,entries for processors,and similar stuff.

Code: Select all

#include <std.h>
#include <memory.h>
#include <type.h>
#include <error.h>

#define MP_SIGL 0x5f504d5f
#define MP_SIGSTR "_MP_"

#define MP_CONFIG_SIGL 0x504d4350
#define MP_CONFIG_SIGSTR "PCMP"

struct mp {
	u8 sig[4];
	u32 config_ptr;
	u8 len;
	u8 ver;
	u8 checksum;
	u8 f1;
	u8 f2;
	u8 fr[3];
}__attribute__((packed))*mp;

struct mp_config {
	u8 sig[4];
	u16 len;
	u8 ver;
	u8 checksum;
	u8 oem_id[8];
	u8 prod_id[12];
	u32 oem_ptr;	/*For an optional oem table*/
	u16 oem_sz;
	u16 nr_entries;
	u32 lapic_addr;
	u16 ext_len;
	u8 ext_checksum;
}__attribute__((packed))*mp_config;

#define ENT_PROC 0
#define ENT_BUS 1
#define ENT_IOAPIC 2
#define ENT_IOINTA 3	 /*IO interrupt assignment*/
#define ENT_LINTA 4		/*Local interrupt assignment*/


/*A processor entry*/
struct mp_proc {
	u8 type;
	u8 lapic_id;
	u8 lapic_ver;
	u8 flags; /*bit0:enabled proc, bit1:bootstrap*/
	u32 sig;
	u32 feat;
	u8 resvd[8];
}__attribute__((packed));

/*ioapic entry*/
struct mp_ioapic {
	u8 type;
	u8 id;
	u8 ver;
	u8 flags; /*bit0: enabled ioapic*/
	u32 addr;
}__attribute__((packed));

#define MP_PROC_SZ 20
#define MP_IOAPIC_SZ 8
#define MP_BUS_SZ 8
#define MP_IOINTA_SZ 8
#define MP_LINTA_SZ 8

struct processor {
	struct mp_proc *proc; /*Note:This points to actual entry by BIOS. We have not made a local copy*/
	struct processor *next;
}*processors=NULL;

struct ioapic {
	struct mp_ioapic *ioapic;
	struct ioapic *next;
}*ioapics=NULL;

/*This is just a local linked list, nothing to do with the bios tables*/
/*Add a processor to the linked list*/
int add_processor(struct mp_proc *x)
{
	struct processor *tail;
	if(!((x->flags) & 1)) {/*Disabled*/
		return NULL;
	}
	if(x->flags & 2) /*Bootstrap*/
		printk(":boot");
	if(!processors) {
		processors=new(struct processor);
		processors->next=NULL;
		tail=processors;
	} else {
		for(tail=processors;tail->next;tail=tail->next);
		tail->next=new(struct processor);
		tail=tail->next;
		tail->next=NULL;
	}
	tail->proc=x;
	return NULL;
}

/*Add an ioapic to the linked list*/
int add_ioapic(struct mp_ioapic *x)
{
	struct ioapic *tail;
	if(!ioapics) {
		ioapics=new(struct ioapic);
		ioapics->next=NULL;
		tail=ioapics;
	} else {
		for(tail=ioapics;tail->next;tail=tail->next);
		tail->next=new(struct ioapic);
		tail=tail->next;
		tail->next=NULL;
	}
	tail->ioapic=x;
	return NULL;
}

/*Build list by scanning the entries after the config table*/
int build_list()
{
	int i;
	u8 *ent;
	struct mp_proc *proc;
	struct mp_ioapic *ioapic;
	mp_config=(struct mp_config *)mp->config_ptr;
	if((*((u32 *)mp_config->sig))!=MP_CONFIG_SIGL)
		return ELLERR;
	printk("PCMP %x",mp_config); /*found a PCMP*/
	ent=(char *)((u32)mp_config+sizeof(struct mp_config)); /*Go to the array of entries*/
	for(i=0;i<mp_config->nr_entries;i++) {
		switch(ent[0]) { /*Read the type*/
			case ENT_PROC:
				printk(".proc");
				add_processor((struct mp_proc *)ent);
				ent+=MP_PROC_SZ;
				break;
			case ENT_BUS:
				printk(".bus");
				ent+=MP_BUS_SZ;
				break;
			case ENT_IOAPIC:
				printk(".ioapic");
				add_ioapic((struct mp_ioapic *)ent);
				ent+=MP_IOAPIC_SZ;
				break;
			case ENT_IOINTA:
				printk(".iointa");
				ent+=MP_IOINTA_SZ;
				break;
			case ENT_LINTA:
				printk(".linta");
				ent+=MP_LINTA_SZ;
				break;
			default:
				goto inv_exit;
		}
	}
	inv_exit:
	return NULL;
}

int compute_checksum(char *b,int sz)
{
	int i;
	unsigned char c=0;
	for(i=0;i<sz;i++) {
		c+=b[i];
	}
	return (int)c;
}

#define EBDA_START 0x40E
#define EBDA_SEARCH_SZ 0x400

#define BASE_START 0xa0000
#define BASE_SEARCH_SZ 0x400

#define ROM_START 0xf0000
#define ROM_END 0x100000
/*Search for the mp floating pointer in the above 3 areas*/
int search_mp()
{
	u32 *i=NULL;
	int c;
	for(i=0;(u32)i<0x400;i++) { /*RAM start 1k*/
		if(*i==MP_SIGL) {
			mp=(struct mp *)i;
			return NULL;
		}
	}
	for(i=EBDA_START;(u32)i<EBDA_START+EBDA_SEARCH_SZ;i++) {
		if(*i==MP_SIGL) {
			mp=(struct mp *)i;
			return NULL;
		}
	}
	for(i=BASE_START;(u32)i<BASE_START+BASE_SEARCH_SZ;i++) {
		if(*i==MP_SIGL) {
			mp=(struct mp *)i;
			return NULL;
		}
	}
	for(i=ROM_START;(u32)i<ROM_END;i++) {
		if(*i==MP_SIGL) {
			mp=(struct mp *)i;
			return NULL;
		}
	}
	return ENODEV;
}


int init_smp()
{
	if(!search_mp()) {
		printk("\n_MP_,");
		build_list(); /*Build a linked list of procs and ioapics*/
		return NULL;
	}
}
So this code works in finding the _MP_ and the PCMP, i also get to the entries. But i find too many processors even when configuring bochs with a uniprocessor, and i don't find an ioapic which is enabled... Heres the output for Bochs 4 processors:

Booting 'K' [02:39:21 am]

kernel /kernel
[Multiboot-kludge, loadaddr=0x100000, text-and-data=0x1a9b0, bss=0xd70c, ent
ry=0x100000]


Video size:2000 initiated
Init memory allocator
Processor:Pentium 60/66 Family:5 Model:1 Stepping:3
Speed:8 Mhz,MMX FPU:initialized
_MP_,PCMP fd000.proc.proc.proc.proc.proc.proc.ioapic.iointa.linta
Init keyboard:AT
Scheduler initiating
Drive:62730 sectors
OHCI:not found
Error:Invalid parameter(s)
Cannot read disk lba:42973875
Partitions:0)FAT321)NTFS 2)FAT16
ATA hd initiated
Init finished
vm>

The output shows that it found 6 processors, with none of them being appended with a ':boot' according to the code. If the 1st bit is set then it should be a boot processor... Besides the ioapic that is found is not enabled, as i didn't get it when i didn't print the ones not enabled. The procs too are weird with some enabled, some not enabled.
I think theres some trouble in me checking the bootstrap bit. But i think its correct. So what is going on?
User avatar
lukem95
Member
Member
Posts: 536
Joined: Fri Aug 03, 2007 6:03 am
Location: Cambridge, UK

Re: SMP table scanning

Post by lukem95 »

try it on a real computer?
~ Lukem95 [ Cake ]
Release: 0.08b
Image
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: SMP table scanning

Post by Brendan »

Hi,

I took a look, and couldn't find anything that would explain the symptoms...

The "search_mp()" function is entirely dodgy though and it'd be good to actually use the "compute_checksum() is a few places (it's not used at all in the code posted). It's also a good idea to use the "static" keyword in function declarations and function definitions (where possible - e.g. whenever the function won't be used by external code) as that helps the compiler's optimizer to inline functions (and generate "function defined but not used" warning messages).

Apart from that, it might be good to support "default configurations" (where the MP Floating Pointer Structure contains a default configuration number and there is no configuration tables), but I'm guessing you'd rather get it working right before adding more code to it.

Hmm - about your dodgy "search_mp()" function - maybe you accidentally find the string "_MP_" somewhere that isn't actually the start of the MP Floating Pointer Structure. This isn't that unlikely considering that the Bochs BIOS contains code to generate this structure during boot. I'd suggest only checking every 16 bytes (as the MP Floating Pointer Structure must be on a 16-byte boundary, and because it's faster than checking at each byte), then (if you find the "_MP_" string) check if length and checksum are sane, and if they aren't sane continue checking until you do find a sane MP Floating Pointer Structure (or run out of areas to search).

However, this isn't the only reason I'm calling your "search_mp()" function dodgy. You're meant to search the first 1 KB of the EBDA if the EBDA is present (but not if there is no EBDA). If there is no EBDA (and only if there is no EBDA) you search the *last* 1 KB of "base memory" (e.g. the area from 0x0009FC00 to 0x0009FFFF or the area from 0x0007FC00 to 0x0007FFFF, depending on how much memory is there). If you still haven't found it you'd search the BIOS area from 0x000F0000 to 0x000FFFFF. Note: This means that you never need to search more than two areas out of the three possible areas.

What you're actually doing is:
  • 1) search from 0x00000000 to 0x000003FF for no reason whatsoever.
    2) search from 0x0000040E to 0x0000080E (which is probably a bug - the starting segment of the EBDA is stored at 0x0000040E, but that's not the start of the EBDA itself)
    3) search from 0x000A0000 to 0x000A03FF, just in case the MP Floating Pointer Structure happens to be in video display memory???
    4) search from 0x000F0000 to 0x000FFFFF, which is the only part you got right! ;)

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.
Post Reply