QEMU의 Raspberry Pi2 모델에서 프레임 버퍼 그리기

프레임 버퍼에 QEMU의 Raspberry Pi2 모델을 사용하여 그립니다.
QEMU의 라즈베리 Pi2 모델은 VRAM의 주소만 알면 나체 금속도 간단하게 묘사할 수 있다.
사실 저는 라즈베리 파이 3 모델을 사용하고 싶은데 왜 움직이지 않는지 모르겠어요.(다시 한 번 시도해 봤는데 천 부분의 코드를 AARCH64로 바꿨어요. 라즈베리 Pi3 모델에서 Frabe Buffer도 움직였어요. 그런데 메일박스가 안 움직였어요.)

작업 예


QEMU는 2.12를 사용합니다.
$ emu-system-arm -no-reboot -m 128 -M raspi3 -kernel kernel.elf 
QEMU를 실행하면 표시되는 이미지입니다.

'30일이면 완성! OS 자체 제작 입문'의 예를 묘사했다.

설명

  • QEMU2.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으로 고정됩니다.(실제로 원본을 봐도 몰라서 메일박스를 통해 주소를 얻었다.)
  • 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 주소를 확인할 수 있습니다.
    QEMU에서 아래 코드를 이동하여 0x4100000 주소를 얻었습니다.
    메일박스의 사용 방법은 참고 페이지를 보십시오.
    다만 실제 기기와 QEMU의 동작이 같은지는 잘 모르겠다.
    실제 컴퓨터(Raspberry Pi)에서는 메일박스의 불빛을 통해 전달되는 주소에 0x40억 위안을 더했지만 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 에 설명되어 있습니다.

    참조 페이지

  • VC framebuffer on QEMU -M raspi2
  • boot-arm-raspi.s
  • 라즈베리 파이 나금속 프로그래밍 중인 메일박스 기능

  • RPiHaribotemailbox.c
  • Officital의githubwiki에 있음Mailbox property interface
  • 좋은 웹페이지 즐겨찾기