Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
6
Help us understand the problem. What is going on with this article?
@eggman

QEMUでRaspberry Pi 3のUARTをベアメタルで動かす

More than 1 year has passed since last update.

QEMUのシステムエミュレーションを使ってRaspberry Pi 3のUARTをベアメタル動作させてみました。

Ubuntu 18.04で試してました。

準備

Raspberry Pi 3のシステムエミュレーションはQEMUが新しくないと対応していないので、QEMU 2.12をコンパイルして使いました。

% sudo apt build-dep qemu
% sudo apt install gcc-aarch64-linux-gnu

$ git clone git://git.qemu.org/qemu.git
$ cd qemu
$ git checkout -b v2.12.0-release refs/tags/v2.12.0
$ git submodule update --init

$ ./configure --prefix=`pwd`/build --target-list=arm-softmmu,aarch64-softmmu --audio-drv-list=alsa
$ make
$ make install

動作例

% cd src
% make
aarch64-linux-gnu-gcc -mcpu=cortex-a53 -fpic -ffreestanding -c boot.S -o boot.o
aarch64-linux-gnu-gcc -mcpu=cortex-a53 -fpic -ffreestanding -std=gnu99 -O2 -Wall -Wextra -c kernel.c -o kernel.o
aarch64-linux-gnu-gcc -T linker.ld -o kernel.elf -ffreestanding -O2 -nostdlib boot.o kernel.o

% file kernel.elf 
kernel.elf: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked,
interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=cbfa706293919f9c941e3d63c748e339643c4cc9, not stripped

% cd ..
% qemu/build/bin/qemu-system-aarch64 -m 128 -M raspi3  -nographic -kernel src/kernel.elf
Hello, kernel World!
Hello, kernel World!
Hello, kernel World!
Hello, kernel World!
QEMU: Terminated

ソースコードでは1回しか出力してないのに、なぜか4回出力されます。なんでだろうと思ったけど、4コアで実行されているから4回出力されているっぽいです。

QEMUを終了するときは、Ctrl-a xを入力します。

QEMUのraspi3は、PL011のUARTがデフォルトで使われれていました。 miniuartを標準入出力で使いたい場合は-serial を2回指示すると、使えました。1個目でPL011のUARTを無視し、2個目でminiuartを標準入出力に設定します。

qemu-system-aarch64 -m 128 -M raspi3 -nographic -kernel kernel.elf -serial null -serial mon:stdio

ソースコード

ブート

boot.S

// To keep this in the first portion of the binary.
.section ".text.boot"

// Make _start global.
.globl _start

// Entry point for the kernel.
// r15 -> should begin execution at 0x0080000.
// r0 -> 0x00000000
// r1 -> 0x00000C42
// r2 -> 0x00000100 - start of ATAGS
// preserve these registers as argument for kernel_main
_start:
    // Setup the stack.
    mov sp, #0x0080000

    // Call kernel_main
    bl kernel_main

    b .

UART処理

kernel.c

#include <stdint.h>

// Memory-Mapped I/O output
static inline void mmio_write(uint32_t reg, uint32_t data)
{
    *(volatile uint32_t*)reg = data;
}

// Memory-Mapped I/O input
static inline uint32_t mmio_read(uint32_t reg)
{
    return *(volatile uint32_t*)reg;
}

enum
{
    // The base address for UART.
    UART0_BASE = 0x3F201000, // for raspi2 & 3, 0x20201000 for raspi1

    // The offsets for reach register for the UART.
    UART0_DR     = (UART0_BASE + 0x00),
    UART0_FR     = (UART0_BASE + 0x18),
};

void uart_putc(unsigned char c)
{
    // Wait for UART to become ready to transmit.
    while ( mmio_read(UART0_FR) & (1 << 5) ) { }
    mmio_write(UART0_DR, c);
}

unsigned char uart_getc()
{
    // Wait for UART to have received something.
    while ( mmio_read(UART0_FR) & (1 << 4) ) { }
    return mmio_read(UART0_DR);
}

void uart_puts(const char* str)
{
    int i;
    for (i = 0; str[i] != '\0'; i ++)
        uart_putc((unsigned char)str[i]);
}

#if defined(__cplusplus)
extern "C" /* Use C linkage for kernel_main. */
#endif
void kernel_main(uint32_t r0, uint32_t r1, uint32_t atags)
{
    // Declare as unused
    (void) r0;
    (void) r1;
    (void) atags;

    uart_puts("Hello, kernel World!\n");

    while (1)
        uart_putc(uart_getc());
}

UARTは初期化しないでも動いたので初期化処理は省略しました。

リンカスクリプト

linker.ld

ENTRY(_start)

SECTIONS
{
    /* Starts at LOADER_ADDR. */
    . = 0x80000;
    __start = .;
    __text_start = .;
    .text :
    {
        KEEP(*(.text.boot))
        *(.text)
    }
    . = ALIGN(4096); /* align to page size */
    __text_end = .;

    __rodata_start = .;
    .rodata :
    {
        *(.rodata)
    }
    . = ALIGN(4096); /* align to page size */
    __rodata_end = .;

    __data_start = .;
    .data :
    {
        *(.data)
    }
    . = ALIGN(4096); /* align to page size */
    __data_end = .;

    __bss_start = .;
    .bss :
    {
        bss = .;
        *(.bss)
    }
    . = ALIGN(4096); /* align to page size */
    __bss_end = .;
    __end = .;
}

Makefile

Makefile

CC = aarch64-linux-gnu-gcc
CFLAGS =  -mcpu=cortex-a53 -fpic -ffreestanding -std=gnu99 -O2 -Wall -Wextra
ASM_FLAGS = -mcpu=cortex-a53 -fpic -ffreestanding
OBJ = boot.o kernel.o

kernel.elf: ${OBJ}
    ${CC} -T linker.ld -o $@ -ffreestanding -O2 -nostdlib ${OBJ}

boot.o: boot.S
    ${CC} ${ASM_FLAGS} -c $< -o $@

kernel.o: kernel.c
    ${CC} ${CFLAGS} -c $< -o $@

clean:
    rm -f *.o *.elf

.PHONY: clean

参考にしたページ

6
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
eggman

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
6
Help us understand the problem. What is going on with this article?