Autor Thema: x86-Startup  (Gelesen 6528 mal)

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« am: 15. December 2015, 23:34 »
Hallo zusammen,

habe mir mal ein wenig Code zusammen gebastelt, um die AP-Prozessoren zu starten.
Leider bekomme ich bei den IPIs immer Fehlercodes zurück.

Hier ist meine LAPIC-Initialisierung des BSP:
static __inline int __MaskLVT(ApicRegister i_register) {
    UInt32 value;

    int retval = CPU_ApicRead(i_register, &value);
    if (ESuccess != retval)
        return retval;

    return CPU_ApicWrite(i_register, value | (1 << 16));
}

static __inline int __CleanLApic() {
    UInt32 maxLvt;

    int retval = CPU_ApicRead(LocalApicVersion, &maxLvt);
    if (ESuccess != retval)
        return retval;
    maxLvt = (maxLvt >> 16) & 0xff;

    if (3 <= maxLvt) {
        retval = CPU_ApicWrite(LvtError, 0xfe | (1 << 16));
        if (ESuccess != retval)
            return retval;
    }

    retval = __MaskLVT(LvtTimer);
    if (ESuccess != retval)
        return retval;
    retval = __MaskLVT(LvtLInt0);
    if (ESuccess != retval)
        return retval;
    retval = __MaskLVT(LvtLInt1);
    if (ESuccess != retval)
        return retval;
    if (4 <= maxLvt) {
        retval = __MaskLVT(LvtPerformanceMonitoringCounters);
        if (ESuccess != retval)
            return retval;
    }

    retval = CPU_ApicWrite(LvtTimer, 1 << 16);
    if (ESuccess != retval)
        return retval;
    retval = CPU_ApicWrite(LvtLInt0, 1 << 16);
    if (ESuccess != retval)
        return retval;
    retval = CPU_ApicWrite(LvtLInt1, 1 << 16);
    if (ESuccess != retval)
        return retval;
    if (3 <= maxLvt) {
        retval = CPU_ApicWrite(LvtError, 1 << 16);
        if (ESuccess != retval)
            return retval;
    }
    if (4 <= maxLvt) {
        retval = CPU_ApicWrite(LvtPerformanceMonitoringCounters, 1 << 16);
        if (ESuccess != retval)
            return retval;
    }

    return ESuccess;
}

static __inline int __InitializeLogicDestination() {
    UInt32 value;
    int retval = CPU_ApicWrite(DestinationFormat, 0xffffffff);
    if (ESuccess != retval)
        return retval;
    retval = CPU_ApicRead(LogicalDestination, &value);
    if (ESuccess != retval)
        return retval;
    value &= ~(0xff << 24);

    UInt32 cpuId;
    retval = CPU_GetProcessorId(&cpuId);
    if (ESuccess != retval)
        return retval;
    value |= ((1 << cpuId) << 24);

    return CPU_ApicWrite(LogicalDestination, value);
}

int CPU_ApicInitialize() {
    UInt32 value;

    /* disable the PIC */
    CPU_IOPortWriteByte(0xa1, 0xff);
    CPU_IOPortWriteByte(0x21, 0xff);

    int retval = __CleanLApic();
    if (ESuccess != retval)
        return retval;

    retval = __InitializeLogicDestination();
    if (ESuccess != retval)
        return retval;

    /* set the task priority */
    retval = CPU_ApicRead(TaskPriority, &value);
    if (ESuccess != retval)
        return retval;
    value &= ~0xff;
    retval = CPU_ApicWrite(TaskPriority, value);
    if (ESuccess != retval)
        return retval;

    /* all done - activate the lapic */
    retval = CPU_ApicRead(SpuriousInterruptVector, &value);
    if (ESuccess != retval)
        return retval;
    value &= ~0xff;
    value |= (1 << 8) | 0xff;
    retval = CPU_ApicWrite(SpuriousInterruptVector, value);
    if (ESuccess != retval)
        return retval;

    return ESuccess;
}

Wenn ich über das MSR prüfe, ob die LAPIC aktiv ist, bekomme ich auch eine positive Rückmeldung.

Hier mein SMP-Init-Code (dabei verwende ich INIT-INIT-STARTUP):
static __inline int __BootApplicationProcessor(UInt16 i_cpuCount, UInt32 i_stacks[]) {
    UIntPtr size = (UIntPtr)__trampoline_smp_end - (UIntPtr)__trampoline_smp_start;
    UInt32* stackPtr = (UInt32*)(0x7000 + size - 8);
    UInt8 acpis[i_cpuCount];
    UInt32 status;

    int retval = ACPI_GetProcessorInformation(acpis);
    if (ESuccess != retval) {
        KLog(Error, "Unable to get processor information: %d\n", retval);
        return retval;
    }

    UInt32 ownId;
    retval = CPU_GetProcessorId(&ownId);
    if (ESuccess != retval) {
        KLog(Error, "Unable to get the own CPU-ID: %d\n", retval);
        return retval;
    }

    UInt32 low, high;
    CPU_ReadMsr(0x1B, &low, &high);
    if (0 == (0x800 & low)) {
        KLog(Error, "LAPIC is not disabled\n");
        return -ENoPermission;
    }

    UInt32 value;
    retval = CPU_ApicRead(ErrorStatus, &value);

    for (UInt16 i = 0, c = 0; i < i_cpuCount; i++) {
        if (ownId == acpis[i])
            continue;

        *stackPtr = i_stacks[c++];
        KLog(Info, "Bringing up CPU #%u...", acpis[i]);
        currentBootId = acpis[i];

        KLog(Debug, "Sending INIT-IPI-1...");
        retval = __SendIPI(0xc500, &status);
        if (ESuccess != retval)
            return retval;
        KLog(Debug, "%#x\n", status);

        __UDelay(10000);

        KLog(Debug, "Sending INIT-IPI-2...");
        retval = __SendIPI(0x8500, &status);
        if (ESuccess != retval)
            return retval;
        KLog(Debug, "%#x\n", status);

        retval = CPU_ApicRead(ErrorStatus, &value);
        if (ESuccess != retval)
            return retval;

        KLog(Debug, "Sending STARTUP-IPI...");
        retval = __SendIPI(0x600 | (0x7000 >> 12), &status);
        if (ESuccess != retval)
            return retval;
        KLog(Debug, "%#x\n", status);

        __UDelay(300);
        __WaitForApic(&status);
        __UDelay(200);

        retval = CPU_ApicRead(ErrorStatus, &value);
        UInt32 accept = value & 0xef;

        if (0 == accept && 0 == status) {
            while ((UInt32)~0x0 != currentBootId) ;
            continue;
        }

        if (0 != accept)
            KLog(Error, "APIC-delivery: %#x\n", accept);
        if (0 != status)
            KLog(Error, "APIC not delivered: %#x\n", status);

        return -EBusy;
    }

    return ESuccess;
}

status innerhalb der __SendIPI-Aufrufe gibt immer 0x1000 zurück.
Ich sorge durch io_delay auch dafür, dass ich die erforderliche Zeit warte, damit die APs genug Zeit bekommen.

Getestet ist der Code unter QEmu.

Sieht einer von euch den Fehler?
Die APIC-IDs hole ich mir über die ACPI-MADT-Tabelle.

Viele Grüße
rizor
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

xenos1984

  • Beiträge: 9
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 15. December 2015, 23:55 »
Ich würde den Code mal in Bochs laufen lassen und schauen, was die Log-Datei zum Thema APICs sagt.

Davon abgesehen: Sollte die IPI-Sequenz nicht eher INIT-STARTUP-STARTUP sein? (Wobei der INIT einmal mit "assert" eingeschaltet und mit "deassert" gleich wieder ausgeschaltet wird, danach kommen 10ms Pause, dann STARTUP, dann 200µs Pause, und falls der AP dann nicht läuft noch mal ein STARTUP.)

rizor

  • Beiträge: 521
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 23. December 2015, 17:15 »
Danke fuer den Tipp. Hatte bisher nur mit QEmu getestet.
Jetzt klappt es und die anderen CPUs booten
Programmiertechnik:
Vermeide in Assembler zu programmieren wann immer es geht.

 

Einloggen