森yyds 2026-04-27 17:49 采纳率: 0%
浏览 8

内核串口仅在直接给串口地址赋值时输出

问题描述

串口只会在直接给串口地址赋值是输出 凡是包含进函数都无法输出

代码

boot.s

.section ".text.boot"
.global _start
_start:
    mov x0,= 0x90000
    mov sp, x0
    ldr x0, __bss_start
    ldr x1, __bss_end
    mov x2, #0
clear_bss_loop:
    cmp x0, x1
    beq __start_kernel
    str x2, [x0], #8
    b clear_bss_loop
__start_kernel:
    bl main
    b .

kernel.c

#define UART0 0x09000000
typedef volatile unsigned char uint8_t;
*(uint8_t *)uart = (uint8_t *)UART0;

void uart_putc(char c) {
    *uart = c;
}

void uart_puts(const char *s) {
    while (*s) {
        uart_putc(*s++);
    }
}

void main(void) {
    uart_puts("hello,world!");
}

link.ld

ENTRY(_start)
SECTIONS {
    . = 0x80000;
    .text : { *(.text) *(.text.boot) }
    .data : { *(.data) }
    .bss : {
        __bss_start = .;
        *(.bss)
        __bss_end = .;
    }
}

使用环境

termux

使用虚拟环境

qemu virt

  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2026-04-27 17:50
    关注

    下午好🌅🌅🌅
    本答案参考通义千问

    你描述的问题是:在内核串口代码中,只有当直接给串口地址赋值时才能输出数据,而一旦将代码封装进函数(如 uart_putsuart_putc),就无法输出内容。

    这表明 你的程序可能没有正确地初始化串口硬件或未正确设置内存映射,或者 某些编译器优化导致函数中的代码被移除


    ✅ 问题分析

    1. 内存映射问题

    你定义了 UART0 = 0x09000000,但需要确认该地址是否为真实存在的设备地址,并且 是否被正确映射到物理内存。如果这个地址在 QEMU 或虚拟环境中无效,则无论是否调用函数,都无法输出。

    2. 函数调用与编译器优化

    如果你的 main() 调用了 uart_puts("hello,world!"),但 编译器优化(如 -O2)可能会移除看似无用的代码,特别是当你没有使用任何变量、没有打印任何调试信息时。

    3. 硬件初始化问题

    你可能没有正确初始化串口控制器(如波特率、模式等)。即使地址正确,若寄存器配置错误,也无法输出。


    🔧 解决方案

    ✅ 第一步:确保 UART 地址有效

    你需要确认 0x09000000 是否为 QEMU 模拟的串口地址。通常,在 QEMU 中,串口地址为:

    • 0x10000000(对于 virt 配置)
    • 0x80000000(也可能)

    你可以尝试修改 UART0 的值进行测试,例如:

    #define UART0 0x10000000
    

    ✅ 第二步:禁用编译器优化

    在编译时,不要使用 -O2 或更高优化级别,否则编译器可能移除看似无用的代码。

    示例编译命令(假设使用 arm-linux-gnueabi-gcc):

    arm-linux-gnueabi-gcc -nostdlib -static -o kernel.elf kernel.c boot.s -Wl,-Tlink.ld
    

    ✅ 第三步:确保函数不会被优化掉

    main() 中添加一些变量来防止优化:

    void main(void) {
        char *s = "hello,world!";
        uart_puts(s);
    }
    

    ✅ 第四步:检查串口寄存器配置

    如果你的串口驱动只是写入一个字符,但没有配置寄存器(如控制寄存器、波特率寄存器等),那么即使地址正确也无法输出。以下是一个简单的 UART 控制寄存器示例(假设为 8N1 模式):

    #define UART0 0x10000000
    #define UART_THR 0x00   // Transmit Holding Register
    #define UART_LSR 0x05   // Line Status Register
    
    void uart_putc(char c) {
        while ((*(uint8_t*)(UART0 + UART_LSR) & 0x20) == 0); // Wait until TX buffer is empty
        *(uint8_t*)(UART0 + UART_THR) = c;
    }
    

    ⚠️ 注意:具体寄存器偏移需根据你使用的硬件手册确定。


    🛠 修改后的完整代码

    kernel.c

    #define UART0 0x10000000
    #define UART_THR 0x00
    #define UART_LSR 0x05
    
    typedef volatile unsigned char uint8_t;
    
    void uart_putc(char c) {
        while ((*(uint8_t*)(UART0 + UART_LSR) & 0x20) == 0); // Wait for TX buffer to be empty
        *(uint8_t*)(UART0 + UART_THR) = c;
    }
    
    void uart_puts(const char *s) {
        while (*s) {
            uart_putc(*s++);
        }
    }
    
    void main(void) {
        char *msg = "hello,world!";
        uart_puts(msg);
    }
    

    boot.s

    .section ".text.boot"
    .global _start
    
    _start:
        mov x0, #0x90000
        mov sp, x0
        ldr x0, __bss_start
        ldr x1, __bss_end
        mov x2, #0
    clear_bss_loop:
        cmp x0, x1
        beq __start_kernel
        str x2, [x0], #8
        b clear_bss_loop
    
    __start_kernel:
        bl main
        b .
    

    link.ld

    ENTRY(_start)
    SECTIONS {
        . = 0x80000;
        .text : { *(.text) *(.text.boot) }
        .data : { *(.data) }
        .bss : {
            __bss_start = .;
            *(.bss)
            __bss_end = .;
        }
    }
    

    🧪 测试方法

    1. 使用 QEMU 启动你的内核镜像:

      qemu-system-aarch64 -M virt -cpu cortex-a53 -nographic -kernel kernel.elf
      
    2. 观察是否有输出 hello,world!


    📌 总结

    | 问题点 | 解决方案 | |--------|----------| | UART 地址无效 | 确认地址是否正确(如 0x10000000) | | 编译器优化 | 不要使用 -O2 或更高优化等级 | | 函数未执行 | 在 main() 中使用变量防止优化 | | 串口未初始化 | 添加寄存器配置(如 LSR、THR) |


    如果你能提供你使用的 QEMU 命令和具体硬件平台(如 virt, raspi3 等),我可以进一步帮助你调整地址和寄存器配置。

    评论

报告相同问题?

问题事件

  • 创建了问题 4月27日