try to boot other cpu in lapic
try to boot other cpu in lapic
In order to support mulltiprocessors,I try to write code to boot other cpus.the follow is the address I use: http://www.scs.stanford.edu/histar/src/kern/dev
and the code is apic.c.
I have some questions:1)should the lapic location must be 0xfee00000,even in an 64bit os,and if it is,should I write to the location directly without mapping or not when I write code at the location .
2)since the OS I write is an 64bit OS ,so I map the 0xfee00000 to a address in my OS , however when I run the code ,the cpus cannot be booted correctly,it seems like a deadlock happens ,can anyone give me some tips? Thanks in advance~
and the code is apic.c.
I have some questions:1)should the lapic location must be 0xfee00000,even in an 64bit os,and if it is,should I write to the location directly without mapping or not when I write code at the location .
2)since the OS I write is an 64bit OS ,so I map the 0xfee00000 to a address in my OS , however when I run the code ,the cpus cannot be booted correctly,it seems like a deadlock happens ,can anyone give me some tips? Thanks in advance~
- Combuster
- Member
- Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 1½m distance
- Contact:
Re: try to boot other cpu in lapic
1) The location is a hardcoded value on old CPUs and can be found in the APIC BASE model-specifc-register on anything current.
2) the APIC is mapped in the physical address space. Paging and segmentation will apply.
2) the APIC is mapped in the physical address space. Paging and segmentation will apply.
Re: try to boot other cpu in lapic
Hi,
Cheers,
Brendan
This code is for systems with an external local APIC (e.g. an 82489DX chip) and isn't suitable for anything with a local APIC built into the CPU. This means that it's useless for anything that has a "Pentium or later" CPU in it. In theory this code will work on "80486 or older" systems that support SMP; but in practice these systems are so rare that it's impossible to tell if any of them still exist.hfcao wrote:In order to support mulltiprocessors,I try to write code to boot other cpus.the follow is the address I use: http://www.scs.stanford.edu/histar/src/kern/dev
and the code is apic.c.
The safest way is to assume the local APIC could be anywhere (and not just at 0xFEE00000) and find the physical address of the local APIC from the ACPI or MP specification tables when/while you're finding out the APIC IDs for AP CPUs.hfcao wrote:I have some questions:1)should the lapic location must be 0xfee00000,even in an 64bit os,and if it is,should I write to the location directly without mapping or not when I write code at the location .
My tip is, never automatically trust any information provided by any university. Even if a university says "frozen water is cold", double check.hfcao wrote:2)since the OS I write is an 64bit OS ,so I map the 0xfee00000 to a address in my OS , however when I run the code ,the cpus cannot be booted correctly,it seems like a deadlock happens ,can anyone give me some tips? Thanks in advance~
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.
Re: try to boot other cpu in lapic
Thanks for your tips.I don't how to detect whether the AP has been waked up by the BP or not?
- gravaera
- Member
- Posts: 737
- Joined: Tue Jun 02, 2009 4:35 pm
- Location: Supporting the cause: Use \tabs to indent code. NOT \x20 spaces.
Re: try to boot other cpu in lapic
Yo:
You don't detect that -- you assume that the AP is in the correct state based on the multiprocessor specification. If a board does not comply with the predefined MP state at handover to the OS, then it is not MP compatible, and you can assume it that multiprocessing should not be used.
Realistically though, a core not being left in the "wait for IPI" state isn't going to interfere with your MP setup; at least, not to my knowledge \o/
--Peace out,
gravaera
You don't detect that -- you assume that the AP is in the correct state based on the multiprocessor specification. If a board does not comply with the predefined MP state at handover to the OS, then it is not MP compatible, and you can assume it that multiprocessing should not be used.
Realistically though, a core not being left in the "wait for IPI" state isn't going to interfere with your MP setup; at least, not to my knowledge \o/
--Peace out,
gravaera
17:56 < sortie> Paging is called paging because you need to draw it on pages in your notebook to succeed at it.
Re: try to boot other cpu in lapic
Hi,
Cheers,
Brendan
Normally in your AP CPU startup code you'd set some sort of flag or variable (in memory) as soon as you can. The BSP would wait for this flag to be set, with a timeout. If the flag hasn't been set within some amount of time (e.g. maybe 20 ms) the BSP stops waiting and adds a "Failed to start AP" error message to the boot log or something.hfcao wrote:I don't how to detect whether the AP has been waked up by the BP or not?
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.
Re: try to boot other cpu in lapic
Thank you so much for your replies~
1)I try to understand the following code ,however I still don't know one sentence:,
why the physical address should be shift right by 12 bit? .
2)when I try to modify this code,it seems that the AP cannot get the right address to start executing the routines,it just halt.and I doubt the executing address I try to send to AP is wrong.
1)I try to understand the following code ,however I still don't know one sentence:
Code: Select all
apic_write(LAPIC_ICRLO, LAPIC_DLMODE_STARTUP | (pa >> 12));
why the physical address should be shift right by 12 bit? .
2)when I try to modify this code,it seems that the AP cannot get the right address to start executing the routines,it just halt.and I doubt the executing address I try to send to AP is wrong.
Code: Select all
void
apic_start_ap(uint32_t apicid, physaddr_t pa)
{
// Universal Start-up Algorithm from Intel MultiProcessor spec
int r;
uint16_t *dwordptr;
// "The BSP must initialize CMOS shutdown code to 0Ah ..."
outb(IO_RTC, NVRAM_RESET);
outb(IO_RTC + 1, NVRAM_RESET_JUMP);
// "and the warm reset vector (DWORD based at 40:67) to point
// to the AP startup code ..."
dwordptr = pa2kva(0x467);
dwordptr[0] = 0;
dwordptr[1] = pa >> 4;
// ... prior to executing the following sequence:"
if ((r = ipi_init(apicid)) < 0)
panic("unable to send init");
timer_delay(10 * 1000000); // 10ms
for (uint32_t i = 0; i < 2; i++) {
apic_icr_wait();
apic_write(LAPIC_ICRHI, apicid <<24);
apic_write(LAPIC_ICRLO, STARTUP | (pa >> 12));
timer_delay(200 * 1000); // 200us
}
}
Re: try to boot other cpu in lapic
Hi,
Ok.
Unnecessary variable (see below).
This is all unnecessary (only needed for 80486 or older SMP systems that don't exist).
I don't know how many bugs are in "ipi_init()" - possibly a lot.
Should just do "if ( ipi_init(apicid) < 0)" because you don't use the value in "r" anyway.
Probably don't need to do the first "apic_write()" here, as the destination APIC ID should still be set after the "ipi_init()".
The second "apic_write()" looks right, but I don't know what the code in the "apic_write()" function does, or if LAPIC_ICRLO is correct, or if STARTUP is correct, or if "pa" is sane (e.g. if pa is the address 0x123, then..).
Cheers,
Brendan
Code: Select all
void apic_start_ap(uint32_t apicid, physaddr_t pa)
{
// Universal Start-up Algorithm from Intel MultiProcessor spec
Code: Select all
int r;
Code: Select all
uint16_t *dwordptr;
// "The BSP must initialize CMOS shutdown code to 0Ah ..."
outb(IO_RTC, NVRAM_RESET);
outb(IO_RTC + 1, NVRAM_RESET_JUMP);
// "and the warm reset vector (DWORD based at 40:67) to point
// to the AP startup code ..."
dwordptr = pa2kva(0x467);
dwordptr[0] = 0;
dwordptr[1] = pa >> 4;
Code: Select all
// ... prior to executing the following sequence:"
if ((r = ipi_init(apicid)) < 0)
panic("unable to send init");
timer_delay(10 * 1000000); // 10ms
Should just do "if ( ipi_init(apicid) < 0)" because you don't use the value in "r" anyway.
Code: Select all
for (uint32_t i = 0; i < 2; i++) {
apic_icr_wait();
apic_write(LAPIC_ICRHI, apicid <<24);
apic_write(LAPIC_ICRLO, STARTUP | (pa >> 12));
timer_delay(200 * 1000); // 200us
}
}
The second "apic_write()" looks right, but I don't know what the code in the "apic_write()" function does, or if LAPIC_ICRLO is correct, or if STARTUP is correct, or if "pa" is sane (e.g. if pa is the address 0x123, then..).
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.
Re: try to boot other cpu in lapic
1)the code of apic_write like this :
2)
3)
Code: Select all
static void
apic_write(uint32_t off, uint32_t val)
{
*(volatile uint32_t *) (pa2kva(LAPIC_BASE) + off) = val;
}
the following is the ipi_init()I don't know how many bugs are in "ipi_init()" - possibly a lot.
Code: Select all
static int
ipi_init(uint32_t apicid)
{
// Intel MultiProcessor spec. section B.4.1
apic_write(LAPIC_ICRHI, apicid << LAPIC_ID_SHIFT);
apic_write(LAPIC_ICRLO, apicid | LAPIC_DLMODE_INIT | LAPIC_LVL_TRIG |
LAPIC_LVL_ASSERT);
apic_icr_wait();
timer_delay(10 * 1000000); // 10ms
apic_write(LAPIC_ICRLO, apicid | LAPIC_DLMODE_INIT | LAPIC_LVL_TRIG |
LAPIC_LVL_DEASSERT);
apic_icr_wait();
return (apic_read(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) ? -1 : 0;
}
the LAPIC_ICRLO is 0x300, and STARTUP is 0x00000600.Are there some errors?thanks~The second "apic_write()" looks right, but I don't know what the code in the "apic_write()" function does, or if LAPIC_ICRLO is correct, or if STARTUP is correct, or if "pa" is sane (e.g. if pa is the address 0x123, then..).
Re: try to boot other cpu in lapic
Hi,
I assume that "LAPIC_DLSTAT_BUSY" is the delivery status flag. This tells you if the local APIC is still sending, not if it failed to send. Normally you'd test this in a loop (with a timeout) before you send an IPI (not after). For example:
To detect errors, see the Error Status Register (but be very careful with race conditions).
Cheers,
Brendan
That looks OK to me.hfcao wrote:1)the code of apic_write like this :Code: Select all
static void apic_write(uint32_t off, uint32_t val) { *(volatile uint32_t *) (pa2kva(LAPIC_BASE) + off) = val; }
That code is only for "80486 or older". You only need to send one "INIT assert".hfcao wrote:2)the following is the ipi_init()I don't know how many bugs are in "ipi_init()" - possibly a lot.Code: Select all
static int ipi_init(uint32_t apicid) { // Intel MultiProcessor spec. section B.4.1 apic_write(LAPIC_ICRHI, apicid << LAPIC_ID_SHIFT); apic_write(LAPIC_ICRLO, apicid | LAPIC_DLMODE_INIT | LAPIC_LVL_TRIG | LAPIC_LVL_ASSERT); apic_icr_wait(); timer_delay(10 * 1000000); // 10ms apic_write(LAPIC_ICRLO, apicid | LAPIC_DLMODE_INIT | LAPIC_LVL_TRIG | LAPIC_LVL_DEASSERT); apic_icr_wait(); return (apic_read(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) ? -1 : 0; }
I assume that "LAPIC_DLSTAT_BUSY" is the delivery status flag. This tells you if the local APIC is still sending, not if it failed to send. Normally you'd test this in a loop (with a timeout) before you send an IPI (not after). For example:
Code: Select all
while( delivery_status_is_set() ) {
/* Wait for it to finish sending previous IPI */
}
send_IPI(); /* Send the next IPI */
That sounds right to me too.hfcao wrote:3)the LAPIC_ICRLO is 0x300, and STARTUP is 0x00000600.Are there some errors?thanks~The second "apic_write()" looks right, but I don't know what the code in the "apic_write()" function does, or if LAPIC_ICRLO is correct, or if STARTUP is correct, or if "pa" is sane (e.g. if pa is the address 0x123, then..).
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.
Re: try to boot other cpu in lapic
thaks~
and the lapic_read() is
and it just print "lapic error:ESR:0"
does it means it is right?
I use the following code to print the contents of Error Status RegisterTo detect errors, see the Error Status Register (but be very careful with race conditions).
Code: Select all
printf("lapic error:ESR:%x\n",apic_read(ESR))
Code: Select all
static uint32_t
apic_read(uint32_t off)
{
return *(volatile uint32_t *) (pa2kva(LAPIC_BASE) + off);
}
does it means it is right?
Re: try to boot other cpu in lapic
I forgot to say the platform I use is AMD 64bit machine ,does that affect? (Since I think MP Specification is an open standard.I don't I am right or wrong?)
Re: try to boot other cpu in lapic
Hi,
Also note that whatever the problem is that you're currently having (I lost track), it may have nothing to do with the AP startup code itself. For a silly/hypothetical example, a bug in your virtual memory manager could mean that you're not actually reading/writing to the local APIC at all; or a bug in your real mode "trampoline" might make it look like the AP CPU didn't start even though it did.
Cheers,
Brendan
hfcao wrote:I use the following code to print the contents of Error Status RegisterTo detect errors, see the Error Status Register (but be very careful with race conditions).
It could mean that it was right, or it could just mean that the hardware didn't update the Error Status Register until after you read it.hfcao wrote:and it just print "lapic error:ESR:0"
does it means it is right?
If the MP Specification tables exist and are valid, then it shouldn't matter if the CPU is 64-bit or if the OS is 64-bit. However, the MP Specification tables have become obsolete (replaced by ACPI's "MADT/APIC" table), and for modern hardware the MP Specification tables may not exist or may not be valid (e.g. just an minimal stub that says there's only one CPU and not much else, even though there are more CPUs, etc).hfcao wrote:I forgot to say the platform I use is AMD 64bit machine ,does that affect? (Since I think MP Specification is an open standard.I don't I am right or wrong?)
Also note that whatever the problem is that you're currently having (I lost track), it may have nothing to do with the AP startup code itself. For a silly/hypothetical example, a bug in your virtual memory manager could mean that you're not actually reading/writing to the local APIC at all; or a bug in your real mode "trampoline" might make it look like the AP CPU didn't start even though it did.
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.
- Owen
- Member
- Posts: 1700
- Joined: Fri Jun 13, 2008 3:21 pm
- Location: Cambridge, United Kingdom
- Contact:
Re: try to boot other cpu in lapic
It's much worse than that: The MP specification tables exist, appear valid, and are wrong in subtle ways.Brendan wrote: However, the MP Specification tables have become obsolete (replaced by ACPI's "MADT/APIC" table), and for modern hardware the MP Specification tables may not exist or may not be valid (e.g. just an minimal stub that says there's only one CPU and not much else, even though there are more CPUs, etc).
In general, expect the MP tables to be generated by some BIOS code which has been copied and pasted from BIOS to BIOS for the last 5 years with no maintenance (because nobody cares about NT 4 any more) and only continues to exist because it would cost money to dike it out and nobody cares ("It boots Windows 2000 through 7. Ship it")