制作 arm-linux 的 sd 启动卡(AM335x 启动卡制作)
核心步骤:
- 制作用于拷贝的 SD 启动卡镜像文件:boot 文件(uboot,kernel等),roofs.tar.gz 文件系统
- 使用ubuntu 将待制作的启动卡 SD 卡进行格式化,格式化为需要的大小及内存格式分区
- 将步骤1 的镜像拷贝至步骤2 的对应分区,解压缩
踩坑经验:
- 步骤1 中的镜像制作时,须保持文件的所有者为 root;打包 rootfs.tar.gz 时,须注意将文件的属性一并进行打包
- 步骤3 ,文件拷贝至目标 SD 启动卡完成后,须注意解压缩后,某些文件的属性须设置为可执行,如init 文件,及其调用的 so 文件的属性等
可用的制卡脚本
点击查看代码
#!/bin/bash
# ==============================================================================
# 批量 SD 卡启动盘制作脚本 (基于 sfdisk)
# 适用场景: ARM 开发板 (AM335x, i.MX6, RK3399 等) 批量系统烧录
# 依赖工具: sfdisk, mkfs.vfat, mkfs.ext4, tar, md5sum, lsblk
# ==============================================================================
# ================= 配置区域 (请根据实际需求修改) =================
BOOT_FILES_DIR="./boot_files" # 存放 MLO, u-boot.img, zImage, dtb 等文件的目录
ROOTFS_ARCHIVE="./rootfs.tar.gz" # 根文件系统压缩包路径
LOG_FILE="batch_burn_log.csv" # 日志文件路径
SYSTEM_DISK="sda" # 【重要】当前主机的系统盘标识,防止误格式化
MIN_SIZE_GB=1 # 最小 SD 卡容量限制 (GB)
MAX_SIZE_GB=64 # 最大 SD 卡容量限制 (GB)
PARALLEL_JOBS=4 # 最大并行烧录数量
# ================================================================
# 全局变量
START_TIME=$(date +%s)
TARGET_DEVICES=()
FAILED_DEVICES=()
# 初始化日志
echo "Timestamp,Device,Status,Message" > "$LOG_FILE"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log_msg() {
local device=$1
local status=$2
local message=$3
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "${timestamp},${device},${status},${message}" >> "$LOG_FILE"
if [ "$status" == "FAIL" ]; then
echo -e "${RED}[FAIL]${NC} ${device}: ${message}"
elif [ "$status" == "SUCCESS" ]; then
echo -e "${GREEN}[OK]${NC} ${device}: ${message}"
else
echo -e "${YELLOW}[INFO]${NC} ${device}: ${message}"
fi
}
# 1. 自动识别候选 SD 卡
scan_devices() {
echo ">>> 正在扫描可用存储设备..."
local candidates=()
# 获取所有磁盘设备及其大小
while read -r dev_name dev_size_bytes; do
# 跳过系统盘
if [[ "$dev_name" == "$SYSTEM_DISK" ]]; then
continue
fi
# 计算大小 (GB)
local size_gb=$((dev_size_bytes / 1024 / 1024 / 1024))
# 检查容量范围
if [ "$size_gb" -ge "$MIN_SIZE_GB" ] && [ "$size_gb" -le "$MAX_SIZE_GB" ]; then
local full_path="/dev/${dev_name}"
# 检查是否被挂载,如果挂载则尝试卸载
if mount | grep -q "$full_path"; then
log_msg "$full_path" "WARN" "设备已挂载,将尝试卸载"
sudo umount -l "${full_path}"* 2>/dev/null
fi
candidates+=("$full_path")
fi
done < <(lsblk -d -n -o NAME,SIZE --bytes --noheadings | awk '{print $1, $2}')
if [ ${#candidates[@]} -eq 0 ]; then
echo -e "${RED}错误: 未找到符合条件的 SD 卡 (容量 ${MIN_SIZE_GB}-${MAX_SIZE_GB}GB, 非系统盘)${NC}"
exit 1
fi
TARGET_DEVICES=("${candidates[@]}")
echo ">>> 发现 ${#TARGET_DEVICES[@]} 张候选 SD 卡: ${TARGET_DEVICES[*]}"
}
# 2. 单卡烧录核心逻辑
burn_single_card() {
local DEV=$1
local boot_part="${DEV}1"
local root_part="${DEV}2"
log_msg "$DEV" "INFO" "开始烧录流程..."
# --- Step 1: 清理与准备 ---
# 强制卸载所有分区
for part in $(lsblk -n -o MOUNTPOINT "$DEV" 2>/dev/null | grep -v '^$'); do
sudo umount -l "$part" 2>/dev/null
done
# 清除旧分区表签名 (可选,防止残留引导信息)
sudo wipefs -a "$DEV" 2>/dev/null || true
# --- Step 2: 使用 sfdisk 分区 ---
# AM335x 典型分区布局:
# Partition 1: Start 2048, Size 200MB (FAT32, Bootable)
# Partition 2: Start 206848, Rest of disk (Linux)
log_msg "$DEV" "INFO" "执行分区操作..."
if ! sudo sfdisk "$DEV" <<EOF >/dev/null 2>&1
label: dos
label-id: 0x00000000
device: $DEV
unit: sectors
${DEV}1 : start=2048, size=409600, type=0c, bootable
${DEV}2 : start=411648, type=83
EOF
then
log_msg "$DEV" "FAIL" "sfdisk 分区失败"
return 1
fi
# 通知内核重新读取分区表
sudo partprobe "$DEV" 2>/dev/null || true
sleep 1 # 等待节点生成
# --- Step 3: 格式化 ---
log_msg "$DEV" "INFO" "格式化分区..."
if ! sudo mkfs.vfat -F 32 -n "boot" "${boot_part}" >/dev/null 2>&1; then
log_msg "$DEV" "FAIL" "Boot 分区格式化失败"
return 1
fi
if ! sudo mkfs.ext3 -L "rootfs" -m 0 "${root_part}" >/dev/null 2>&1; then
log_msg "$DEV" "FAIL" "Rootfs 分区格式化失败"
return 1
fi
# --- Step 4: 挂载与写入数据 ---
local MOUNT_BOOT=$(mktemp -d)
local MOUNT_ROOT=$(mktemp -d)
# 显式指定挂载参数,禁用noexec,确保根分区支持可执行文件
sudo mount -o rw,exec "${root_part}" "$MOUNT_ROOT"
sudo mount "${boot_part}" "$MOUNT_BOOT"
# 写入 Boot 文件
if [ -d "$BOOT_FILES_DIR" ]; then
sudo cp -r "${BOOT_FILES_DIR}"/* "$MOUNT_BOOT/"
# 确保MLO在FAT32根目录且无隐藏属性
sudo chmod 644 "$MOUNT_BOOT"/MLO 2>/dev/null || true
else
log_msg "$DEV" "WARN" "未找到 boot_files 目录,跳过 Boot 文件拷贝"
fi
# 写入 Rootfs:保留完整权限、所有者、特殊属性
if [ -f "$ROOTFS_ARCHIVE" ]; then
log_msg "$DEV" "INFO" "解压根文件系统 (耗时较长)..."
# 关键修改:添加--same-owner参数,完整保留原始文件所有者信息
if ! sudo tar xzpf "$ROOTFS_ARCHIVE" --same-owner -C "$MOUNT_ROOT" 2>/dev/null; then
log_msg "$DEV" "FAIL" "Rootfs 解压失败"
sudo umount "$MOUNT_BOOT" "$MOUNT_ROOT"
rm -rf "$MOUNT_BOOT" "$MOUNT_ROOT"
return 1
fi
# 新增:强制修复所有初始化脚本的可执行权限,彻底解决rcS权限拒绝
log_msg "$DEV" "INFO" "修复根文件系统初始化脚本权限..."
sudo chmod +x "${MOUNT_ROOT}/etc/init.d/rcS"
sudo chmod 755 "${MOUNT_ROOT}/etc/init.d/"*
# 确保passwd/shadow等关键系统文件权限符合安全规范
sudo chmod 644 "${MOUNT_ROOT}/etc/passwd"
sudo chmod 600 "${MOUNT_ROOT}/etc/shadow"
else
log_msg "$DEV" "WARN" "未找到 rootfs.tar.gz,跳过根文件系统写入"
fi
# 卸载前同步数据,避免权限属性未写入磁盘
sudo sync
sudo umount "$MOUNT_BOOT" "$MOUNT_ROOT"
rm -rf "$MOUNT_BOOT" "$MOUNT_ROOT"
# --- Step 5: 烧录后权限预校验 ---
log_msg "$DEV" "INFO" "执行烧录后权限预校验..."
# 临时挂载根分区,验证rcS权限是否正常
local TMP_CHECK=$(mktemp -d)
sudo mount "${root_part}" "$TMP_CHECK"
# 校验rcS是否具备可执行权限
if [ ! -x "${TMP_CHECK}/etc/init.d/rcS" ]; then
log_msg "$DEV" "FAIL" "rcS权限校验失败,可执行位丢失"
sudo umount "$TMP_CHECK"
rm -rf "$TMP_CHECK"
FAILED_DEVICES+=("$DEV")
return 1
fi
sudo umount "$TMP_CHECK"
rm -rf "$TMP_CHECK"
log_msg "$DEV" "SUCCESS" "SD卡烧录完成,rcS权限校验通过"
log_msg "$DEV" "SUCCESS" "烧录完成"
return 0
}
# 3. 并行执行控制器
run_parallel_burn() {
local pids=()
local index=0
echo ">>> 开始并行烧录 (最大并发: $PARALLEL_JOBS)"
for dev in "${TARGET_DEVICES[@]}"; do
# 等待空闲槽位
while [ $(jobs -r | wc -l) -ge $PARALLEL_JOBS ]; do
sleep 1
done
# 后台执行烧录
burn_single_card "$dev" &
pids+=($!)
done
# 等待所有后台任务完成
for pid in "${pids[@]}"; do
wait $pid
if [ $? -ne 0 ]; then
# 错误已在 burn_single_card 中记录
:
fi
done
echo ">>> 所有任务已结束"
}
# 4. 最终校验与报告
final_report() {
local end_time=$(date +%s)
local duration=$((end_time - START_TIME))
echo ""
echo "=========================================="
echo " 烧录任务总结"
echo "=========================================="
echo "总耗时: ${duration} 秒"
echo "成功/总数: $(grep -c "SUCCESS" "$LOG_FILE") / ${#TARGET_DEVICES[@]}"
echo "失败列表:"
grep "FAIL" "$LOG_FILE" | awk -F',' '{print " - " $2 ": " $4}'
echo "详细日志已保存至: $LOG_FILE"
echo "=========================================="
}
# ================= 主执行流程 =================
if [ "$(id -u)" -ne 0 ]; then
echo "错误: 请使用 sudo 运行此脚本"
exit 1
fi
# 检查必要文件
if [ ! -d "$BOOT_FILES_DIR" ] && [ ! -f "$ROOTFS_ARCHIVE" ]; then
echo "错误: 请确保 boot_files 目录或 rootfs.tar.gz 存在"
exit
fi
scan_devices
run_parallel_burn
final_report

浙公网安备 33010602011771号