2013-11-18 4 views
13

Я бил головой об этом в течение последних 3-4 дней, и я не могу найти объяснительную документацию DECENT (из ARM или неофициальную), чтобы помочь мне. У меня есть Плата ODROID-XU (большая. LITTLE 2 x Cortex-A15 + 2 x Cortex-A7) и я пытаюсь понять немного больше об архитектуре ARM. В моем «экспериментирующем» кодексе я сейчас пришел на сцену, где хочу ВЗГЛЯДАТЬ ДРУГИЕ КОРЫ ИЗ ИХ WFI (ожидание для прерывания).ARM: запуск/пробуждение/вывод других процессоров/точек доступа и запуск начального адреса запуска?

недостающее информация, которую я до сих пор пытаюсь найти это:

1. При получении базового адреса памяти, отображенные ГИК Я понимаю, что мне нужно прочитать сантибар; Но ни одна часть документации не объясняет, как биты в сантибар (в 2 значения PERIPHBASE) должны быть организованы, чтобы добраться до конечного базового адреса GIC

2. При отправке SGI через регистр GICD_SGIR, какое прерывание ID между 0 и 15 я должен выбрать? Это имеет значение?

3. При отправке SGI через регистр GICD_SGIR, как я могу сказать, другие ядра ЧЕГО НАЧАТЬ ИСПОЛНЕНИЕ ИЗ?

4. Как тот факт, что мой код загружается U-BOOT загрузчиком влияет на этот контекст?

Cortex-A Руководство по программированию серии v3.0 (здесь: link) утверждает следующее в разделе 22.5.2 (загрузки SMP в Linux, стр):

В то время как основное ядро ​​загружается, вторичные сердечники будут находиться в режиме ожидания, используя команду WFI . Он (первичный сердечник) обеспечит адрес запуска для вторичных ядер и разбудить их, используя Inter-Processor Interrupt (IPI), означающей SGI средствами сигнализации через ГИК

Как Linux сделать это? Документация-S не дает никаких других сведений, относящихся к «Он предоставит начальный адрес для вторичных сердечников».

Мое расстройство растет, и я был бы очень благодарен за ответы. Большое вам спасибо!

дополнительные детали

документация Я использую:

  • ARMv7-A & R Архитектура Справочное руководство
  • Cortex-А15 ТРМ (Техническое справочное руководство)
  • Cortex-A15 MPCore TRM
  • Cortex-A Руководство по программированию серии v3.0
  • GICv2 Спецификация архитектуры

Что я сделал сейчас:

  • UBoot грузы со мной в 0x40008000; Я настроил вверх Таблицы перевода (БВУ), написанная TTBR0 и TTBCR соответственно и сопоставляются 0x40008000 до 0x8000_0000 (2 Гб), так что я также позволил MMU
  • Set-обработчики исключений из моих собственных
  • У меня есть функция Printf по сравнению с последовательным (UART2 на ODROID-XU)

Все вышеперечисленное работает нормально.

То, что я пытаюсь сделать сейчас:

  • Получить базовый адрес ГИК => в данный момент я прочитал сантибар и я просто и (&) его значение с 0xFFFF8000 и использовать это в качестве ГИК базовый адрес, хотя я почти уверен, что это не так
  • Включить распределитель ГИК (по смещению 0x1000 от GIC базового адреса?), по сочинительство GICD_CTLR со значением 0x1
  • Построить SGI ш с следующими параметрами: Group = 0, ID = 0, TargetListFilter = «Все процессоры за исключением меня» и отправить его (записать) через регистр GICD_SGIR GIC
  • Поскольку я не передал начальный адрес запуска для другого ядра, ничего не происходит после того, как все это

.... UPDATE ....

Я начал смотреть на ядре Linux и исходные коды QEMU в поисках ответа.Вот что я узнал (пожалуйста, поправьте меня, если я ошибаюсь):

  • При включении питания платы всех сердечников начать работу с вектора сброса
  • программного обеспечения (прошивки) компоненты выполняет WFI на вторичных ядрах и некоторый другой код, который будет действовать как протокол между этими вторичными ядрами и основным ядром, когда последний хочет их снова разбудить
  • Например, протокол, используемый для EnergyCore ECX -100 0 (Highbank) плата выглядит следующим образом:

**(1)** the secondary cores enter WFI and when

**(2)** the primary core sends an SGI to wake them up

**(3)** they check if the value at address (0x40 + 0x10 * coreid) is non-null;

**(4)** if it is non-null, they use it as an address to jump to (execute a BX)

**(5)** otherwise, they re-enter standby state, by re-executing WFI

**(6)** So, if I had an EnergyCore ECX-1000 board, I should write (0x40 + 0x10 * coreid) with the address I want each of the cores to jump to and send an SGI

Вопросы:

  • 1. Что такое программный компонент, который делает это? Является ли это бинарным BL1, который я написал на SD-карте, или это U-BOOT?
  • 2. Из того, что я понимаю, этот программный протокол отличается от платы. Это так, или это зависит только от основного процессора?
  • 3. Где я могу найти информацию об этом протоколе для одной платы ARM? - Могу ли я найти его на официальном сайте ARM или на веб-странице форума?
+0

Я считаю, что такая последовательность инициализации (то есть пробуждение вторичных ядер в режиме ожидания) выполняется с помощью кода ПЗУ, который предварительно загружен производителем чипа (т. Е. TI, Samsung, ...). Например, так это делается в TI OMAP. http://omappedia.org/wiki/Bootloader_Project – Adi

+0

Я считаю (очевидно), что он должен быть одним из: ROM-кода, BL1 code => как недоступным (BL1 зашифрован [подписан]), либо загрузчиком ... Я искал бит через источники загрузчика, но я не нашел никакого отношения к этому протоколу SMP; Кроме того, Linux, похоже, не полагается на аргументы от загрузчика, чтобы обнаружить протокол (возможно, через ATAG), что заставляет меня думать, что то, что я ищу, либо в коде ROM, либо в коде BL1. Вопрос в том, какой из них и, поскольку они непосредственно недоступны, где я могу найти детали о том, что они делают для моей доски (ODROID-XU) ... –

+0

Я полагаю, вы имели в виду «т.е. помещение вторичных ядер в ждущий режим» –

ответ

8

Хорошо, я вернулся к ребенку. Вот выводы:

  • компонент программного обеспечения, которое ставит процессоры для сна является загрузчиком (в моем случае U-Boot)
  • Linux как-то знает, как загрузчик делает это (зашито в Linux ядро для каждой платы) и знает, как разбудить их снова

для моего совета ODROID-XU источники, описывающие этот процесс, являются UBOOT ODROID-v2012.07 и ядро ​​Linux здесь: LINUX ODROIDXU-3.4.y (это было бы лучше, если бы я л окунул в версию ядра из ветки odroid-3.12.y, так как первый не запускает все 8 процессоров, всего 4 из них, но последний делает).

Во всяком случае, вот исходный код, который я придумал, я выложу соответствующие исходные файлы из указанных выше исходного кода деревьев, которые помогли мне писать этот код впоследствии:

typedef unsigned int DWORD; 
typedef unsigned char BOOLEAN; 
#define FAILURE (0) 
#define SUCCESS (1) 
#define NR_EXTRA_CPUS (3) // actually 7, but this kernel version can't wake them up all -> check kernel version 3.12 if you need this 

// Hardcoded in the kernel and in U-Boot; here I've put the physical addresses for ease 
// In my code (and in the linux kernel) these addresses are actually virtual 
// (thus the 'VA' part in S5P_VA_...); note: mapped with memory type DEVICE 
#define S5P_VA_CHIPID (0x10000000) 
#define S5P_VA_SYSRAM_NS (0x02073000) 
#define S5P_VA_PMU (0x10040000) 
#define EXYNOS_SWRESET ((DWORD) S5P_VA_PMU + 0x0400) 
// Other hardcoded values 
#define EXYNOS5410_REV_1_0 (0x10) 
#define EXYNOS_CORE_LOCAL_PWR_EN (0x3) 

BOOLEAN BootAllSecondaryCPUs(void* CPUExecutionAddress){ 

// 1. Get bootBase (the address where we need to write the address where the woken CPUs will jump to) 
// and powerBase (we also need to power up the cpus before waking them up (?)) 
DWORD bootBase, powerBase, powerOffset, clusterID; 

asm volatile ("mrc p15, 0, %0, c0, c0, 5" : "=r" (clusterID)); 
clusterID = (clusterID >> 8); 
powerOffset = 0; 
if((*(DWORD*)S5P_VA_CHIPID & 0xFF) < EXYNOS5410_REV_1_0) 
{ 
    if((clusterID & 0x1) == 0) powerOffset = 4; 
} 
else if((clusterID & 0x1) != 0) powerOffset = 4; 

bootBase = S5P_VA_SYSRAM_NS + 0x1C; 
powerBase = (S5P_VA_PMU + 0x2000) + (powerOffset * 0x80); 

// 2. Power up each CPU, write bootBase and send a SEV (they are in WFE [wait-for-event] standby state) 
for (i = 1; i <= NR_EXTRA_CPUS; i++) 
{ 
    // 2.1 Power up this CPU 
    powerBase += 0x80; 
    DWORD powerStatus = *(DWORD*)((DWORD) powerBase + 0x4); 

    if ((powerStatus & EXYNOS_CORE_LOCAL_PWR_EN) == 0) 
    { 
     *(DWORD*) powerBase = EXYNOS_CORE_LOCAL_PWR_EN; 
     for (i = 0; i < 10; i++) // 10 millis timeout 
     { 
      powerStatus = *(DWORD*)((DWORD) powerBase + 0x4); 
      if ((powerStatus & EXYNOS_CORE_LOCAL_PWR_EN) == EXYNOS_CORE_LOCAL_PWR_EN) 
       break; 
      DelayMilliseconds(1); // not implemented here, if you need this, post a comment request 
     } 
     if ((powerStatus & EXYNOS_CORE_LOCAL_PWR_EN) != EXYNOS_CORE_LOCAL_PWR_EN) 
      return FAILURE; 
    } 
    if ((clusterID & 0x0F) != 0) 
    { 
     if (*(DWORD*)(S5P_VA_PMU + 0x0908) == 0) 
     do { DelayMicroseconds(10); } // not implemented here, if you need this, post a comment request 
     while (*(DWORD*)(S5P_VA_PMU + 0x0908) == 0); 
     *(DWORD*) EXYNOS_SWRESET = (DWORD)(((1 << 20) | (1 << 8)) << i); 
    } 

    // 2.2 Write bootBase and execute a SEV to finally wake up the CPUs 
    asm volatile ("dmb" : : : "memory"); 
    *(DWORD*) bootBase = (DWORD) CPUExecutionAddress; 
    asm volatile ("isb"); 
    asm volatile ("\n dsb\n sev\n nop\n"); 
} 
return SUCCESS; 
} 

Это успешно будит 3 из 7 вторичных процессоров.

А теперь этот короткий список соответствующих исходных файлов в U-Boot и ядра Linux:

  • UBoot: lowlevel_init.S - уведомление линии 363-369, как вторичные процессоры ждать WFE для значения _hotplug_addr для того, чтобы не обнулять и прыгать на него; _hotplug_addr - фактически bootBase в приведенном выше коде; также линии 282-285 говорят нам, что _hotplug_addr должен быть переведен на CONFIG_PHY_IRAM_NS_BASE + _hotplug_addr - nscode_base (_hotplug_addr - nscode_base является 0x1C и CONFIG_PHY_IRAM_NS_BASE является 0x02073000, таким образом, вышеприведенные hardcodings в ядре Linux)

  • LINUX KERNEL: общий - smp.c (взгляд на функции __cpu_up) конкретной платформы (ODROID-Xu): platsmp.c (функция boot_secondary, вызываемая общим __cpu_up; Также смотрите на platform_smp_prepare_cpus [внизу] => это функция, которая на самом деле устанавливает базу загрузки и значение мощности базового)

0

Перейдите на страницу www.arm.com и загрузите там оценочную копию набора для разработки DS-5. После установки под примерами будет startup_Cortex-A15MPCore directory. Посмотрите на startup.s.

+1

Это может быть намного более полезно с немного большей детализацией. Что иллюстрирует «startup.s»? – user1618143

+0

привет, извините за то, что вы не опубликовали это раньше, мне это уже удалось решить; Я выложу свои выводы и решение завтра, прямо сейчас у меня нет времени –

1

Для ясности и в будущем, есть тонкая часть информации здесь отсутствует благодаря отсутствие надлежащей документации по протоколу загрузки Exynos (nb этот вопрос действительно должен быть отмечен как «Exynos 5», а не «Cortex-A15» - это специфическая для SoC вещь, и то, что ARM говорит, является лишь общей рекомендацией). Из холодного ботинка вторичные сердечники находятся не в WFI, они все еще отключены от.

Чем проще минимальное решение (основано на том, что делает автоопределение в Linux), который я разработал в процессе написания загрузки подкладки, чтобы получить гипервизор работает на XU, делает два шага:

  1. Первой запись адрес точки входа в холдинг Exynos (0x02073000 + 0x1c)
  2. Затем выталкивает контроллер мощности для включения соответствующего ядра (я): Таким образом, они выходят из безопасного пути загрузки в удерживающее перо, чтобы найти точку входа ожидая их, пропуская петлю WFI и избавляясь от необходимости даже касаться GIC.

Если вы планируете реализацию полнофункционального процессора hotplug, вы можете пропустить проверку идентификатора кластера - если мы загружаемся, мы находимся в кластере 0 и нигде (проверка на наличие готовых чипов с обратные кластерные регистры тоже должны быть ненужными на Odroid - наверняка были для меня).

Из моего расследования, стрельба по A7s немного больше задействована. Судя по the Exynos big.LITTLE switcher driver, кажется, вам нужно выставить отдельный набор регистров контроллера мощности, чтобы сначала включить кластер 1 (и вам, возможно, придется обходиться с CCI, особенно для включения MMU и кешей) - я не получил тем более, что к этому моменту это было более «весело», чем «делать настоящую работу» ...

В качестве альтернативы Samsung's mainline patch for CPU hotplug on the 5410 делает основной элемент управления питанием более ясным, чем беспорядок в их нижнем коде IMO.

+0

спасибо за разъяснения, я просто хотел сказать, что я удалил «Cortex-A15» из названия, но не добавил «Exynos» », поскольку обсуждение может помочь программистам ARM в целом, независимо от SoC, т.е. процедура пробуждения других ядер может быть найдена аналогично для других конкретных SoC; Таким образом, поисковые запросы Google также могут направлять их сюда –