0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

多核CPU的啟動(dòng)方式

科技綠洲 ? 來源:一起學(xué)嵌入式 ? 作者:一起學(xué)嵌入式 ? 2023-06-22 10:04 ? 次閱讀

工作中遇到的多核 ARM CPU 越來越多,總結(jié)分享一些多核啟動(dòng)的知識(shí),希望能幫助更多小伙伴。

在 ARM64 架構(gòu)下如果想要啟動(dòng)多核,有 spin-table 和 psci 兩種方式,下面針對(duì)這兩種啟動(dòng)流程進(jìn)行分析。

代碼版本

  • boot-wrapper-aarch64 version : 28932c41e14d730b8b9a7310071384178611fb32
  • linux v5.14

多核 CPU 的啟動(dòng)方式

嵌入式系統(tǒng)的啟動(dòng)的基本流程是先運(yùn)行 bootloader ,然后由 bootloader 引導(dǎo)啟動(dòng) kernel,這里無論啟動(dòng)的是 rt-thread 或者是 linux 原理都是一樣的。

上電后所有的 CPU 都會(huì)從 bootrom 里面開始執(zhí)行代碼,為了防止并發(fā)造成的一些問題,需要將除了 primary cpu 以外的 cpu 攔截下來,這樣才能保證啟動(dòng)的順序是可控的。

spin-table 啟動(dòng)方法

在啟動(dòng)的過程中,bootloader 中有一道柵欄,它攔住了除了 cpu0 外的其他 cpucpu0 直接往下運(yùn)行,進(jìn)行設(shè)備初始化以及運(yùn)行 Kernel。其他 cpu0 則在柵欄外進(jìn)入睡眠狀態(tài)。

cpu0 在初始化 smp 的時(shí)候,會(huì)在 cpu-release-addr 里面填入一個(gè)地址并喚醒其他 cpu。這時(shí)睡眠的 cpu 接收到信號(hào),醒來的時(shí)候會(huì)先檢查 cpu-release-addr 這個(gè)地址里面的數(shù)據(jù)是不是有效。如果該地址是有效的(非 0 ),意味著自己需要真正開始啟動(dòng)了,接下來他會(huì)跳轉(zhuǎn)到。

下面我們看看 arm64 里面的實(shí)現(xiàn),在 arch/arm64/boot/dts/xxx.dts 中有如下描述:

1cpu@0 {
2    device_type = "cpu";
3    compatible = "arm,armv8";
4    reg = < 0x0 0x0="" >;
5    enable-method = "spin-table"; /* 選擇使用 spin-table 方式啟動(dòng)  */
6    cpu-release-addr = < 0x0 0x8000fff8="" >;
7};

arch/arm64/kernel/smp_spin_table.c 中處理了向其他 cpu 發(fā)送信號(hào)的方法:

1、先是獲取 release_addr 的虛擬地址

2、向該地址寫入從 cpu 的入口地址

3、通過 sev() 指令喚醒其他 cpu

1static int smp_spin_table_cpu_prepare(unsigned int cpu)
 2{
 3    __le64 __iomem *release_addr;
 4    phys_addr_t pa_holding_pen = __pa_symbol(function_nocfi(secondary_holding_pen));
 5
 6    if (!cpu_release_addr[cpu])
 7        return -ENODEV;
 8
 9    /*
10     * The cpu-release-addr may or may not be inside the linear mapping.
11     * As ioremap_cache will either give us a new mapping or reuse the
12     * existing linear mapping, we can use it to cover both cases. In
13     * either case the memory will be MT_NORMAL.
14     */
15    release_addr = ioremap_cache(cpu_release_addr[cpu],
16                     sizeof(*release_addr));
17    if (!release_addr)
18        return -ENOMEM;
19
20    /*
21     * We write the release address as LE regardless of the native
22     * endianness of the kernel. Therefore, any boot-loaders that
23     * read this address need to convert this address to the
24     * boot-loader's endianness before jumping. This is mandated by
25     * the boot protocol.
26     */
27    writeq_relaxed(pa_holding_pen, release_addr);
28    dcache_clean_inval_poc((__force unsigned long)release_addr,
29                (__force unsigned long)release_addr +
30                    sizeof(*release_addr));
31
32    /*
33     * Send an event to wake up the secondary CPU.
34     */
35    sev();
36
37    iounmap(release_addr);
38
39    return 0;
40}

Bootloader 部分以 boot-wrapper-aarch64 中的代碼做示例,非主 CPU 會(huì)輪詢檢查 mbox(其地址等同cpu-release-addr)中的值,當(dāng)其值為 0 的時(shí)候繼續(xù)睡眠,否則就跳轉(zhuǎn)到內(nèi)核執(zhí)行,代碼如下所示:

1/**
 2 * Wait for an address to appear in mbox, and jump to it.
 3 *
 4 * @mbox: location to watch
 5 * @invalid: value of an invalid address, 0 or -1 depending on the boot method
 6 * @is_entry: when true, pass boot parameters to the kernel, instead of 0
 7 */
 8void __noreturn spin(unsigned long *mbox, unsigned long invalid, int is_entry)
 9{
10    unsigned long addr = invalid;
11
12    while (addr == invalid) {
13        wfe();
14        addr = *mbox;
15    }
16
17    if (is_entry)
18#ifdef KERNEL_32
19        jump_kernel(addr, 0, ~0, (unsigned long)&dtb, 0);
20#else
21        jump_kernel(addr, (unsigned long)&dtb, 0, 0, 0);
22#endif
23
24    jump_kernel(addr, 0, 0, 0, 0);
25
26    unreachable();
27}
28
29/**
30 * Primary CPU finishes platform initialisation and jumps to the kernel.
31 * Secondaries are parked, waiting for their mbox to contain a valid address.
32 *
33 * @cpu: logical CPU number
34 * @mbox: location to watch
35 * @invalid: value of an invalid address, 0 or -1 depending on the boot method
36 */
37void __noreturn first_spin(unsigned int cpu, unsigned long *mbox,
38               unsigned long invalid)
39{
40    if (cpu == 0) {
41        init_platform();
42
43        *mbox = (unsigned long)&entrypoint;
44        sevl();
45        spin(mbox, invalid, 1);
46    } else {
47        *mbox = invalid;
48        spin(mbox, invalid, 0);
49    }
50
51    unreachable();
52}

PSCI 啟動(dòng)方法

另外一種 enable-method 就是 PSCI,依舊先從 kernel 開始分析。先看 arch/arm64/boot/dts/mediatek/mt8173.dtsi 文件,里面 cpu 節(jié)點(diǎn)選擇了PSCI 的方法:

1cpu0: cpu@0 {
2    compatible = "arm,cortex-a53";
3    device_type = "cpu";
4    enable-method = "psci";    /* 啟動(dòng)方式選擇 PSCI */
5    operating-points-v2 = < &cpu_opp_table >;
6    reg = < 0x0 >;
7    cpu-idle-states = < &CPU_SLEEP_0 >;
8};

并且有一個(gè) PSCI 的節(jié)點(diǎn):

1psci {
2    compatible = "arm,psci-1.0", "arm,psci-0.2", "arm,psci";
3    method = "smc";
4    cpu_suspend   = < 0x84000001 >;
5    cpu_off          = < 0x84000002 >;
6    cpu_on          = < 0x84000003 >;
7};

PSCI 中的節(jié)點(diǎn)詳細(xì)說明請(qǐng)參考文檔:

kernel/Documentation/devicetree/bindings/arm/psci.txt。

在此僅說一下 method 字段。該字段有兩個(gè)可選值:smc 和 hvc。表示調(diào)用 PSCI 功能使用什么指令。smc、hvc、svc 這些指令都是由低運(yùn)行級(jí)別向更高級(jí)別請(qǐng)求服務(wù)的指令。

和系統(tǒng)調(diào)用一樣。調(diào)用了該指令,cpu 會(huì)進(jìn)入異常,切入更高的權(quán)限。

異常處理程序根據(jù)下面?zhèn)魃蟻淼?a target="_blank">參數(shù)決定給予什么服務(wù),smc 陷入 EL3,hvc 陷入 EL2,svc 陷入EL1。在 ARMv8 里面,EL3 總是是 secure 狀態(tài),EL2 是虛擬機(jī)狀態(tài),EL1 是普通的系統(tǒng)態(tài)。

接下來可以看看 arch/arm64/kernel/psci.c 里面的代碼,psci_ops.cpu_on 最終調(diào)用 smc call:

1static int cpu_psci_cpu_boot(unsigned int cpu)
 2{
 3    phys_addr_t pa_secondary_entry = __pa_symbol(function_nocfi(secondary_entry));
 4    int err = psci_ops.cpu_on(cpu_logical_map(cpu), pa_secondary_entry);
 5    if (err)
 6        pr_err("failed to boot CPU%d (%d)\\n", cpu, err);
 7
 8    return err;
 9}
10
11static unsigned long __invoke_psci_fn_smc(unsigned long function_id,
12            unsigned long arg0, unsigned long arg1,
13            unsigned long arg2)
14{
15    struct arm_smccc_res res;
16
17    arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res);
18    return res.a0;
19}

Bootloader 以 boot-wrapper-aarch64 作分析,看 psci.c 里的 psci_call 實(shí)現(xiàn)函數(shù),通過 fid 與 PSCI_CPU_OFF 和 PSCI_CPU_ON 相比,找出需要執(zhí)行的動(dòng)作:

1long psci_call(unsigned long fid, unsigned long arg1, unsigned long arg2)
 2{
 3    switch (fid) {
 4    case PSCI_CPU_OFF:
 5        return psci_cpu_off();
 6
 7    case PSCI_CPU_ON_64:
 8        return psci_cpu_on(arg1, arg2);
 9
10    default:
11        return PSCI_RET_NOT_SUPPORTED;
12    }
13}

當(dāng)然 boot-wrapper-aarch64 里也需要同樣的定義:

1#define PSCI_CPU_OFF        0x84000002
2#define PSCI_CPU_ON_32      0x84000003
3#define PSCI_CPU_ON_64      0xc4000003

boot-wrapper-aarch64 按照和 kernel 約定的好參數(shù)列表,為目標(biāo) cpu 設(shè)置好跳轉(zhuǎn)地址,然后返回到 kernel 執(zhí)行,下面給出關(guān)鍵代碼說明:

1static int psci_cpu_on(unsigned long target_mpidr, unsigned long address)
 2{
 3    int ret;
 4    unsigned int cpu = find_logical_id(target_mpidr);
 5    unsigned int this_cpu = this_cpu_logical_id();
 6
 7    if (cpu == MPIDR_INVALID)
 8        return PSCI_RET_INVALID_PARAMETERS;
 9
10    bakery_lock(branch_table_lock, this_cpu);
11    ret = psci_store_address(cpu, address);   /* 寫入啟動(dòng)地址  */
12    bakery_unlock(branch_table_lock, this_cpu);
13
14    return ret;
15}

總結(jié)

目前比較主流的多核啟動(dòng)方式是 PSCI,一般正式的產(chǎn)品都有 ATF。通過 PSCI 可以實(shí)現(xiàn) CPU 的開啟關(guān)閉以及掛起等操作。

在實(shí)際的移植工作過程中,如果有帶有 ATF 的 bootloader, 那多核移植就相對(duì)容易很多,如果沒有的話,也可以采用 spin_table 的方式來啟動(dòng)多核。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    10863

    瀏覽量

    211782
  • 程序
    +關(guān)注

    關(guān)注

    117

    文章

    3787

    瀏覽量

    81049
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4788

    瀏覽量

    68616
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    C6678 多核啟動(dòng)的問題

    我想知道怎么讓dsp啟動(dòng)的時(shí)候多核啟動(dòng),debug的時(shí)候可以選擇下載到那個(gè)核,然后運(yùn)行,選擇的核就會(huì)開始運(yùn)行。 如果我把程序?qū)懙絜eprom那么boot起來后就只有core0,運(yùn)行,怎么讓所有的核都
    發(fā)表于 06-21 14:36

    目前使用6657,想通過TFTP完成多核啟動(dòng)~

    各位大大們~~我的板子是6657,使用IBL實(shí)現(xiàn)了OUT(ELF)文件在TFTP的方式啟動(dòng)成功;1)不知道能不能把兩個(gè)OUT文件合并,完成多核啟動(dòng);2)我看到IBL中有一種BTBL
    發(fā)表于 08-06 07:36

    ARM64 SMP多核啟動(dòng)相關(guān)資料推薦(下)

    2、psci方式多核啟動(dòng)描述上面說了pin-table的多核啟動(dòng)方式,看似很繁瑣,實(shí)際上并不復(fù)雜
    發(fā)表于 06-06 17:11

    介紹在ARM64架構(gòu)下啟動(dòng)多核的兩種方式

    : 28932c41e14d730b8b9a7310071384178611fb32linux v5.14多核 CPU啟動(dòng)方式嵌入式系統(tǒng)的啟動(dòng)
    發(fā)表于 06-13 18:23

    多核CPU、多進(jìn)程、多線程之間的聯(lián)系解析

    多核cpu主要分原生多核和封裝多核。Windows 應(yīng)用程序中消息有兩種送出途徑;直接和排隊(duì)。Windows或某些運(yùn)行的應(yīng)用程序可直接發(fā)布消息給窗口過程。線程,是指從軟件或者硬件上實(shí)
    發(fā)表于 12-01 09:37 ?9326次閱讀
    <b class='flag-5'>多核</b><b class='flag-5'>CPU</b>、多進(jìn)程、多線程之間的聯(lián)系解析

    多核CPU打游戲更快嗎

    隨著AMD銳龍的橫空出世,電腦CPU進(jìn)入了多核震懾的時(shí)代。
    發(fā)表于 07-28 09:57 ?2636次閱讀

    多核CPU和SoC芯片及其工作原理

    前言:現(xiàn)在的CPU或SoC基本都是在單芯片中集成多個(gè)CPU核心,形成通常所說的4核、8核或更多核CPU或SoC芯片。為什么要采用這種方式?
    的頭像 發(fā)表于 01-06 11:35 ?1.5w次閱讀
    <b class='flag-5'>多核</b>心<b class='flag-5'>CPU</b>和SoC芯片及其工作原理

    多核CPU和單核的區(qū)別~

    昨天有同學(xué)問我多核cpu和單核的區(qū)別大不大,今天簡(jiǎn)單寫一篇回復(fù)下吧。大家有其他問題也可以文末給我留言,我會(huì)盡量抽時(shí)間寫文回復(fù)。首先回顧下基本概念,cpu,就是中央處理器,包括運(yùn)算器和控制器...
    發(fā)表于 12-01 20:06 ?1次下載
    <b class='flag-5'>多核</b><b class='flag-5'>CPU</b>和單核的區(qū)別~

    ARM64 SMP多核啟動(dòng)(下)—PSCI

    上面說了pin-table的多核啟動(dòng)方式,看似很繁瑣,實(shí)際上并不復(fù)雜,無外乎主處理器喚醒從處理器到指定地址上去執(zhí)行指令
    發(fā)表于 06-09 14:31 ?727次閱讀
    ARM64 SMP<b class='flag-5'>多核</b><b class='flag-5'>啟動(dòng)</b>(下)—PSCI

    基于Tricore芯片的AUTOSAR架構(gòu)下的多核啟動(dòng)

    隨著汽車ECU迅速的往域控制器方向發(fā)展,ECU要出來任務(wù)越來越多,單核CPU的負(fù)載越來越大,多核ECU勢(shì)在必行。AUTOSAR架構(gòu)下OS支持多核處理,本系列文章將詳細(xì)介紹AUTOSAR架構(gòu)下的
    的頭像 發(fā)表于 10-23 10:15 ?3243次閱讀
    基于Tricore芯片的AUTOSAR架構(gòu)下的<b class='flag-5'>多核</b><b class='flag-5'>啟動(dòng)</b>

    如何在內(nèi)核中啟動(dòng)secondary cpu

    給調(diào)度器之前,并沒有實(shí)際的業(yè)務(wù)進(jìn)程,而我們知道內(nèi)核中cpu在空閑時(shí)會(huì)執(zhí)行idle進(jìn)程。因此,在其啟動(dòng)之前需要為每個(gè)cpu初始化一個(gè)idle進(jìn)程。 另外,由于將一個(gè)cpu通過熱插拔
    的頭像 發(fā)表于 12-05 15:46 ?607次閱讀
    如何在內(nèi)核中<b class='flag-5'>啟動(dòng)</b>secondary <b class='flag-5'>cpu</b>

    SMP多核啟動(dòng)cpu操作函數(shù)

    其中spin-table啟動(dòng)方式的回調(diào)如下: const struct cpu_operations smp_spin_table_ops = {.name= "spin-table
    的頭像 發(fā)表于 12-05 16:04 ?781次閱讀
    SMP<b class='flag-5'>多核</b><b class='flag-5'>啟動(dòng)</b><b class='flag-5'>cpu</b>操作函數(shù)

    使用自旋表啟動(dòng)的平臺(tái)設(shè)備樹cpu節(jié)點(diǎn)介紹

    補(bǔ)充一下一個(gè)使用自旋表作為啟動(dòng)方式的平臺(tái)設(shè)備樹cpu節(jié)點(diǎn): arch /arm64/ boot /dts/ xxx.dtsi: cpu@ 0 { device_type = "
    的頭像 發(fā)表于 12-05 16:19 ?846次閱讀

    SMP多核secondary cpu啟動(dòng)流程

    secondary cpu啟動(dòng) 由于psci方式啟動(dòng)secondary cpu的流程,除了其所執(zhí)行的cp
    的頭像 發(fā)表于 12-05 17:41 ?878次閱讀
    SMP<b class='flag-5'>多核</b>secondary <b class='flag-5'>cpu</b><b class='flag-5'>啟動(dòng)</b>流程

    多核CPU的優(yōu)勢(shì)是什么

    多核CPU(Central Processing Unit,中央處理器)作為現(xiàn)代計(jì)算機(jī)技術(shù)的重要里程碑,其優(yōu)勢(shì)在于顯著提升了計(jì)算性能、多任務(wù)處理能力、系統(tǒng)穩(wěn)定性以及能效比等多個(gè)方面。以下將詳細(xì)闡述多核
    的頭像 發(fā)表于 08-22 14:30 ?2737次閱讀