Vengineerの妄想(準備期間)

人生は短いけど、長いです。人生を楽しみましょう!

Raspberry Pi 5 は、PSCIをサポートしている!

はじめに

Raspberry Pi 5 については、今週の日曜部のブログに書きました。

vengineer.hatenablog.com

その後も継続して Raspberry Pi 5 に関して、色々と調べています。ソースは、Raspberry Pi 用の linux kernel です。

今回は、PSCI についてです。

PSCI とは?

PSCI はARMが定義した省エネインターフェースです。

64ビットLinuxでは、PSCIをDevice Tree Sourceファイル内のCPUノードのenable-methodパラメータに設定するものとしています。

Raspberry Pi 4 では、arch/arm/boot/dts/bcm2711.dtsi に cpu のところを見てみると、enable-method = "spin-table" でした。

     cpu0: cpu@0 {
            device_type = "cpu";
            compatible = "arm,cortex-a72";
            reg = <0>;
            enable-method = "spin-table";
            cpu-release-addr = <0x0 0x000000d8>;
            d-cache-size = <0x8000>;
            d-cache-line-size = <64>;
            d-cache-sets = <256>; // 32KiB(size)/64(line-size)=512ways/2-way set
            i-cache-size = <0xc000>;
            i-cache-line-size = <64>;
            i-cache-sets = <256>; // 48KiB(size)/64(line-size)=768ways/3-way set
            next-level-cache = <&l2>;
        };

一方、Rasberry Pi 5では、arch/arm/boot/dts/bcm2711.dtsi に cpu のところを見てみると、enable-method = "psci" です。

     cpu0: cpu@0 {
            device_type = "cpu";
            compatible = "arm,cortex-a76";
            reg = <0x000>;
            enable-method = "psci";
            next-level-cache = <&l2_cache>;
        };

Raspberry Pi 4/5 のブートシーケンス

Raspberry Pi 4 では、

  • 1st Level Boot Loader : 内部ROM (GPUが内部ROMを実行が FAT32フォーマットのSDカード内から bootcode.bin を GPU の L2キャッシュにロードする)
  • 2nd Level Boot Loader : bootcode.bin (GPUがbootcode.binを実行し、SDRAMを初期化し、SDカード内のstart4.elfをロードする)
  • 3rd Level Boot Loader : start4.elf (GPUがstart.elfを実行し、SDカード内のkernel8.img をロードする。dtbファイルもSDRAMにロードする)
  • Linux Kernel : kernel8.img (GPUがCPUコアのリセットを解除し、CPUがkernel8.imgを実行して、Linux Kernelを起動する)

です。1st から 3rd までは、GPUが実行しています。

Raspberry Pi 5 では、Raspberry Pi 4と同じように 下記のように、Linux Kernel を起動しているのだと思います。

  • 1st Level Boot Loader : 内部ROM (GPUが内部ROMを実行が FAT32フォーマットのSDカード内から bootcode.bin を GPU の L2キャッシュにロードする)
  • 2nd Level Boot Loader : bootcode.bin (GPUがbootcode.binを実行し、SDRAMを初期化し、SDカード内のstart4.elfをロードする)
  • 3rd Level Boot Loader : start4.elf (GPUがstart.elfを実行し、SDカード内のkernel_2712.img をロードする。dtbファイルもSDRAMにロードする)
  • Linux Kernel : kernel8.img (GPUがCPUコアのリセットを解除し、CPUがkernel_2712.imgを実行して、Linux Kernelを起動する)

Rasberry Pi 4 との違いは、Linux Kernel が kernel8.img から kernel_2712.img になりました。

一般的なArm64なSoCのブートシーケンス

一般的な Arm64なCPUコアを搭載しているSoCでのブートシーケンスは、

  • 1st Level Boot Loader : 内部ROM
  • 2nd Level Boot Loader : Arm Trusted Firmware
  • 3rd Level Boot Loader : Arm Trusted Firmware runtime + UBoot
  • Linux Kernel

のようになっています。Linux Kernel を起動する前に、Arm Trusted Firmware という runtime をロードします。

Raspberry Pi 5のブートシーケンスでは、Arm Trusted Firmware runtime をロードしているところはありません。

では、Arm Trusted Firmware runtime はどこでロードするのでしょうか?

ヒントは、arch/arm/boot/dts/bcm2712.dtsi の rmem のところに下記のようなものがありました。

 rmem: reserved-memory {
        #address-cells = <2>;
        #size-cells = <1>;
        ranges;

        atf@0 {
            reg = <0x0 0x0 0x80000>;
            no-map;
        };

reserved-memory (Linux Kernelでは使わないようにする)の領域に、atf@0 というエントリがあります。アドレス 0x0 から 0x80000 (512KB) を atf として予約しています。

rmem は、arch/arm/boot/dts/bcm2712-rpi.dtsi に下記のように追記されています。

&rmem {
    /*
     * RPi4's co-processor will copy the board's bootloader configuration
     * into memory for the OS to consume. It'll also update this node with
     * its placement information.
     */
    blconfig: nvram@0 {
        compatible = "raspberrypi,bootloader-config", "nvmem-rmem";
        #address-cells = <1>;
        #size-cells = <1>;
        reg = <0x0 0x0 0x0>;
        no-map;
        status = "disabled";
    };
};

raspberrypi,bootloader-config に対しては何も出てこなかったのですが、nvmem-rmem については、drivers/nvmem/rmem.c がありました。

rmem_probe関数が下記のようになっています。

static int rmem_probe(struct platform_device *pdev)
{
    struct nvmem_config config = { };
    struct device *dev = &pdev->dev;
    struct reserved_mem *mem;
    struct rmem *priv;

    priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;
    priv->dev = dev;

    mem = of_reserved_mem_lookup(dev->of_node);
    if (!mem) {
        dev_err(dev, "Failed to lookup reserved memory\n");
        return -EINVAL;
    }
    priv->mem = mem;

    config.dev = dev;
    config.priv = priv;
    config.name = "rmem";
    config.id = NVMEM_DEVID_AUTO;
    config.size = mem->size;
    config.reg_read = rmem_read;

    return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config));
}

これは妄想ですが、nvmemからmemory(0x0)に 512KB コピーしているのではないでしょうか?

おわりに

Linux Kernelが起動中に、GPU が nvmem から memory (DRAM) に Arm Trusted Firmware BL31 をコピーすることで、Raspberry Pi 4 の ブートシーケンスと同じでも PSCI をサポートできるようにしているのでは?ということが分かりました。

本当かな?