Как использовать APIC для создания IPI для пробуждения точек доступа для SMP в сборке x86?

В постзагрузочной среде (без ОС) как можно использовать BSP (первое ядро/процессор) для создания IPI для точек доступа (все остальные ядра/процессоры)? По сути, как разбудить и установить указатель инструкций для других ядер при запуске с одного?


person QRXQ    schedule 03.05.2013    source источник
comment
В этом вопросе показан базовый алгоритм запуска другого процессора. Это то, что вы ищете, или вам нужны подробности о том, как именно отправлять IPI? (В любом случае я бы указал вам на вики OSDev и форумы, на которых есть много полезной информации.)   -  person ughoavgfhw    schedule 04.05.2013
comment
Минимальный пример: stackoverflow.com/a/33651438/895245   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 11.11.2015


Ответы (1)


ВНИМАНИЕ: здесь я предположил 80x86. Если не 80х86 то не знаю :-)

Сначала вам нужно узнать, сколько других процессоров существует и каковы их идентификаторы APIC, а также определить физический адрес локальных APIC. Для этого вы анализируете таблицы ACPI (см. MADT/APIC в спецификации ACPI). Если вы не можете найти действительные таблицы ACPI (например, компьютер слишком стар), существует более старая «Спецификация многопроцессорности», которая определяет свои собственные таблицы с той же информацией. Обратите внимание, что «Спецификация многопроцессорности» теперь устарела (и есть некоторые компьютеры с фиктивными многопроцессорными таблицами), поэтому вам необходимо сначала проверить таблицы ACPI.

Следующий шаг — определить, какой у вас тип локального APIC. Есть 3 случая - старые внешние локальные APIC "82489DX" (не встроенные в сам ЦП), xAPIC и x2APIC.

Начните с проверки CPUID, чтобы определить, является ли локальный APIC x2APIC. Если это так, у вас есть 2 варианта: вы можете использовать x2APIC или использовать «режим совместимости xAPIC». Для «режима совместимости с xAPIC» вы можете использовать только 8-битные идентификаторы APIC и не сможете поддерживать компьютеры с большим количеством ЦП (например, 255 или более ЦП). Я бы рекомендовал использовать x2APIC (даже если вас не интересуют компьютеры с большим количеством процессоров), так как это быстрее. Если вы используете режим x2APIC, вам необходимо переключить локальный APIC в этот режим.

В противном случае, если это не x2APIC, прочитайте регистр версии локального APIC. Если версия локального APIC 0x10 или выше, то это xAPIC, а если 0x0F или ниже, то это внешний локальный APIC "82489DX".

Старые внешние локальные APIC «82489DX» использовались в компьютерах 80486 и более старых, и они крайне редки (они были очень редки 20 лет назад, затем большинство из них умерло и / или с тех пор было заменено и выброшено). Поскольку для запуска других ЦП используется другая последовательность, а компьютеры с этими локальными APIC крайне редки (например, вы, вероятно, никогда не сможете протестировать свой код), имеет смысл не заботиться о поддержке этих компьютеров. Если вы вообще поддерживаете эти старые компьютеры; Я бы рекомендовал рассматривать их как «только однопроцессорные» и просто не запускать другие ЦП, если локальный APIC «82489DX». По этой причине я не буду здесь описывать способ их запуска (он описан в «Спецификации многопроцессорных процессов» Intel, если вам интересно).

Для xAPIC и x2APIC последовательность запуска другого ЦП практически одинакова (только разные способы доступа к локальному APIC — MSR или отображение памяти). Я бы рекомендовал использовать (например) указатели функций, чтобы скрыть эти различия; так что более поздний код может вызывать функцию «отправить IPI» через. указатель на функцию, не заботясь о том, является ли локальный APIC x2APIC или xAPIC.

Чтобы фактически запустить другой ЦП, вам необходимо отправить ему последовательность IPI (межпроцессорных прерываний). Метод Intel выглядит следующим образом:

Send an INIT IPI to the CPU you're starting
Wait for 10 ms
Send a STARTUP IPI to the CPU you're starting
Wait for 200 us
Send another STARTUP IPI to the CPU you're starting
Wait for 200 us
Wait for started CPU to set a flag (so you know it started)
    If flag was set by other CPU, other CPU was started successfully
    Else if time-out, other CPU failed to start

Есть 2 проблемы с методом Intel. Часто другой ЦП будет запущен первым STARTUP IPI, и в некоторых случаях это может привести к проблемам (например, если код запуска другого ЦП делает что-то вроде total_CPUs++;, тогда каждый ЦП может выполнить его дважды. Чтобы избежать этой проблемы, вы можете добавить дополнительные синхронизация (например, другой ЦП ждет, пока первый ЦП установит флаг «Я знаю, что вы начали», прежде чем продолжить). Вторая проблема с методом Intel заключается в измерении этих задержек. Обычно ОС запускает другие ЦП, а затем выясняет, что включает поддержку ЦП и то, какое оборудование присутствует после этого, и не имеет точной настройки таймера / с для точного измерения этих задержек в 200 мкс.

Чтобы избежать этих проблем; Я использую альтернативный метод, который выглядит следующим образом:

Send an INIT IPI to the CPU you're starting
Wait for 10 ms
Send a STARTUP IPI to the CPU you're starting
Wait for started CPU to set a flag (so you know it started) with a short timeout (e.g. 1 ms)
    If flag was set by other CPU, other CPU was started successfully
    Else if time-out
        Send another STARTUP IPI to the CPU you're starting
        Wait for started CPU to set a flag with a long timeout (e.g. 200 ms)
            If flag was set by other CPU, other CPU was started successfully
            Else if time-out, other CPU failed to start
If CPU started successfully
    Set flag to tell other CPU it can continue

Также обратите внимание, что вам нужно запускать процессоры по отдельности. Я видел, как люди запускали все процессоры одновременно, используя функцию «транслировать IPI всем, кроме себя» — это неправильно, неработоспособно и хитро (не делайте этого, если вы не пишете прошивку). Проблема заключается в том, что некоторые ЦП могут быть неисправны (например, не прошли проверку BIST/встроенного самотестирования), а некоторые ЦП могут быть отключены (например, гиперпоточность, когда гиперпоточность отключена в прошивке); а метод «транслировать IPI всем, кроме себя» может запускать процессоры, которые никогда не должны были запускаться.

Наконец, для компьютеров с большим количеством ЦП запуск их всех может занять относительно много времени, если вы запускаете их по одному. Например, если для запуска каждого процессора требуется 11 мс, а процессоров 128, то это займет 1,4 секунды. Если вы хотите загружаться быстро, есть способы избежать этого. Например, первый ЦП может запустить второй ЦП, затем 1-й и 2-й ЦП могут запустить 3-й и 4-й ЦП, затем эти четыре ЦП могут запустить следующие четыре ЦП и т. д. Таким образом, вы можете запустить 128 ЦП за 77 мс. вместо 1,4 секунды.

Примечание. Я бы рекомендовал запускать ЦП по одному и убедиться, что это работает, прежде чем пытаться выполнить какой-либо "параллельный запуск" (об этом вы можете беспокоиться после того, как узнаете, что остальные работают).

Адрес, с которого начнут выполняться другие ЦП, закодирован в поле «вектор» STARTUP IPI. ЦП начнет выполнение кода (в реальном режиме) с CS = vector * 256 и IP = 0. Поле вектора 8-битное, поэтому максимальный начальный адрес, который вы можете использовать, — 0x000FF000 (0xFF00:0x0000 в реальном режиме). Однако это устаревшая область ПЗУ (на практике начальный адрес должен быть ниже). Обычно вы копируете небольшой фрагмент кода запуска на подходящий адрес; где код запуска обрабатывает синхронизацию (например, устанавливает флаг «Я начал», который может видеть другой ЦП и ждет, когда ему сообщат, что все в порядке для продолжения), а затем выполняет такие действия, как включение защищенного/длинного режима и настройка стека перед переходом к записи точку в обычном коде ОС. Этот небольшой фрагмент кода запуска называется «батутом запуска ЦП AP». Это также делает "параллельный запуск" немного сложным; так как каждому запускаемому процессору нужны свои/отдельные флаги синхронизации и стек; и поскольку эти вещи обычно реализуются с переменными в батуте (например, mov esp,[cs:stackTop]), это означает, что в конечном итоге будет несколько батутов.

person Brendan    schedule 03.05.2013
comment
Как вы ждете заданное количество времени? Связанные stackoverflow.com/questions/ 9971405/ - person Ciro Santilli 新疆再教育营六四事件ۍ 07.11.2015