2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

QEMUのRaspberry Pi2モデルでフレームバッファ描画

Last updated at Posted at 2018-05-17

QEMUのRaspberry Pi2モデルを使って、フレームバッファに描画してみました。

QEMUのRaspberry Pi2モデルは、VRAMのアドレスさえ分かれば、ベアメタルでも簡単に描画できます。

本当は、Raspberry Pi3モデルを使いたいのだけど、なぜか動かないんです。(もう一回試したところ、ブー部分のコードをAARCH64に書き換えたら、Raspberry Pi3モデルでもFrabeBufferは動いた。でもメールボックスは動かない。)

動作例

QEMUは2.12を使っています。

$ emu-system-arm -no-reboot -m 128 -M raspi3 -kernel kernel.elf 

QEMUを実行して表示される画像です。
スクリーンショット 2018-05-17 17.35.38.png
「30日でできる!OS自作入門」の例を描画しています。

説明

  • QEMU 2.12 の Raspberry Pi モデルは、フレームバッファの設定がソースコードに記述されています。
hw/display/bcm2835_fb.c
static Property bcm2835_fb_props[] = {
    DEFINE_PROP_UINT32("vcram-base", BCM2835FBState, vcram_base, 0),/*required*/
    DEFINE_PROP_UINT32("vcram-size", BCM2835FBState, vcram_size,
                       DEFAULT_VCRAM_SIZE),
    DEFINE_PROP_UINT32("xres", BCM2835FBState, xres, 640),
    DEFINE_PROP_UINT32("yres", BCM2835FBState, yres, 480),
    DEFINE_PROP_UINT32("bpp", BCM2835FBState, bpp, 16),
    DEFINE_PROP_UINT32("pixo", BCM2835FBState, pixo, 1), /* 1=RGB, 0=BGR */
    DEFINE_PROP_UINT32("alpha", BCM2835FBState, alpha, 2), /* alpha ignored */
    DEFINE_PROP_END_OF_LIST()
};
  • つまり、640x480の16bitモードがデフォルト設定です。(なので、QEMUではフレームバッファの設定をしなくても、この設定で動きます。)
  • この時のVRAMのアドレスは0x04100000固定です。(実際にはソースを見てもわからないので、mailbox経由でアドレスを取得しました。)
  • VRAMに16ビットのカラーコードを書き込めば、描画できます。
  • VRAMのアドレスを使って、画面の左上の1ピクセルを白にする場合の例です。
uint16_t *vram = (uint16_t *) 0x4100000;
vram = 0xffff;

動作例を描画したコードです。

void boxfill8(uint16_t *vram, int xsize, uint16_t c, int x0, int y0, int x1, int y1)
{
    int x, y;
    for (y = y0; y <= y1; y++) {
        for (x = x0; x <= x1; x++)
            vram[y * xsize + x] = c;
    }
    return;
}

#define COL8_000000 (((0x00>>3)<<11) + ((0x00>>3)<<6) + (0x00>>3))
#define COL8_008484 (((0x00>>3)<<11) + ((0x84>>3)<<6) + (0x84>>3))
#define COL8_848484 (((0x84>>3)<<11) + ((0x84>>3)<<6) + (0x84>>3))
#define COL8_C6C6C6 (((0xC6>>3)<<11) + ((0xC6>>3)<<6) + (0xC6>>3))
#define COL8_FFFFFF (((0xFF>>3)<<11) + ((0xFF>>3)<<6) + (0xFF>>3))

void kernel_main(void)
{
    uint16_t *vram = (uint16_t *) 0x4100000;
    int x = 640 ,y = 480;

    boxfill8(vram, x, COL8_008484,  0,     0,      x -  1, y - 29);
    boxfill8(vram, x, COL8_C6C6C6,  0,     y - 28, x -  1, y - 28);
    boxfill8(vram, x, COL8_FFFFFF,  0,     y - 27, x -  1, y - 27);
    boxfill8(vram, x, COL8_C6C6C6,  0,     y - 26, x -  1, y -  1);

    boxfill8(vram, x, COL8_FFFFFF,  3,     y - 24, 59,     y - 24);
    boxfill8(vram, x, COL8_FFFFFF,  2,     y - 24,  2,     y -  4);
    boxfill8(vram, x, COL8_848484,  3,     y -  4, 59,     y -  4);
    boxfill8(vram, x, COL8_848484, 59,     y - 23, 59,     y -  5);
    boxfill8(vram, x, COL8_000000,  2,     y -  3, 59,     y -  3);
    boxfill8(vram, x, COL8_000000, 60,     y - 24, 60,     y -  3);

    boxfill8(vram, x, COL8_848484, x - 47, y - 24, x -  4, y - 24);
    boxfill8(vram, x, COL8_848484, x - 47, y - 23, x - 47, y -  4);
    boxfill8(vram, x, COL8_FFFFFF, x - 47, y -  3, x -  4, y -  3);
    boxfill8(vram, x, COL8_FFFFFF, x -  3, y - 24, x -  3, y -  3);

    while (1)
        ;
}

VRAMアドレス取得

QEMU上でもVRAMのアドレスをメールボックス経由で取得できます。
0x4100000というアドレスは下記のコードをQEMU上で動かして取得しました。

メールボックスの使い方は、参考にしたページを見てください。

ただ実機とQEMUの動作が同じかどうかはよくわからないです。
実機(Raspberry Pi)では、メールボックスへのライトで渡すアドレスに、0x40000000を足しているのですが、QEMUだと、そのままのアドレスでないと動きませんでした。

#define MB_READ   0x3F00B880
#define MB_WRITE  0x3F00B8A0
#define MB_STATUS 0x2000B898

void mb_write(uint32_t cmd_addr, uint32_t channel)
{
    volatile uint32_t sta, cmd = 0;

    do {
        sta = *( (volatile uint32_t *) MB_STATUS);
    } while (sta & 0x80000000);

    cmd = cmd_addr | channel;
    *((volatile uint32_t *) MB_WRITE) = cmd;
    return;
}

void mb_read(uint32_t channel, uint32_t *resp_addr)
{
    volatile uint32_t mail, sta;

    do {
        do {
            sta = *( (volatile uint32_t *) MB_STATUS);
        } while (sta & 0x40000000);
        mail = *( (volatile uint32_t *) MB_READ);
    } while ((mail & 0x0000000F) != channel);

    *resp_addr =  mail & 0xFFFFFFF0;
    uart_puts("mb_read resp addr ");
    uart_hex_puts(*resp_addr);
    return;
}

static uint32_t pt[8192] __attribute__((aligned(16)));

uint16_t *fb_get_address(void)
{
    uint32_t i;
    uint32_t mail;
    uint32_t *mailp;

    pt[0] = 32;      // buffer size in bytes
    pt[1] = 0;       // request code: process request
    pt[2] = 0x40001; // tag: allocate frame buffer
    pt[3] = 4;       // length
    pt[4] = 0;
    pt[5] = 16;      // alignments in bytes
    pt[7] = 0;       // end tag

    uart_puts("mailbox cmd addr: ");
    uart_hex_puts( (uint32_t) pt);
    for(i=0; i<*((uint32_t *) pt)/4; i++) {
        uart_hex_puts( *((uint32_t *) (pt+i)));
    }

    mb_write((uint32_t) pt, 8);
    mb_read(8, &mail);

    uart_puts("mailbox resp addr: ");
    uart_hex_puts( (unsigned int) mail);
    mailp = (uint32_t *) mail;
    for(i=0; i<32/4; i++) {
        uart_hex_puts( * (mailp+i));
    }

    return (uint16_t *) *(mailp + 5);
}

void kernel_main(void)
{
    uint16_t *vram;
    uart_init();

    vram = fb_get_address();
...
}

上記を動作させた時のUART出力ログ

mailbox cmd addr: 0xB000
0x20
0x0
0x40001
0x4
0x0
0x10
0x0
0x0

mb_read resp addr 0xB000

mailbox resp addr: 0xB000
0x20
0x80000000
0x40001
0x4
0x80000008
0x4100000
0x96000
0x0

このログの、0x4100000がVRAMのアドレスです。

QEMUが対応しているメールボックスのコマンドは、QEMUの[bcm2835_property.c] (https://github.com/qemu/qemu/blob/master/hw/misc/bcm2835_property.c)に記述されています。

参考にしたページ

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?