An Exhibition of A Hunger Artist 1nzag (graypanda) Security researcher

Emulate Mali on QEMU

Intro

mali 나 adreno 같은 경우는 기존에 에뮬레이팅을 할 수가 없어서 실기기에서 직접 커널 디버깅을 하는 방법을 찾고 있었다.

근데 그 중에 다음과 같은 글에서 Mali 는 에뮬레이팅을 할 수 있다는 정보를 얻었다.

따라서 나도 x86 환경에서 Mali GPU 드라이버를 에뮬레이팅 해보기로 했다.

Download Mali source

mali 는 오픈소스 형태로 드라이버 소스 코드를 배포한다. 나는 여기에서 BX304L01B-SW-99002-r38p0-01eac0 버전의 Mali GPU driver 소스코드를 가져왔다.

다운받은 파일의 압축을 풀어보면 소스의 구조는 다음과 같다.

BX304L01B-SW-99002-r38p0-01eac0/
└── product
    └── kernel
        ├── build.bp
        ├── Documentation
        │   ├── ABI
        │   │   └── testing
        │   ├── devicetree
        │   │   └── bindings
        │   └── dma-buf-test-exporter.txt
        ├── drivers
        │   ├── base
        │   │   └── arm
        │   └── gpu
        │       └── arm
        ├── include
        │   ├── linux
        │   │   ├── dma-buf-test-exporter.h
        │   │   ├── memory_group_manager.h
        │   │   ├── priority_control_manager.h
        │   │   ├── protected_memory_allocator.h
        │   │   ├── protected_mode_switcher.h
        │   │   └── version_compat_defs.h
        │   └── uapi
        │       └── gpu
        ├── license.txt
        └── Mconfig

Download Kernel Source

그리고 Mali 를 구동시킬 리눅스 커널이 필요하다.

나는 기존에 잘 사용하던 5.13.18 버전의 linux kernel 소스를 가져왔다.

Import driver code to kernel

커널 코드에 드라이버를 합칠 부분은 총 3곳이다.

  • kernel driver source
  • Kconfig
  • Makefile

차례대로 코드를 합쳐보자

1. copy kernel driver source

먼저 커널 소스의 drivers/gpu에 드라이버 소스 products/kernel/drivers/gpu/arm 디렉토리를 복사한다.

cp -r mali/source/directory/products/kernel/drivers/gpu/arm /kernel/source/directory/drivers/gpu

그 후 커널 소스의 include/linux 에 드라이버 소스 products/kernel/include/linux 의 소스를 모두 복사해준다.

cp mali/source/directory/products/kernel/include/linux/* /kernel/source/directory/include/linux

마지막으로 커널 소스의 include/uapi/gpu 디렉토리에 products/kernel/include/uapi/gpu/arm 디렉토리를 복사한다.

cp -r mali/source/directory/products/kernel/include/uapi/gpu/arm /kernel/source/directory/include/uapi/gpu

2. Modify drivers Kconfig

drivers/Kconfig 파일에 다음과 같은 줄을 추가해서 drivers 설정에 mali 컴파일 설정을 추가해준다.

source "drivers/gpu/arm/midgard/Kconfig"

3. Modify gpu driver Makefile

drivers/gpu/Makefile 에 다음과 같은 줄을 추가해 줘 빌드 목록에 추가해준다.

obj-y += arm/midgard/

Build config

이후 make menuconfig를 통해 커널 빌드 설정을 하면 Device Drivers 설정에 Mali Midgard series support 옵션이 생긴다.

나는 그 중에 에뮬레이팅을 위해 Mali GPU 가 없는 환경에서 구동가능하게 해주는 설정인 CONFIG_MALI_NO_MALI를 설정해 주었다.

menuconfig 에서 설정해주는 방법은 mali 빌드 설정에서 Enable Expert Settings --> Enable No Mali 옵션을 활성화 해주면 된다.

Fix header source

이 상태로 커널을 빌드하면 asm/arch_timer.h 헤더파일이 없다는 빌드 에러가 뜬다. 해당 헤더파일은 소스 안에서 쓰이지 않으니 그냥 주석처리 해주면 된다.

drivers/gpu/arm/midgard/platform/devicetree/mali_kbase_config_platform.c 파일의 코드를 다음과 같이 주석처리 한다.

#include <mali_kbase.h>
#include <mali_kbase_defs.h>
#include <mali_kbase_config.h>
#include "mali_kbase_config_platform.h"
#include <device/mali_kbase_device.h>
#include <mali_kbase_hwaccess_time.h>
#include <gpu/mali_kbase_gpu_regmap.h>

#include <linux/kthread.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/gcd.h>
//#include <asm/arch_timer.h>

struct kbase_platform_funcs_conf platform_funcs = {
	.platform_init_func = NULL,
	.platform_term_func = NULL,
	.platform_late_init_func = NULL,
	.platform_late_term_func = NULL,
};

Fix kbase_driver_init function

이렇게 구성하고 커널을 빌드하면 커널이 빌드된다. 커널을 구동한 후 심볼도 확인하면 mali gpu 드라이버 심볼도 확인가능하다.

~ # cat /proc/kallsyms | grep kbase_driver_init
ffffffff82d0e873 t kbase_driver_init
ffffffff82f065f0 d __initcall__kmod_mali_kbase__788_5707_kbase_driver_init6

하지만 /dev 디렉토리에 mali0 라는 이름을 가졌어야 할 디바이스 파일이 생성되지 않는 것을 볼 수 있다.

디버깅과 자료 조사를 통해 알아냈는데, 이는 내가 가지고 있는 드라이버가 platform_driver 만 추가해줬을 뿐, platform_device 는 추가해 주지 않기 때문이라는 것을 알았다.

따라서 platform_device 를 추가해주는 소스를 추가해서 다시 빌드해줬다.

  • drivers/gpu/arm/midgard/mali_kbase_core_linux.c ```c

static struct platform_device kbase_platform_device_cus = { // add platform_device define .name = kbase_drv_name, };

/*

  • The driver will not provide a shortcut to create the Mali platform device
  • anymore when using Device Tree. */ #if IS_ENABLED(CONFIG_OF) module_platform_driver(kbase_platform_driver); #else

static int __init kbase_driver_init(void) { int ret;

ret = kbase_platform_register();
if (ret)
	return ret;

ret = platform_driver_register(&kbase_platform_driver);

platform_device_register(&kbase_platform_device_cus); //add platform_device

if (ret)
	kbase_platform_unregister();

return ret; } ... #endif ```

그럼 다음과 같이 mali 디바이스에 접근할 수 있는 것을 볼 수 있다.

~ # ls -al /dev | grep mali
crw-rw----    1 0        0          10, 125 Feb  5 09:42 mali0

Conclusion

이제 mali 를 x86_64 환경에서 에뮬레이팅 할 수 있게 되었다! 물론 mali 의 모든 기능을 에뮬레이팅 시키는 것에는 무리가 있겠지만, 지금까지 나온 원데이들을 편하게 디버깅 하면서 실험해 볼 수 있을것이다.

References