Page 1 of 1

SMP boot on qemu

Posted: Fri May 29, 2009 7:37 am
by rsteiner
Hello,

I'm doing a graduation work about SMP scheduling, but first I need to boot the cpus.
I'm having problems with init startup startup sequence.
The first cpu seems to reboot on startup, and the second cpu doesnt boot.
I based my implementation on the Intel manual, and linux and BSD implementation.

Here's my code:

Code: Select all

void inline setup_sys_smp2(){
	//while(1);
	int i;
	int max_cpu = 2, next_apic;
	int vector;

	//IA32::int_enable();
	/*Road Map*/
	kout << "Setup trampoline ... \n";

	unsigned int trampoline_base = 0x00002000;

	//memcpy((char *)trampoline_base, (char *) 0x7c00, 512);
	//memcpy((void *)trampoline_base, trampoline_data, trampoline_end - trampoline_data);
	memcpy((char *)trampoline_base, bin_data, bin_data_size);

	vector = (trampoline_base >> 12);

	kout << "Executing Waking up AP CPUS "; /*now we work with 2 cpus*/

	apic_ids[0] = Apic::get_apic_id();
	next_apic = apic_ids[0] + 1;

	// build the apic_id table
	for(i = 1; i < max_cpu; i++){
		apic_ids[i] = next_apic++;
	}
	// start the ap protocol
	for(i = 1; i < max_cpu; i++){
		//int apic_id = apic_ids[i];
		//int apic_id = Apic::get_apic_id();
		//apic_id = apic_id;
		int apic_id = 0x01;

		Apic::apic_write(Apic::APIC_ICR_HIGH, apic_id << Apic::APIC_ID_SHIFT);
		Apic::apic_write(Apic::APIC_ICR_LOW, Apic::APIC_TRIGMOD_LEVEL | 
							Apic::APIC_DELMODE_INIT | Apic::APIC_LEVEL_ASSERT );
		Apic::apic_ipi_wait(-1);

		Apic::pit_delay(10000);
		Apic::pit_delay(10000);
		//Apic::pit_delay(10000);

		kout << " .. step 1\n";
		
		//while(1);

		Apic::apic_write(Apic::APIC_ICR_HIGH, apic_id << Apic::APIC_ID_SHIFT);
		Apic::apic_write(Apic::APIC_ICR_LOW, Apic::APIC_TRIGMOD_LEVEL | 
					Apic::APIC_DELMODE_INIT);
		//Apic::apic_write(Apic::APIC_ICR_LOW, 0x000C4500);

		unsigned char *video = (unsigned char *)0xB8000;
		*(video + (2 + 2 * 80) * 2) = 'T' & 0xFF;

		Apic::apic_ipi_wait(-1);
		//Apic::pit_delay(200);
		Apic::pit_delay(10000);
		Apic::pit_delay(10000);
		//Apic::pit_delay(10000);

		//while(1);
		kout << " .. step 2\n";

//		while(1);

		Apic::apic_write(Apic::APIC_ICR_HIGH, apic_id << Apic::APIC_ID_SHIFT);
		Apic::apic_write(Apic::APIC_ICR_LOW,Apic::APIC_TRIGMOD_EDGE | Apic::APIC_DELMODE_STARTUP | vector);
		//Apic::apic_write(Apic::APIC_ICR_LOW, 0x000C4602 | vector);

//while(1);
		Apic::apic_ipi_wait(-1);
		*(video + (2 + 2 * 80) * 2) = 'U' & 0xFF;
//while(1);
		//while(1);
		Apic::pit_delay(200);
		Apic::pit_delay(200);
		Apic::pit_delay(200);
		//while(1);
		//{
			*(video + (2 + 2 * 80) * 2) = 'U' & 0xFF;
		//}
		kout << " .. step 3\n";

		//while(1);
		Apic::apic_write(Apic::APIC_ICR_HIGH, apic_id << Apic::APIC_ID_SHIFT);
		Apic::apic_write(Apic::APIC_ICR_LOW,Apic::APIC_TRIGMOD_EDGE | Apic::APIC_DELMODE_STARTUP | vector);
		//Apic::apic_write(Apic::APIC_ICR_LOW, 0x000C4602 | vector);

		Apic::apic_ipi_wait(-1);
		*(video + (2 + 2 * 80) * 2) = 'K' & 0xFF;
		Apic::pit_delay(200);
		Apic::pit_delay(200);
		//Apic::pit_delay(200);

		kout << " .. step 4\n";
		*(video + (2 + 2 * 80) * 2) = 'G' & 0xFF;
		//while(1);
	}	
}
If anyone has a working example on qemu, I would really appreciate.

Thank you

Re: SMP boot on qemu

Posted: Fri May 29, 2009 8:47 am
by Laksen
I use the following code. While it may seem a bit strange, I've yet to find a system where it doesn't work (except for older systems where you have to do the funky startup stuff). I know the timing bit is irresponsible, but well... It works so far :P

Code: Select all

var i: longint;
begin
   LAPICSendIPI(0, Destination, dInit, false, tmLevel, true, dsNoShorthand);
   for i := 0 to 20000 do
	begin
		asm 
			nop
		end;
	end;
	LAPICSendIPI(0, Destination, dInit, false, tmLevel, true, dsNoShorthand);
   for i := 0 to 40000 do
	begin
		asm
			nop
		end;
	end;
	LAPICSendIPI((BootAddress div 4096), Destination, dStartUp, false, tmLevel, false, dsNoShorthand);
end;

Code: Select all

type
 TDelivery = (dFixed = 0, dLowPriority = 1, dSMI = 2, dNMI = 4, dINIT = 5, dStartUp = 6);
 TTriggerMode = (tmLevel = 0, tmEdge = 1);
 TDestShorthand = (dsNoShorthand, dsSelf, dsAllSelf, dsAllNoSelf);

procedure LAPICSendIPI(Vector: byte; DestField: longword; Delivery: TDelivery; DestLogical: boolean; Trigger: TTriggerMode; DeAssert: boolean; DestShorthand: TDestShorthand);
begin
	ApicSet(ApicICRhi, DestField shl 24);
	ApicSet(ApicICRlo, Vector or (byte(Delivery) shl 8) or (byte(DestLogical) shl 11) or (byte(DeAssert) shl 14) or (byte(Trigger) shl 15) or (Byte(DestShorthand) shl 18));
end;

Re: SMP boot on qemu

Posted: Fri May 29, 2009 10:16 pm
by thooot
The minimum you need for Qemu is to enable the APIC and send a SIPI. Note that on real hardware you will need more. But this code works for me (my real code that I'm using does an INIT, SIPI, SIPI sequence with proper timeouts...):

Code: Select all

#define APIC_SPURIOUS_INTERRUPT_VECTOR      0x0F
#define APIC_INTERRUPT_COMMAND_REGISTER     0x30

uint32 KeReadAPICRegister(uint32 reg) {
    return *((volatile uint32*) (apicInfo->APICAddress + reg * 16));
}

void KeWriteAPICRegister(uint32 reg, uint32 value) {
    *((volatile uint32*) (apicInfo->APICAddress + reg * 16)) = value;
}

void KeSendSIPI(uint32 apicId, uint32 addr) {
    KeAPICICR icr;

    icr.Vector = (addr >> 12) & 0xFF;
    icr.DeliveryMode = 6;                   // Start up IPI
    icr.DestinationMode = 0;                // Physical
    icr.DeliveryStatus = 0;                 // Idle
    icr.Reserved1 = 0;
    icr.Level = 1;                          // Assert
    icr.TriggerMode = 0;                    // Edge
    icr.Reserved2 = 0;
    icr.DestinationShorthand = 0;           // No shorthand
    icr.Reserved3 = 0;
    icr.Reserved4 = 0;
    icr.Destination = apicId;               // Processor to send SIPI to

    KeWriteAPICRegister(APIC_INTERRUPT_COMMAND_REGISTER + 1, icr.high);
    KeWriteAPICRegister(APIC_INTERRUPT_COMMAND_REGISTER, icr.low);
}

void KeInitAPIC() {
    // Make sure the APIC is enabled by writing to the spurious interrupt vector
    KeWriteAPICRegister(APIC_SPURIOUS_INTERRUPT_VECTOR, KeReadAPICRegister(APIC_SPURIOUS_INTERRUPT_VECTOR) | 0x100);

    // Copy trampoline code to 0x20000
    memcpy((uint8*)KeGetDirectMappedAddress(fs->files[curFile].address), (uint8*)KeGetDirectMappedAddress(0x20000), fs->files[curFile].size);

    KeSendSIPI(1, 0x20000);
}