15AH, San Francisco

California, United States.

Send Your Mail At:

tianyingkejishe@sina.cn

Working Hours

Mon-Sat: 9.30am To 7.00pm

作者标题

Autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et dolore feugait.

Author Archive by stormwind

【Linux】构建基于Busybox的Initramfs

一、BusyBox

1、下载BusyBox

$ wget http://busybox.net/downloads/busybox-1.22.1.tar.bz2

2、配置

$ tar xf busybox-1.22.1.tar.bz2
$ cd busybox-1.22.1
$ make defconfig
$ make menuconfig

执行 make menuconfig 之后,把下面几个选项选上:

Busybox Settings:
- General Configuration -> Don't use /usr
- General Configuration -> Show verbose applet usage messages
- General Configuration -> Runtime SUID/SGID configuration via /etc/busybox.conf
- Build Options -> Build BusyBox as a static binary (no shared libs)
- Cross compi—>指定编译工具链路径,这个可选项

注解:

defconfig – set .config to largest generic configuration就是最大化选用通用的功能

3、编译及安装

$ make
$ make install

执行 make install 之后,会生成一个 _install 目录,里面就是 编译后Busybox,包含 bin 、 sbin 、 及 linuxrc 软链接。这个目 录的内容可以直接复制到下面制作initramfs的目录。

警告:

这里假设initramfs的根目录是 /tmp/initramfs/ 。

$ cp -r _install/* /tmp/initramfs/

到这里,编译Busybox的工作就算是完成了。

二、InitramFs

1、文件结构

经过上面的步骤之后,现在来看看initramfs根目录( /tmp/initramfs/ )的结 构,应该是这样子的:

├── bin
├── linuxrc -> bin/busybox
└── sbin

下面我们需要创建一个可执行文件 init ,这是一个Shell脚本。

2、init文件

init 起到承上启下的作用。内核加载完成之后,就会执行这个文件,在这个 文件,可以执行相关命令,最后挂载要启动的文件系统,并切换执行目标系统的 /sbin/init 文件,开始引导真实的Linux系统。

为了简单,这里直接贴出源代码,流程也相对比较简单。

#!/bin/sh

echo "Loading, please wait..."

export PATH="/bin:/sbin"

[ -d /dev ] || mkdir -m 0755 /dev
[ -d /root ] || mkdir --mode=0700 /root
[ -d /sys ] || mkdir /sys
[ -d /proc ] || mkdir /proc
[ -d /tmp ] || mkdir /tmp
[ -d /mnt ] || mkdir /mnt

# Mount /proc and /sys:
mount -n proc /proc -t proc
mount -n sysfs /sys -t sysfs

# Note that this only becomes /dev on the real filesystem if udev's scripts
# are used; which they will be, but it's worth pointing out
#mount -t tmpfs -o mode=0755 udev /dev
[ -e /dev/console ] || mknod /dev/console c 5 1
[ -e /dev/null ] || mknod /dev/null c 1 3

echo /sbin/mdev >/proc/sys/kernel/hotplug
mdev -s

# Get real root device by LABEL or UUID
get_root() {
    if [ ! -z "$LABEL" ]; then
        ROOT=`blkid | sed -n "/$LABEL/p" | cut -d: -f 1`
    fi
    if [ ! -z "$UUID" ]; then
        ROOT=`blkid | sed -n "/$UUID/p" | cut -d: -f 1`
    fi
}

for x in $(cat /proc/cmdline); do
    case $x in
    init=*)
        init=${x#init=}
        ;;
    root=*)
        ROOT=${x#root=}
        case $ROOT in
            LABEL=*)
                LABEL=${ROOT#LABEL=}
                ;;
            UUID=*)
                UUID=${ROOT#UUID=}
                ;;
            /dev/nfs)
                [ -z "${BOOT}" ] && BOOT=nfs
                ;;
        esac
        ;;
    rootflags=*)
        ROOTFLAGS="-o ${x#rootflags=}"
        ;;
    rootfstype=*)
        ROOTFSTYPE="${x#rootfstype=}"
        ;;
    rootdelay=*)
        ROOTDELAY="${x#rootdelay=}"
        ;;
    resumedelay=*)
        RESUMEDELAY="${x#resumedelay=}"
        ;;
    loop=*)
        LOOP="${x#loop=}"
        ;;
    loopflags=*)
        LOOPFLAGS="-o ${x#loopflags=}"
        ;;
    loopfstype=*)
        LOOPFSTYPE="${x#loopfstype=}"
        ;;
    cryptopts=*)
        cryptopts="${x#cryptopts=}"
        ;;
    nfsroot=*)
        NFSROOT="${x#nfsroot=}"
        ;;
    netboot=*)
        NETBOOT="${x#netboot=}"
        ;;
    ip=*)
        IPOPTS="${x#ip=}"
        ;;
    boot=*)
        BOOT=${x#boot=}
        ;;
    resume=*)
        RESUME="${x#resume=}"
        ;;
    noresume)
        NORESUME=y
        ;;
    panic=*)
        panic="${x#panic=}"
        ;;
    quiet)
        quiet=y
        ;;
    ro)
        readonly=y
        ;;
    rw)
        readonly=n
        ;;
    debug)
        debug=y
        exec >/tmp/initramfs.debug 2>&1
        set -x
        ;;
    debug=*)
        debug=y
        set -x
        ;;
    break=*)
        break=${x#break=}
        ;;
    break)
        break=premount
        ;;
    0|1|2|3|4|5|6)
        RUNLEVEL=$x
        ;;
    esac
done

for t in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16;
do
    echo "Mounting the ROOT DEVICE (Time $t) ..."
    get_root $ROOT
    mount -o ro $ROOT /mnt
    mountpoint -q /mnt && break
    echo "Sleep 4s ..."
    sleep 4
done

if [ -e /mnt/sbin/init ] ;
then
    umount /proc
    umount /sys
    exec switch_root /mnt /sbin/init $RUNLEVEL
fi

/bin/sh -i

别忘记给 init 加上执行权限:

$ chmod +x init

现在文件结构应该是这样的:

├── bin
├── init
├── linuxrc -> bin/busybox
└── sbin

到这里,initramfs已经构建好了,下面进行打包及压缩。

注解

关于mdev的几点说明:

  1. mdev不支持LVM卷
  2. mdev支持USB设置热插拨,不过需要把HOTPLUG及USB_STORAGE支持及驱动编 译进内容,而不是编译成模块。Bus options (PCI, PCMCIA, EISA, MCA, ISA) -> PCI Hotplug SupportDevice Drivers -> USB support

3、打包压缩

使用下面这个命令就可以完成打包压缩工作:

cd /tmp/initramfs
find * | cpio -o -H newc | gzip -9 > ../initrd.img

现在, /tmp/initrd.img 就是制作好的initramfs。

三、uboot引导initramfs

  • 内核镜像:Image
  • 设备树:system.dtb
  • initrd 镜像:initrd.img(约 16MB)
# 1. 设置加载地址(按需调整,确保不与内核/DTB 冲突)
setenv loadaddr    0x80080000   # kernel
setenv fdtaddr    0x81000000   # dtb
setenv initrd_addr 0x83000000   # initrd

# 2. 加载内核、DTB、initrd
load mmc 0:1 ${loadaddr}    /boot/Image
load mmc 0:1 ${fdtaddr}     /boot/system.dtb
load mmc 0:1 ${initrd_addr} /boot/initrd.img

# 3. 计算 initrd 大小(U-Boot 自动填充)
setexpr initrd_size ${filesize} - 0

# 4. 构造 bootargs(关键!必须含 initrd 参数)
setenv bootargs \
  "console=ttyS0,115200n8 \
   root=/dev/mmcblk0p2 rw \
   init=/init \
   initrd=${initrd_addr},${initrd_size} \
   ${extra_bootargs}"

# 5. 启动(ARM64 使用 booti)
booti ${loadaddr} ${initrd_addr}:${initrd_size} ${fdtaddr}

【flash】bin制作工具

#!/bin/bash

echo $1
echo $(stat -c%s flash-system.bin)

if [ "$1" = "Image" ];then
        rm flash-system.bin
        cp fip-all.bin flash-system.bin
        echo "Image"
        dd if=$1 of=flash-system.bin bs=1M seek=8 conv=notrunc
elif [ "$1" = "s5000c-64c.dtb" ];then
        echo "s5000c-64c.dtb"
        dd if=$1 of=flash-system.bin bs=1M seek=78 conv=notrunc
else
        echo "Input Error"
        exit 1
fi

【飞腾】实现flash超过16M访问

参考《飞腾编程手册》,0x1a100000为qspi基地址

1、uboot指令操作

mw.l 0x1a100010 0x9f003000 //参考 flash 数据手册,RDID=0x9f
md.l 0x1a10001c 1 (2801401c: 000000c8 )

mw 0x1a100010 0xb7001000 //参考 flash 数据手册,Enable 4-byte Mode cmd=0xb7
mw 0x1a10001c 1

mw 0x1a100010 0x35003000 //参考 flash 数据手册,RDSD=0x35
md.l 0x1a10001c 1 (此处打印 2801401c: 00000008 )

mw 0x1a100000 0x00000005

mw 0x1a100004 0x0308000e

md 0x1000000

2、uboot程序修改

writel(0xb7001000,0x1a100010);
writel(0x1,0x1a10001c);
writel(0x5,0x1a100000);
writel(0x0308000e,0x1a100000);

3、设置启动参数

setenv boot_fdt “booti 0x800000 -:- 0x4e00000”

0x800000为Image起始地址

0x4e00000为dtb起始地址

【python】tkinter.TclError: wrong # args: should be “wm iconphoto window ?-default? image1 ?image2 …?”【python】

tkinter.TclError: wrong # args: should be "wm iconphoto window ?-default? image1 ?image2 ...?"

是由于在 Windows 上调用 root.iconphoto(False, photo) 时,传入了 None(即 self._create_icon() 返回 None,而 iconphoto 不接受 None 作为图像参数 —— 这在部分 Windows 版本(尤其是未安装 PIL 的环境)中会直接崩溃。

✅ 根本原因
您的系统中未安装 Pillow(PIL),导致 _create_icon() 中的 from PIL import Image 失败,回退逻辑也因异常未正确返回默认图标,最终向 iconphoto 传入了 None

安装 Pillow(推荐 · 完整功能)

Cmd

pip install Pillow

【驱动】version magic ‘5.10.209-rt101.5.15-v2-s5000c+ SMP mod_unload aarch64’ should be ‘5.10.209-rt101.5.15-v2-s5000c SMP mod_unload aarch64’【驱动】

当加载驱动的时候总是遇到跟内核系统版本不匹配问题,特别是这个+号,这儿是git自动增加的,主要原因就是本地存在未提交改动。解决方式两种:

1、git add提交

2、kernel跟目录下

echo ” > .scmversion # 清除 git 版本标记
echo ‘CONFIG_LOCALVERSION_AUTO=n’ >> .config

【JESD】JESD204b建链过程

     ┌───────────────┐
     │ 上电或复位     │
     └──────┬────────┘
            ↓
     ┌──────┴────────┐
     │ FPGA 拉低 SYNC~│ ← 通知 ADC 停止发数据
     └──────┬────────┘
            ↓
     ┌──────┴────────────┐
     │ ADC 发送 CGS(K28.5)│ ← 帮助 FPGA 实现字符边界对齐、CDR 锁定
     └──────┬────────────┘
            ↓
     ┌──────┴──────────────┐
     │ FPGA 识别 CGS 并拉高 SYNC~│ ← 表示准备接收 ILAS
     └──────┬──────────────┘
            ↓
     ┌──────┴────────────────────┐
     │ ADC 发送 ILAS(4 Multi-Frame) │ ← 传输 JESD 参数 & 多 Lane 对齐
     └──────┬────────────────────┘
            ↓
     ┌──────┴────────────────────┐
     │ FPGA 检查 ILAS 是否一致     │ ← 校验 F、K、M、L、S 等参数
     └──────┬────────────────────┘
            ↓
     ┌──────┴─────────────┐
     │ 进入数据传输状态(DATA) │ ← 链路 Ready!
     └────────────────────┘

✅ 1️⃣ CGS 阶段(Code Group Synchronization)

  • 由 FPGA 拉低 SYNC~ 启动
  • ADC 发送大量 /K28.5/ 控制符
  • 目的:
    • CDR 锁相
    • 找字节边界
    • 准备 ILAS 的同步基准

✅ 2️⃣ ILAS 阶段(Initial Lane Alignment Sequence)

  • FPGA 检测到 CGS 后 → 拉高 SYNC~
  • ADC 立即开始发 4 个多帧(Multi-Frame),内容包括:
    • 帧对齐符号(K28.0/K28.4/K28.7)
    • JESD 参数字段(F/K/M/S/L 等)
    • Lane ID、Device ID 等
  • 作用:
    • 多 Lane 数据对齐
    • 参数一致性校验
    • Subclass 1 中 LMFC 的同步参考点

✅ 3️⃣ 数据传输阶段(Data)

  • ILAS 正确接收后,进入正常传输状态
  • ADC 开始发送连续的数据帧
  • FPGA 开始解码并送入 AXI 或 FIFO 接口
  • 若中途链路错误、同步丢失,可能自动退回 CGS

🧠 Subclass 特殊流程说明(Subclass 1)
在 Subclass 1 中,额外存在:

  • SYSREF 信号输入
  • LMFC(Local Multi-Frame Clock)生成
  • ILAS 必须与 LMFC 对齐(边界固定)
  • 所以 SYSREF + ILAS 联合实现“确定性延迟”

🧪 JESD204B 状态机(简略)

状态描述
CGS接收 K28.5,同步 CD & 边界
ILAS接收 4 个多帧的初始化参数
CHECK校验 JESD 配置是否匹配
DATA正常数据传输
LOST如果出现错误,状态机会跳回 CGS 重建链路

📦 FPGA 中实现建链过程的模块

模块功能
GT Transceiver串行解码、CDR、8b10b 编码/解码
JESD204 RX IP包含链路状态机(CGS/ILAS/DATA),配置校验等
SYNC~ 控制控制建链流程,通常是输出引脚连到 ADC SYNC
SYSREF 输入Subclass 1 中的外部同步输入

✅ 总结

阶段关键信号发射方行为接收方行为
CGSSYNC~ = 0发 /K28.5/边界同步、CDR 锁定
ILASSYNC~ = 1发 4 个多帧(含参数)解码 JESD 参数、同步 LMFC
DATASYNC~ = 1发真实采样数据数据解帧、传送到用户逻辑

【飞腾】S5000C处理器SE_RECOVERY双BIOS镜像机制分析

概述

飞腾S5000C处理器支持双BIOS镜像(Dual BIOS)功能,通过SE_RECOVERY硬件引脚和Reset Source状态寄存器实现主备镜像的切换。当主BIOS镜像损坏时,可以通过硬件触发方式切换到备份镜像启动,提高系统可靠性。

一、硬件机制

1.1 Reset Source状态寄存器

S5000C芯片内部集成了一个 Reset Source硬件状态寄存器 ,用于记录芯片复位的原因。该寄存器由芯片硬件自动设置,软件通过SMC(Secure Monitor Call)调用读取。

Reset Source取值定义:

含义说明
0x01PowerOn Reset上电复位
0x04SoftWare Warm Reset软件热复位
0x05RecoverSE_RECOVERY 恢复模式
0x80Watchdog Reset看门狗复位
0x100PII ResetPLL 复位
0xAAS3 ResetS3 休眠恢复
0xA9Standby Reset待机恢复

1.2 SE_RECOVERY引脚

SE_RECOVERY是S5000C芯片的一个 硬件配置引脚 ,在芯片复位时刻被采样:

  • 正常状态(高电平) :芯片按正常流程启动,Reset Source = 0x01或0x04
  • 拉低状态 :芯片进入Recover模式,Reset Source = 0x05
    重要 :SE_RECOVERY引脚的状态只在 复位时刻 被采样,因此需要先拉低该引脚,然后触发芯片复位。

二、固件打包机制

2.1 Pack工具配置

在 s5000c_pack-V1.30 打包工具中,通过Kconfig配置启用双镜像功能:

config RECOVER_ENABLE
    bool "recover enable"
    default n

if RECOVER_ENABLE
    menu "recover config"
    config RECOVER_BL33
        hex "config bl33 recover address"
        default 0x800000
    config RECOVER_BL32
        hex "config bl32 recover address"  
        default 0xc00000
    endmenu
endif

bl33_recovery的地址在0x800000

bl32_recovery的地址在0xc00000

2.2 镜像存放位置

双镜像文件存放结构:

s5000c_pack-V1.30/
├── pbf_bin/v1_30/             # ATF编译生成的默认镜像
│   ├── bl33.bin
│   └── bl33_recover.bin
└── ...

2.3 Flash地址布局

打包后的固件在SPI Flash中的布局:

镜像Flash 偏移地址大小说明
BL1 (Boot ROM)0x000000启动代码
FIP (BL2/BL31)ARM Trusted Firmware
BL33 (主镜像)0x500000 (5MB)根据实际正常启动的 UEFI
BL33_RECOVER0x800000 (8MB)根据实际备份 UEFI 镜像
BL32/BL32_RECOVER0xb00000 (11MB)根据实际TEE 镜像(可选)

三、启动流程

3.1 正常启动流程

芯片上电/复位
    ↓
硬件采样SE_RECOVERY引脚 = 高电平
    ↓
Reset Source = 0x01 (PowerOn) 或 0x04 (Soft Reset)
    ↓
BL1 (Boot ROM) 运行
    ↓
读取Reset Source = 0x01/0x04
    ↓
从Flash 0x500000加载BL33 (主镜像)
    ↓
启动正常UEFI

3.2 Recovery启动流程

外部拉低SE_RECOVERY引脚
    ↓
触发芯片复位(上电或软复位)
    ↓
硬件采样SE_RECOVERY引脚 = 低电平
    ↓
Reset Source = 0x05 (Recover)
    ↓
BL1 (Boot ROM) 运行
    ↓
读取Reset Source = 0x05
    ↓
从Flash 0x800000加载BL33_RECOVER (备份镜像)
    ↓
启动备份UEFI

四、软件实现

4.1 UEFI中获取Reset Source

在UEFI代码中,通过PSSI库获取Reset Source:

// PssiLib.c
UINT32
PssiGetResetSource (VOID)
{
  ARM_SMC_ARGS  ArmSmcArgs;

  ZeroMem (&ArmSmcArgs, sizeof (ARM_SMC_ARGS));
  ArmSmcArgs.Arg0 = PSSI_GET_RST_SOURCE;  // SMC调用号: 0xC2000F01
  ArmCallSmc (&ArmSmcArgs);

  // 返回值即为Reset Source
  // 0x01=PowerOn, 0x04=SoftReset, 0x05=Recover...
  return ArmSmcArgs.Arg0;
}

4.2 Boot Mode设置

UEFI根据Reset Source设置Boot Mode:

// PlatformPei.c
EFI_BOOT_MODE  BootMode;

// 获取硬件复位源
RstSource = GetResetSource();
DEBUG ((DEBUG_INFO, "Check reset source : 0x%x\n", RstSource));

// 设置UEFI Boot Mode
Status = PeiServicesSetBootMode (ArmPlatformGetBootMode ());
Status = PeiServicesGetBootMode (&BootMode);

// 如果是Recover模式,安装Recovery Boot Mode PPI
if (BootMode == BOOT_IN_RECOVERY_MODE) {
    Status = PeiServicesInstallPpi (&mPpiListRecoveryBootMode);
}

4.3 打包脚本处理

在 theone 打包脚本中,双镜像的处理逻辑:

# 固件定义
BL33=bl33.bin                    # 主镜像
BL33_RECOVER=bl33_recover.bin    # 备份镜像

# 地址配置
bl33_base=0x500000               # 主镜像地址
bl33_recover_addr=0x800000       # 备份镜像地址

# 如果启用recover功能
if [ "$recover_enable" = "y" ];then
    # 创建recover镜像证书
    echo "create bl33 recover info"
    bl33_size=$(wc -c $BL33_RECOVER | awk '{print $1}')
    my_scripts/cert_info.sh $bl33_recover_addr $bl33_size bl33_info.
    bin
    dd if=bl33_info.bin of=$INFO bs=16 seek=1 conv=notrunc
fi

bl33.bin bl33_recover.bin放到pbf_bin文件夹里

五、实际应用场景

5.1 主BIOS损坏恢复

场景 :主BIOS镜像(0x500000)损坏,系统无法正常启动

恢复步骤 :

  1. 硬件设计中将SE_RECOVERY引脚连接到跳线或按钮
  2. 用户短接SE_RECOVERY引脚到地(拉低)
  3. 按下复位按钮或重新上电
  4. 芯片检测到Reset Source = 0x05
  5. 自动从0x800000加载备份BIOS启动
  6. 进入系统后修复主BIOS镜像

5.2 BIOS升级失败恢复

场景 :BIOS升级过程中断电,导致主镜像损坏

恢复机制 :

  • 备份镜像保持不变
  • 通过SE_RECOVERY触发恢复模式
  • 使用备份镜像启动后重新升级

六、总结

飞腾S5000C的SE_RECOVERY双BIOS镜像机制通过 硬件引脚 + 状态寄存器 + 固件支持 实现了可靠的故障恢复能力:

  1. 硬件层 :SE_RECOVERY引脚在复位时刻被采样,Reset Source寄存器记录复位原因
  2. 固件层 :BL1根据Reset Source决定从哪个Flash地址加载BL33镜像
  3. 工具层 :Pack工具支持双镜像打包,配置备份镜像地址
  4. 应用层 :UEFI可检测Recovery模式,执行不同的初始化流程
    这种设计确保了即使主BIOS镜像损坏,系统仍可以通过硬件触发方式从备份镜像启动,大大提高了系统的可靠性和可维护性。

七、flash xip启动注意事项

Nor Flash XIP 直接执行时,要求 Flash 硬件总线地址等于 SYS_TEXT_BASE,目的是让代码硬编码的链接地址和 CPU 实际取指地址一致,不用拷贝;

bl33.bin和bl33_recovery.bin的SYS_TEXT_BASE地址要根据pack实际使用地址进行修改。否则会报”Synchronous Abort” handler错误。