基于 MT7621 (MIPS32) Linux 设备panic 处理
·
第一部分:U-Boot 阶段调试 (MT7621 MIPS32)
1.1 MT7621 U-Boot 启动流程与调试点
MT7621 U-Boot MIPS32 启动调用树
_start (arch/mips/cpu/mips32/start.S) | |-> reset (arch/mips/cpu/mips32/start.S) | |-> init_c0 (arch/mips/cpu/mips32/start.S) | | | |-> 初始化 CP0 状态寄存器 | |-> 设置异常向量基地址 | |-> lowlevel_init (arch/mips/cpu/mips32/mt7621/lowlevel_init.S) | | | |-> 初始化内存控制器 | |-> 配置系统时钟 | |-> 设置串口调试 | |-> relocate_code (arch/mips/lib/relocate.S) | | | |-> 代码重定位到 RAM | |-> board_init_f (common/board_f.c) | |-> init_sequence_f[] (common/board_f.c) | |-> mtk_timer_init (arch/mips/mach-mt7621/timer.c) |-> dram_init (arch/mips/mach-mt7621/sdram_mt7621.c) |-> mtk_serial_init (arch/mips/mach-mt7621/serial.c)
MT7621 串口调试初始化 (MIPS32)
// 文件: arch/mips/mach-mt7621/serial.c
void mtk_serial_init(void)
{
// MT7621 UART Lite 基地址: 0xBE000000 (物理地址)
struct mtk_uart *const uart = (struct mtk_uart *)0xBE000000;
// 1. 使能 UART 时钟 - 系统控制器配置
struct mtk_sysctl *sysctl = (struct mtk_sysctl *)MT7621_SYSCTL_BASE; // 0x10000000
// 设置 SYSCTL_CLKCFG1[16] 为 UART 时钟使能
writel(readl(&sysctl->clk_cfg1) | (1 << 16), &sysctl->clk_cfg1);
// 2. 配置 GPIO 复用为 UART 功能
// UART Lite: GPIO14 (TX), GPIO15 (RX)
struct mtk_gpio *gpio = (struct mtk_gpio *)MT7621_GPIO_BASE; // 0x10000600
// GPIO14 配置为 UART TX, GPIO15 配置为 UART RX
writel((readl(&gpio->gpio_mode) & ~0x3) | 0x1, &gpio->gpio_mode); // 设置复用模式
// 3. 配置 UART 参数: 57600 波特率, 8N1
unsigned long clock_rate = 40000000; // MT7621 UART 时钟频率 40MHz
unsigned long baud_rate = 57600; // 调试波特率
unsigned long divisor = clock_rate / (baud_rate * 16);
// 4. 设置 UART 寄存器
uart->ier = 0; // 禁用中断
uart->lcr = UART_LCR_DLAB; // 使能分频锁存器访问
uart->rbr = divisor & 0xff; // 分频低字节
uart->ier = (divisor >> 8) & 0xff; // 分频高字节
uart->lcr = UART_LCR_8BIT; // 8位数据,无校验,1停止位
uart->fcr = UART_FCR_FIFO_EN | // 使能 FIFO
UART_FCR_CLEAR_RCVR | // 清除接收 FIFO
UART_FCR_CLEAR_XMIT; // 清除发送 FIFO
// 5. 使能中断
uart->ier = UART_IER_RDI; // 使能接收数据可用中断
printf("MT7621 UART Lite initialized at 0x%08lx, baud %lu\n",
(ulong)uart, baud_rate);
}
1.2 U-Boot 死在启动时的调试方法
使用 MT7621 串口调试输出
// 文件: common/console.c
// 串口输出字符函数 (MIPS32 实现)
void serial_putc(const char c)
{
struct mtk_uart *uart = (struct mtk_uart *)CONFIG_DEBUG_UART_BASE; // 0xBE000000
// 等待发送缓冲区空 - 检查线路状态寄存器 THRE 位
while (!(readl(&uart->lsr) & UART_LSR_THRE)) {
// MIPS32 空循环等待
asm volatile ("nop");
}
// 发送字符到发送保持寄存器
writel(c, &uart->rbr);
// 如果是换行符,发送回车
if (c == '\n')
serial_putc('\r');
}
// 在关键启动位置插入调试标记
void board_init_f(ulong boot_flags)
{
// 调试标记 A: 进入 board_init_f
serial_putc('A');
// 1. 初始化全局数据
serial_putc('B');
gd = (gd_t *)(CONFIG_SYS_SDRAM_BASE + CONFIG_SYS_INIT_SP_OFFSET);
memset(gd, 0, sizeof(gd_t));
// 2. DRAM 初始化 - MT7621 关键步骤
serial_putc('C');
dram_init();
// 3. 定时器初始化
serial_putc('D');
timer_init();
// 4. 串口控制台初始化
serial_putc('E');
console_init_f();
// 调试标记 F: board_init_f 完成
serial_putc('F');
}
MT7621 DRAM 初始化失败调试
// 文件: arch/mips/mach-mt7621/sdram_mt7621.c
int dram_init(void)
{
struct mt7621_ddr_params *params;
int ret;
printf("DRAM: Starting MT7621 DDR2 initialization\n");
// 1. 获取 DRAM 配置参数
params = get_ddr_config();
if (!params) {
printf("DRAM: Failed to get configuration\n");
return -EINVAL;
}
// 2. 配置 DDR PHY
printf("DRAM: Configuring PHY\n");
ret = ddr_phy_config(params);
if (ret) {
printf("DRAM: PHY configuration failed: %d\n", ret);
return ret;
}
// 3. 配置 DDR 控制器
printf("DRAM: Configuring controller\n");
ret = ddr_pctl_config(params);
if (ret) {
printf("DRAM: Controller configuration failed: %d\n", ret);
return ret;
}
// 4. 执行内存校准
printf("DRAM: Starting memory calibration\n");
ret = ddr_calibration(params);
if (ret) {
printf("DRAM: Memory calibration failed: %d\n", ret);
return ret;
}
// 5. 设置内存大小
gd->ram_size = params->dram_size;
printf("DRAM: Initialized %lu MB\n", gd->ram_size / 1024 / 1024);
return 0;
}
// DDR 校准状态检查
static int check_calibration_status(void)
{
struct mt7621_ddr_pctl *pctl = (struct mt7621_ddr_pctl *)MT7621_DDR_PCTL_BASE;
u32 status;
int timeout = 100000; // 100ms 超时
while (timeout--) {
status = readl(&pctl->stat);
if (status & DDR_CALIBRATION_DONE) {
return 0; // 校准完成
}
udelay(1);
}
printf("DRAM: Calibration timeout, status: 0x%08x\n", status);
return -ETIMEDOUT;
}
第二部分:Linux 内核启动调试 (MT7621 MIPS32)
2.1 MT7621 MIPS32 内核启动流程
MIPS32 内核启动调用树
kernel_entry (arch/mips/kernel/head.S) | |-> kernel_entry_setup (arch/mips/kernel/head.S) | | | |-> 设置 CP0 状态寄存器 | |-> 配置异常处理 | |-> start_kernel (init/main.c) | |-> setup_arch (arch/mips/kernel/setup.c) | | | |-> prom_init (arch/mips/ralink/prom.c) | |-> early_serial_setup (arch/mips/ralink/serial.c) | |-> trap_init (arch/mips/kernel/traps.c) | | | |-> 设置异常处理向量 | |-> time_init (arch/mips/ralink/time.c) | | | |-> mtk_timer_init (arch/mips/ralink/mt7621/time.c) | |-> console_init (drivers/tty/serial/8250/8250_core.c)
MT7621 早期串口控制台初始化
// 文件: arch/mips/ralink/mt7621/serial.c
// 早期控制台设置
void __init early_serial_setup(void)
{
// 映射 UART Lite 物理地址到虚拟地址
// MT7621 UART Lite 物理地址: 0xBE000000
void __iomem *uart_base = ioremap_nocache(0xBE000000, SZ_4K);
if (!uart_base) {
pr_err("Failed to map debug UART\n");
return;
}
// 配置早期控制台
early_console_init(uart_base);
}
// 文件: arch/mips/include/asm/serial.h
// MIPS32 串口调试宏
#define UART_MT7621_BASE 0xBE000000
// 早期打印字符函数
static inline void prom_putchar(char c)
{
volatile u8 *uart = (volatile u8 *)UART_MT7621_BASE;
// 等待发送缓冲区空
while (!(uart[5] & 0x20))
;
// 发送字符
uart[0] = c;
// 如果是换行符,发送回车
if (c == '\n')
prom_putchar('\r');
}
2.2 内核死在启动时的调试方法
使用 earlyprintk 调试 MIPS32 内核
// 在内核命令行添加: earlyprintk=serial,0xBE000000,57600
// 文件: arch/mips/kernel/early_printk.c
static struct earlycon_device early_console_dev;
void __init early_printk(const char *fmt, ...)
{
char buf[512];
va_list args;
int i;
// 1. 格式化字符串
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
// 2. 通过串口输出每个字符
for (i = 0; buf[i] && i < sizeof(buf); i++) {
prom_putchar(buf[i]);
}
}
// 在关键启动函数中添加调试输出
asmlinkage void __init start_kernel(void)
{
early_printk("MIPS32: Entering start_kernel\n");
// 设置架构相关
early_printk("MIPS32: Setting up architecture\n");
setup_arch(&command_line);
early_printk("MIPS32: Initializing traps\n");
trap_init();
early_printk("MIPS32: Initializing memory\n");
mm_init();
early_printk("MIPS32: Starting rest_init\n");
rest_init();
}
MT7621 时钟初始化调试
// 文件: arch/mips/ralink/mt7621/time.c
void __init plat_time_init(void)
{
struct clk *clk;
unsigned long rate;
int ret;
pr_info("MT7621: Initializing clocks\n");
// 1. 获取 CPU 时钟
clk = clk_get(NULL, "cpu");
if (IS_ERR(clk)) {
pr_err("MT7621: Failed to get CPU clock\n");
return;
}
rate = clk_get_rate(clk);
pr_info("MT7621: CPU clock: %lu Hz\n", rate);
// 2. 获取总线时钟
clk = clk_get(NULL, "bus");
if (IS_ERR(clk)) {
pr_err("MT7621: Failed to get bus clock\n");
return;
}
rate = clk_get_rate(clk);
pr_info("MT7621: Bus clock: %lu Hz\n", rate);
// 3. 初始化系统定时器
ret = mtk_timer_init();
if (ret) {
pr_err("MT7621: Timer initialization failed: %d\n", ret);
return;
}
pr_info("MT7621: Clock controller initialized\n");
}
// MT7621 定时器初始化
static int __init mtk_timer_init(void)
{
struct device_node *np;
void __iomem *base;
int irq, ret;
// 查找定时器节点
np = of_find_compatible_node(NULL, NULL, "mediatek,timer");
if (!np) {
pr_err("MT7621: No timer node found\n");
return -ENODEV;
}
// 映射定时器寄存器
base = of_iomap(np, 0);
if (!base) {
pr_err("MT7621: Failed to map timer registers\n");
return -ENOMEM;
}
// 获取中断
irq = irq_of_parse_and_map(np, 0);
if (irq <= 0) {
pr_err("MT7621: Failed to get timer IRQ\n");
return -EINVAL;
}
// 注册时钟事件设备
ret = mtk_clockevent_init(base, irq);
if (ret) {
pr_err("MT7621: Clockevent init failed: %d\n", ret);
return ret;
}
// 注册时钟源
ret = mtk_clocksource_init(base);
if (ret) {
pr_err("MT7621: Clocksource init failed: %d\n", ret);
return ret;
}
pr_info("MT7621: Timer initialized\n");
return 0;
}
第三部分:根文件系统挂载调试 (MT7621)
3.1 MT7621 根文件系统挂载流程
MIPS32 挂载调用树
start_kernel (init/main.c) | |-> vfs_caches_init (fs/dcache.c) | | | |-> dcache_init (fs/dcache.c) | |-> inode_init (fs/inode.c) | |-> files_init (fs/file_table.c) | |-> mnt_init (fs/namespace.c) | | | |-> sysfs_init (fs/sysfs/mount.c) | |-> rootfs_init (fs/ramfs/inode.c) | |-> prepare_namespace (init/do_mounts.c) | |-> mount_root (init/do_mounts.c) | |-> mount_block_root (init/do_mounts.c) | |-> do_mount_root (init/do_mounts.c) | |-> vfs_kern_mount (fs/namespace.c)
MT7621 SPI Flash 驱动初始化 (MIPS32)
// 文件: drivers/mtd/spi-nor/spi-mt7621.c
static int mtk_spi_probe(struct platform_device *pdev)
{
struct spi_controller *ctlr;
struct mtk_spi *ms;
struct resource *res;
int irq, ret;
pr_info("MT7621: Probing SPI controller\n");
// 1. 分配 SPI 控制器
ctlr = spi_alloc_master(&pdev->dev, sizeof(*ms));
if (!ctlr)
return -ENOMEM;
ms = spi_controller_get_devdata(ctlr);
platform_set_drvdata(pdev, ctlr);
// 2. 获取寄存器资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ms->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(ms->base)) {
ret = PTR_ERR(ms->base);
goto err_put_ctlr;
}
// 3. 获取中断
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = irq;
goto err_put_ctlr;
}
// 4. 获取时钟
ms->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(ms->clk)) {
ret = PTR_ERR(ms->clk);
goto err_put_ctlr;
}
// 5. MT7621 特定配置
ctlr->dev.of_node = pdev->dev.of_node;
ctlr->mode_bits = SPI_CPOL | SPI_CPHA;
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
ctlr->num_chipselect = 1;
ctlr->setup = mtk_spi_setup;
ctlr->transfer_one = mtk_spi_transfer_one;
// 6. 注册 SPI 控制器
ret = devm_spi_register_controller(&pdev->dev, ctlr);
if (ret) {
dev_err(&pdev->dev, "Failed to register SPI controller: %d\n", ret);
goto err_put_ctlr;
}
dev_info(&pdev->dev, "MT7621 SPI controller initialized\n");
return 0;
err_put_ctlr:
spi_controller_put(ctlr);
return ret;
}
3.2 根文件系统挂载失败调试
使用 initramfs 进行调试
// 在内核命令行添加: root=/dev/mtdblock5 rw rootwait init=/bin/sh
// 文件: init/do_mounts.c
void __init prepare_namespace(void)
{
int err;
char *root_device_name = NULL;
pr_emerg("MIPS32: Preparing namespace\n");
// 1. 等待根设备就绪
if (root_wait) {
pr_emerg("Waiting for root device %s...\n",
__bdevname(ROOT_DEV, root_device_name));
// MIPS32 特定的设备等待
while (driver_probe_done() != 0) {
mdelay(100);
touch_nmi_watchdog();
}
}
// 2. 挂载根文件系统
pr_emerg("Mounting root filesystem\n");
err = mount_root();
if (err) {
pr_emerg("Failed to mount root fs on %s, err %d\n",
root_device_name, err);
// 打印 MTD 设备信息用于调试
printk("Available MTD devices:\n");
for (int i = 0; i < MAX_MTD_DEVICES; i++) {
struct mtd_info *mtd = get_mtd_device(NULL, i);
if (!IS_ERR(mtd)) {
printk("mtd%d: %s, size 0x%08llx\n",
i, mtd->name, mtd->size);
put_mtd_device(mtd);
}
}
panic("VFS: Unable to mount root fs");
}
// 3. 切换到根文件系统
pr_emerg("Switching to root filesystem\n");
err = sys_chdir("/root");
if (err) {
pr_emerg("Failed to change to root directory: %d\n", err);
panic("VFS: Unable to change to root directory");
}
// 4. 执行 pivot_root
err = sys_pivot_root(".", ".");
if (err) {
pr_emerg("Failed to pivot root: %d\n", err);
panic("VFS: Unable to pivot root");
}
pr_emerg("MIPS32: Root filesystem mounted successfully\n");
}
MT7621 设备树存储节点配置
// 文件: arch/mips/boot/dts/mediatek/mt7621.dtsi
// SPI 控制器
spi0: spi@1e000000 {
compatible = "mediatek,mt7621-spi";
reg = <0x1e000000 0x1000>;
interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&sysclock>;
clock-names = "spi";
resets = <&sysreset 18>;
status = "disabled";
};
// 文件: arch/mips/boot/dts/mediatek/mt7621-rfb.dts
&spi0 {
status = "okay";
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <50000000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x0 0x30000>;
};
partition@30000 {
label = "u-boot-env";
reg = <0x30000 0x10000>;
};
partition@40000 {
label = "factory";
reg = <0x40000 0x10000>;
};
partition@50000 {
label = "firmware";
reg = <0x50000 0xfb0000>;
};
};
};
};
第四部分:应用程序启动调试 (MIPS32)
4.1 MIPS32 应用程序调试准备
交叉编译 GDB 和 GDBServer
# 配置 MIPS32 交叉编译工具链 export CROSS_COMPILE=mipsel-openwrt-linux- export ARCH=mips # 编译 GDBServer cd gdb-9.2/gdb/gdbserver ./configure --host=mipsel-openwrt-linux --target=mipsel-openwrt-linux make # 复制到 MT7621 设备 scp gdbserver root@192.168.1.1:/usr/bin/ # 编译 GDB (在开发机上) cd gdb-9.2 ./configure --target=mipsel-openwrt-linux make
MIPS32 应用程序调试符号
// 文件: app_main.c (MIPS32 应用程序)
#include <signal.h>
#include <execinfo.h>
#define BT_BUF_SIZE 100
// MIPS32 回溯处理函数
void mips32_backtrace(int sig)
{
void *buffer[BT_BUF_SIZE];
char **strings;
int nptrs;
FILE *fp;
// 打开日志文件
fp = fopen("/tmp/backtrace.log", "a");
if (!fp) return;
// 获取回溯
nptrs = backtrace(buffer, BT_BUF_SIZE);
fprintf(fp, "Backtrace (MIPS32) for PID %d, signal %d:\n",
getpid(), sig);
// 解析符号
strings = backtrace_symbols(buffer, nptrs);
if (strings) {
for (int i = 0; i < nptrs; i++) {
fprintf(fp, "#%d %s\n", i, strings[i]);
}
free(strings);
}
fclose(fp);
// 重新抛出信号
signal(sig, SIG_DFL);
raise(sig);
}
int main(int argc, char *argv[])
{
// 设置信号处理
signal(SIGSEGV, mips32_backtrace);
signal(SIGABRT, mips32_backtrace);
signal(SIGILL, mips32_backtrace);
// MIPS32 特定初始化
printf("MIPS32 Application starting on MT7621\n");
// 主应用程序逻辑
return application_main(argc, argv);
}
4.2 使用 GDB 调试 MIPS32 应用程序
GDBServer 启动配置
# 在 MT7621 设备上启动 GDBServer gdbserver :2345 /usr/bin/app_main # 在开发机上连接 GDB mipsel-openwrt-linux-gdb (gdb) target remote 192.168.1.1:2345 (gdb) file app_main (gdb) break main (gdb) continue
MIPS32 寄存器调试
// 文件: mips32_debug.c
#include <sys/ptrace.h>
#include <sys/user.h>
// MIPS32 用户寄存器结构 (来自 Linux 内核)
struct mips32_user_regs {
unsigned long regs[32]; // 通用寄存器 $0-$31
unsigned long lo; // LO 寄存器
unsigned long hi; // HI 寄存器
unsigned long cp0_epc; // CP0 EPC 寄存器
unsigned long cp0_badvaddr;// CP0 BadVAddr 寄存器
unsigned long cp0_status; // CP0 Status 寄存器
unsigned long cp0_cause; // CP0 Cause 寄存器
};
void debug_mips32_registers(pid_t pid)
{
struct mips32_user_regs regs;
// 获取 MIPS32 寄存器
if (ptrace(PTRACE_GETREGS, pid, 0, ®s) == -1) {
perror("ptrace GETREGS failed");
return;
}
printf("MIPS32 Registers for PID %d:\n", pid);
printf("$0(zero): 0x%08lx $1(at): 0x%08lx $2(v0): 0x%08lx $3(v1): 0x%08lx\n",
regs.regs[0], regs.regs[1], regs.regs[2], regs.regs[3]);
printf("$4(a0): 0x%08lx $5(a1): 0x%08lx $6(a2): 0x%08lx $7(a3): 0x%08lx\n",
regs.regs[4], regs.regs[5], regs.regs[6], regs.regs[7]);
printf("$8(t0): 0x%08lx $9(t1): 0x%08lx $10(t2): 0x%08lx $11(t3): 0x%08lx\n",
regs.regs[8], regs.regs[9], regs.regs[10], regs.regs[11]);
printf("$12(t4): 0x%08lx $13(t5): 0x%08lx $14(t6): 0x%08lx $15(t7): 0x%08lx\n",
regs.regs[12], regs.regs[13], regs.regs[14], regs.regs[15]);
printf("$16(s0): 0x%08lx $17(s1): 0x%08lx $18(s2): 0x%08lx $19(s3): 0x%08lx\n",
regs.regs[16], regs.regs[17], regs.regs[18], regs.regs[19]);
printf("$20(s4): 0x%08lx $21(s5): 0x%08lx $22(s6): 0x%08lx $23(s7): 0x%08lx\n",
regs.regs[20], regs.regs[21], regs.regs[22], regs.regs[23]);
printf("$24(t8): 0x%08lx $25(t9): 0x%08lx $26(k0): 0x%08lx $27(k1): 0x%08lx\n",
regs.regs[24], regs.regs[25], regs.regs[26], regs.regs[27]);
printf("$28(gp): 0x%08lx $29(sp): 0x%08lx $30(s8): 0x%08lx $31(ra): 0x%08lx\n",
regs.regs[28], regs.regs[29], regs.regs[30], regs.regs[31]);
printf("LO: 0x%08lx HI: 0x%08lx EPC: 0x%08lx\n",
regs.lo, regs.hi, regs.cp0_epc);
printf("Status: 0x%08lx Cause: 0x%08lx BadVAddr: 0x%08lx\n",
regs.cp0_status, regs.cp0_cause, regs.cp0_badvaddr);
}
第五部分:内存泄漏调试 (MIPS32)
5.1 MIPS32 内核内存泄漏检测
使用 kmemleak 检测 MIPS32 内核内存泄漏
// 在内核命令行添加: kmemleak=on
// 文件: mm/kmemleak.c
// MIPS32 特定的内存分配跟踪
void *__kmalloc(size_t size, gfp_t gfp)
{
void *ptr;
// MIPS32 使用 kmalloc_caches
ptr = __do_kmalloc(size, gfp, _RET_IP_);
// kmemleak 记录
if (kmemleak_enabled && ptr && !(gfp & __GFP_NOLEAKTRACE)) {
kmemleak_alloc(ptr, size, 1, gfp);
}
return ptr;
}
// MIPS32 vmalloc 跟踪
void *vmalloc(unsigned long size)
{
void *addr;
// MIPS32 vmalloc 区域在 0xc0000000 以上
addr = __vmalloc_node_range(size, 1, VMALLOC_START, VMALLOC_END,
GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE,
__builtin_return_address(0));
// kmemleak 记录 vmalloc 分配
if (kmemleak_enabled && addr) {
kmemleak_alloc(addr, size, 2, GFP_KERNEL);
}
return addr;
}
MT7621 特定驱动的内存泄漏检测
// 文件: drivers/net/ethernet/mediatek/mtk_eth_soc.c
struct mtk_mac {
struct device *dev;
struct mtk_eth *eth;
int id;
struct phy_device *phy;
u32 msg_enable;
};
static int mtk_probe(struct platform_device *pdev)
{
struct mtk_eth *eth;
struct resource *res;
int i, ret;
// 分配以太网控制器结构
eth = devm_kzalloc(&pdev->dev, sizeof(*eth), GFP_KERNEL);
if (!eth)
return -ENOMEM;
// kmemleak 跟踪
kmemleak_alloc(eth, sizeof(*eth), 1, GFP_KERNEL);
platform_set_drvdata(pdev, eth);
eth->dev = &pdev->dev;
// 分配 MAC 结构
eth->mac = devm_kcalloc(&pdev->dev, MTK_MAC_COUNT,
sizeof(*eth->mac), GFP_KERNEL);
if (!eth->mac) {
ret = -ENOMEM;
goto free_eth;
}
kmemleak_alloc(eth->mac, sizeof(*eth->mac) * MTK_MAC_COUNT, 1, GFP_KERNEL);
// 初始化每个 MAC
for (i = 0; i < MTK_MAC_COUNT; i++) {
eth->mac[i] = devm_kzalloc(&pdev->dev, sizeof(struct mtk_mac),
GFP_KERNEL);
if (!eth->mac[i]) {
ret = -ENOMEM;
goto free_macs;
}
kmemleak_alloc(eth->mac[i], sizeof(struct mtk_mac), 1, GFP_KERNEL);
eth->mac[i]->id = i;
eth->mac[i]->eth = eth;
}
return 0;
free_macs:
for (i = 0; i < MTK_MAC_COUNT; i++) {
if (eth->mac[i]) {
kmemleak_free(eth->mac[i]);
devm_kfree(&pdev->dev, eth->mac[i]);
}
}
kmemleak_free(eth->mac);
free_eth:
kmemleak_free(eth);
return ret;
}
5.2 MIPS32 用户空间内存泄漏检测
使用 mtrace 检测应用程序内存泄漏
// 文件: app_main.c
#include <mcheck.h>
int main(int argc, char *argv[])
{
// 启用内存跟踪
setenv("MALLOC_TRACE", "/tmp/app_mtrace.log", 1);
mtrace();
// 应用程序逻辑
application_main(argc, argv);
// 关闭内存跟踪
muntrace();
return 0;
}
// 在开发机上分析内存泄漏
// bash
mtrace app_main /tmp/app_mtrace.log
MIPS32 内存调试包装函数
// 文件: mips32_mem_debug.c
#ifdef MIPS32_MEMORY_DEBUG
#include <execinfo.h>
// MIPS32 内存分配跟踪
void *mips32_debug_malloc(size_t size, const char *file, int line)
{
void *ptr = malloc(size);
if (ptr) {
void *bt[10];
int frames = backtrace(bt, 10);
char **symbols = backtrace_symbols(bt, frames);
fprintf(stderr, "MIPS32 MALLOC: %p %zu bytes at %s:%d\n",
ptr, size, file, line);
fprintf(stderr, "Backtrace:\n");
for (int i = 0; i < frames; i++) {
fprintf(stderr, " #%d %s\n", i, symbols[i]);
}
free(symbols);
}
return ptr;
}
void mips32_debug_free(void *ptr, const char *file, int line)
{
if (ptr) {
fprintf(stderr, "MIPS32 FREE: %p at %s:%d\n", ptr, file, line);
free(ptr);
}
}
// 内存使用报告
void mips32_memory_report(void)
{
struct mallinfo mi = mallinfo();
fprintf(stderr, "MIPS32 Memory Report:\n");
fprintf(stderr, " Total non-mmapped bytes: %d\n", mi.arena);
fprintf(stderr, " Number of free chunks: %d\n", mi.ordblks);
fprintf(stderr, " Bytes in free chunks: %d\n", mi.fordblks);
fprintf(stderr, " Maximum total allocated space: %d\n", mi.usmblks);
fprintf(stderr, " Bytes in mmapped regions: %d\n", mi.hblks);
fprintf(stderr, " Total allocated space: %d\n", mi.uordblks);
fprintf(stderr, " Total free space: %d\n", mi.fordblks);
fprintf(stderr, " Releasable free space: %d\n", mi.keepcost);
}
#endif
第六部分:系统崩溃和稳定性调试
6.1 MIPS32 内核崩溃分析
MIPS32 Oops 分析
// 文件: arch/mips/kernel/traps.c
// MIPS32 异常处理
asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write,
unsigned long address)
{
struct task_struct *tsk = current;
struct mm_struct *mm = tsk->mm;
struct vm_area_struct *vma;
int fault;
// 检查地址是否在用户空间
if (address < TASK_SIZE) {
// 用户空间页错误
if (!mm) {
// 没有内存映射
pr_alert("MIPS32: Page fault in NULL mm, address 0x%08lx\n",
address);
goto bad_area_nosemaphore;
}
} else {
// 内核空间页错误
pr_alert("MIPS32: Kernel page fault, address 0x%08lx\n", address);
goto no_context;
}
// 尝试处理页错误
fault = handle_mm_fault(vma, address, write ? FAULT_FLAG_WRITE : 0, regs);
if (fault & VM_FAULT_ERROR) {
// 页错误处理失败
if (fault & VM_FAULT_OOM) {
pr_alert("MIPS32: Out of memory\n");
goto out_of_memory;
} else if (fault & VM_FAULT_SIGBUS) {
pr_alert("MIPS32: SIGBUS error\n");
goto do_sigbus;
}
}
return;
bad_area_nosemaphore:
// 用户空间错误区域
pr_alert("MIPS32: Bad area access\n");
force_sig_fault(SIGSEGV, SEGV_MAPERR, (void __user *)address);
return;
no_context:
// 内核空间无上下文
pr_alert("MIPS32: No context for kernel page fault\n");
die("Oops", regs);
return;
out_of_memory:
// 内存不足
pr_alert("MIPS32: Out of memory\n");
if (!user_mode(regs))
goto no_context;
pagefault_out_of_memory();
return;
do_sigbus:
// 总线错误
pr_alert("MIPS32: Bus error\n");
force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)address);
}
// MIPS32 寄存器显示
void show_regs(struct pt_regs *regs)
{
pr_emerg("MIPS32 Registers:\n");
pr_emerg("$0 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
regs->regs[0], regs->regs[1], regs->regs[2], regs->regs[3],
regs->regs[4], regs->regs[5], regs->regs[6], regs->regs[7]);
pr_emerg("$8 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
regs->regs[8], regs->regs[9], regs->regs[10], regs->regs[11],
regs->regs[12], regs->regs[13], regs->regs[14], regs->regs[15]);
pr_emerg("$16: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
regs->regs[16], regs->regs[17], regs->regs[18], regs->regs[19],
regs->regs[20], regs->regs[21], regs->regs[22], regs->regs[23]);
pr_emerg("$24: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
regs->regs[24], regs->regs[25], regs->regs[26], regs->regs[27],
regs->regs[28], regs->regs[29], regs->regs[30], regs->regs[31]);
pr_emerg("Hi : %08lx\nLo : %08lx\n", regs->hi, regs->lo);
pr_emerg("epc : %08lx %pS\n", regs->cp0_epc, (void *)regs->cp0_epc);
pr_emerg("ra : %08lx %pS\n", regs->regs[31], (void *)regs->regs[31]);
}
6.2 MT7621 系统稳定性监控
系统监控脚本
#!/bin/bash # mt7621_monitor.sh # 监控系统状态 while true; do echo "=== MT7621 System Status $(date) ===" # CPU 使用率 echo "CPU Usage:" mpstat 1 1 | tail -1 # 内存使用 echo "Memory Usage:" free -m # 温度监控 if [ -f /sys/class/thermal/thermal_zone0/temp ]; then temp=$(cat /sys/class/thermal/thermal_zone0/temp) echo "CPU Temperature: $((temp/1000))°C" fi # 网络统计 echo "Network Statistics:" netstat -i # 进程内存泄漏检查 echo "Top memory processes:" ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%mem | head -10 sleep 30 done
更多推荐
所有评论(0)