<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Posts on Donggeun Yoo</title>
    <link>https://donggeunyoo.github.io/posts/</link>
    <description>Recent content in Posts on Donggeun Yoo</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Tue, 29 Sep 2026 14:41:00 +0900</lastBuildDate>
    <atom:link href="https://donggeunyoo.github.io/posts/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>vnpu 개발기 #13 - 버퍼 객체 mmap과 크기 상한</title>
      <link>https://donggeunyoo.github.io/posts/vnpu-13/</link>
      <pubDate>Tue, 29 Sep 2026 14:41:00 +0900</pubDate>
      <guid>https://donggeunyoo.github.io/posts/vnpu-13/</guid>
      <description>&lt;p&gt;BO를 사용자 공간에 mmap해서 CPU로 직접 읽고 쓸 수 있게 했다.&lt;br&gt;
드라이버 코드는 한 줄도 없다. accel 장치 파일의 mmap은 이미 DRM 공통 함수(drm_gem_mmap)로 이어져 있고, BO는 shmem 헬퍼의 mmap을 쓴다.&lt;br&gt;
mmap에는 12편의 mmap 오프셋을 넘긴다. 커널은 이 오프셋으로 BO를 찾고, 이 fd에 권한이 있는지 본 뒤 매핑한다.&lt;/p&gt;
&lt;p&gt;새 BO는 0으로 차 있었고, 쓴 값은 munmap했다가 다시 매핑해도 그대로였다.&lt;br&gt;
BO보다 긴 매핑, BO 중간부터의 매핑, MAP_PRIVATE는 EINVAL이다. 오프셋 0과 닫은 BO의 오프셋도 찾을 BO가 없어서 EINVAL이다.&lt;br&gt;
다른 fd의 오프셋은 EACCES다. 오프셋 공간은 장치에 하나라 BO는 찾지만, 그 fd에는 권한이 없다.&lt;/p&gt;</description>
    </item>
    <item>
      <title>vnpu 개발기 #12 - 버퍼 객체 정보 읽기</title>
      <link>https://donggeunyoo.github.io/posts/vnpu-12/</link>
      <pubDate>Tue, 29 Sep 2026 13:09:00 +0900</pubDate>
      <guid>https://donggeunyoo.github.io/posts/vnpu-12/</guid>
      <description>&lt;p&gt;핸들을 주면 BO의 크기와 mmap 오프셋을 돌려주는 BO_INFO ioctl을 넣었다.&lt;br&gt;
11편에서는 1바이트로 만든 BO가 정말 4096바이트가 됐는지 볼 방법이 없었는데, 이제 크기를 읽어 확인할 수 있다.&lt;br&gt;
오프셋은 다음에 BO를 mmap할 때 쓴다.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drm_vnpu_bo_info&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;n&#34;&gt;__u32&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;handle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;n&#34;&gt;__u32&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pad&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;n&#34;&gt;__u64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;size&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;n&#34;&gt;__u64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mmap_offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;pad는 size를 8바이트 경계에 두려고 채운 칸이고, 0이 아니면 거절한다.&lt;br&gt;
ivpu는 BO의 flags와 장치 쪽 주소도 돌려주는데, vnpu에는 정의한 flags도 장치 쪽 주소도 아직 없어서 뺐다.&lt;/p&gt;
&lt;p&gt;핸들러는 이 fd의 핸들 표에서 BO를 찾아 크기와 오프셋을 채운다. 못 찾으면 ENOENT다.&lt;/p&gt;</description>
    </item>
    <item>
      <title>vnpu 개발기 #11 - 버퍼 객체 만들기</title>
      <link>https://donggeunyoo.github.io/posts/vnpu-11/</link>
      <pubDate>Tue, 29 Sep 2026 00:20:00 +0900</pubDate>
      <guid>https://donggeunyoo.github.io/posts/vnpu-11/</guid>
      <description>&lt;p&gt;사용자 프로그램이 장치에 넘길 메모리를 만들 수 있게 했다.&lt;br&gt;
이 메모리 덩어리를 버퍼 객체(BO, buffer object)라고 한다. ivpu에서도 작업을 제출할 때 필요한 BO들을 핸들 배열로 넘긴다.&lt;/p&gt;
&lt;p&gt;BO는 DRM의 메모리 관리자인 GEM(Graphics Execution Manager)이 관리한다.&lt;br&gt;
GEM은 BO 안에 뭐가 들었는지는 모르고, 사용자 공간에는 BO를 핸들이라는 번호로 보여 준다.&lt;br&gt;
핸들 표는 장치 파일을 open할 때마다 따로 생긴다.&lt;/p&gt;
&lt;p&gt;BO 뒤의 메모리는 shmem 헬퍼로 만든다.&lt;br&gt;
shmem은 커널 안의 공유 메모리 파일 시스템이고, 사용자 공간에는 tmpfs로 보인다. 헬퍼는 BO마다 그 크기의 shmem 파일을 하나 만들어 둔다.&lt;br&gt;
ivpu의 BO도 이 헬퍼 위에 있다.&lt;/p&gt;</description>
    </item>
    <item>
      <title>vnpu 개발기 #10 - 장치 리셋</title>
      <link>https://donggeunyoo.github.io/posts/vnpu-10/</link>
      <pubDate>Mon, 28 Sep 2026 23:14:00 +0900</pubDate>
      <guid>https://donggeunyoo.github.io/posts/vnpu-10/</guid>
      <description>&lt;p&gt;장치가 리셋될 때 IRQ_STATUS를 0으로 되돌리게 했다.&lt;br&gt;
9편에서 만든 이 레지스터는 게스트를 재부팅해도 값이 그대로 남아 있었다.&lt;/p&gt;
&lt;p&gt;make run은 QEMU를 -no-reboot로 띄우는데, 이러면 게스트가 재부팅할 때 QEMU가 그냥 꺼진다.&lt;br&gt;
그래서 이 옵션만 빼고 QEMU를 직접 띄웠다.&lt;br&gt;
드라이버를 올리면 핸들러가 비트를 지우니까, 드라이버 없이 RAISE에 0x5를 쓰고 재부팅했다.&lt;br&gt;
a는 5편과 같이 BAR 0의 시작 주소다.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;~ # devmem $((a+8)) 32 0x5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;~ # devmem $((a+4)) 32
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;0x00000005
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;~ # reboot -f
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[   14.562993] reboot: Restarting system
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[   14.563494] reboot: machine restart
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;다시 부팅한 뒤 STATUS를 읽었다.&lt;/p&gt;</description>
    </item>
    <item>
      <title>vnpu 개발기 #9 - 인터럽트</title>
      <link>https://donggeunyoo.github.io/posts/vnpu-09/</link>
      <pubDate>Mon, 28 Sep 2026 22:54:00 +0900</pubDate>
      <guid>https://donggeunyoo.github.io/posts/vnpu-09/</guid>
      <description>&lt;p&gt;장치가 드라이버에게 인터럽트를 보낼 수 있게 하는 작업을 시작했다.&lt;br&gt;
앞으로 IPC나 job 완료 알림은 전부 인터럽트로 온다.&lt;/p&gt;
&lt;p&gt;인터럽트는 MSI로 보낸다.&lt;br&gt;
MSI는 인터럽트 전용 선 대신, 장치가 정해진 주소에 정해진 값을 메모리 쓰기하는 방식이다. QEMU에서도 MSI를 보내는 코드는 결국 메모리 쓰기 한 번이다.&lt;br&gt;
어떤 주소에 어떤 값을 쓸지는 운영체제가 정해서, 장치의 설정 공간에 있는 MSI capability에 적어 준다.&lt;/p&gt;
&lt;p&gt;그래서 첫 단계는 설정 공간에 MSI capability를 넣는 것이다.&lt;br&gt;
QEMU에서는 realize에서 msi_init 한 번이면 되고, 장치가 없어질 때는 msi_uninit으로 뺀다.&lt;br&gt;
벡터는 하나, 64비트 주소로 보낼 수 있게 했다.&lt;/p&gt;</description>
    </item>
    <item>
      <title>vnpu 개발기 #8 - 첫 ioctl, GET_PARAM</title>
      <link>https://donggeunyoo.github.io/posts/vnpu-08/</link>
      <pubDate>Mon, 28 Sep 2026 22:11:00 +0900</pubDate>
      <guid>https://donggeunyoo.github.io/posts/vnpu-08/</guid>
      <description>&lt;p&gt;사용자 공간 프로그램이 ioctl로 장치 정보를 물어볼 수 있게 했다.&lt;br&gt;
첫 항목은 PCI 디바이스 ID 하나다. ivpu도 첫 ioctl이 GET_PARAM이고, 첫 항목이 디바이스 ID다.&lt;/p&gt;
&lt;p&gt;ioctl 번호와 주고받는 구조체는 드라이버와 사용자 공간이 똑같이 알아야 해서, 둘이 같이 include하는 uapi 헤더에 뒀다.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;drm_vnpu_param&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;n&#34;&gt;__u32&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;param&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;n&#34;&gt;__u32&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;pad&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	&lt;span class=&#34;n&#34;&gt;__u64&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;value가 64비트라 구조체 크기를 64비트 배수로 맞추려고 pad를 명시했고, pad가 0이 아니면 드라이버가 거절한다.&lt;br&gt;
커널 문서(Documentation/process/botching-up-ioctls.rst)에 있는 규칙이다.&lt;/p&gt;
&lt;p&gt;드라이버 쪽은 ioctl 표에 함수 하나를 등록하는 게 전부다.&lt;br&gt;
DRM의 drm_ioctl이 번호로 표를 찾고, 구조체를 커널로 복사해 드라이버 함수를 부른 뒤, 결과를 다시 사용자 공간으로 복사해 준다.&lt;/p&gt;</description>
    </item>
    <item>
      <title>vnpu 개발기 #7 - accel 장치로 등록하기</title>
      <link>https://donggeunyoo.github.io/posts/vnpu-07/</link>
      <pubDate>Mon, 28 Sep 2026 21:40:00 +0900</pubDate>
      <guid>https://donggeunyoo.github.io/posts/vnpu-07/</guid>
      <description>&lt;p&gt;드라이버를 accel 장치로 등록해서 /dev/accel/accel0을 만들었다.&lt;br&gt;
사용자 공간 프로그램이 NPU에 말을 걸 통로다. 앞으로 버퍼 할당이나 job 제출은 이 파일의 ioctl로 들어오게 된다.&lt;/p&gt;
&lt;p&gt;NPU 같은 계산 가속기는 리눅스 accel 서브시스템에 붙는다.&lt;br&gt;
뼈대는 GPU용 DRM 코어를 그대로 쓰고, 장치 파일만 /dev/dri가 아니라 /dev/accel 아래 생긴다.&lt;br&gt;
커널 문서(Documentation/accel/introduction.rst)에 따르면, 그래픽 쪽 사용자 공간 소프트웨어가 가속기를 GPU로 착각하지 않게 하려는 것이다.&lt;/p&gt;
&lt;p&gt;드라이버 쪽에서 할 일은 두 가지다.&lt;br&gt;
drm_driver에 DRIVER_COMPUTE_ACCEL 플래그를 켜고, 파일 연산은 DEFINE_DRM_ACCEL_FOPS로 만든다.&lt;br&gt;
probe에서는 ID 확인이 끝난 뒤 devm_drm_dev_alloc으로 DRM 장치를 할당하고, drm_dev_register로 등록한다. 여기서 장치 파일이 생긴다.&lt;/p&gt;</description>
    </item>
    <item>
      <title>vnpu 개발기 #6 - 드라이버에서 ID 읽기</title>
      <link>https://donggeunyoo.github.io/posts/vnpu-06/</link>
      <pubDate>Mon, 28 Sep 2026 21:14:00 +0900</pubDate>
      <guid>https://donggeunyoo.github.io/posts/vnpu-06/</guid>
      <description>&lt;p&gt;드라이버가 probe에서 ID 레지스터를 읽게 했다.&lt;br&gt;
ID가 0x564e5055가 아니면 이 장치를 잡지 않는다.&lt;/p&gt;
&lt;p&gt;레지스터를 읽기 전에 두 가지가 필요하다.&lt;br&gt;
pcim_enable_device로 장치를 켜고, pcim_iomap_region으로 BAR 0을 커널 주소에 매핑해야 드라이버가 readl로 읽을 수 있다.&lt;/p&gt;
&lt;p&gt;pcim_iomap_region은 매핑과 함께 BAR 0 범위를 &amp;ldquo;vnpu&amp;rdquo; 이름으로 확보한다.&lt;br&gt;
둘 다 이름의 m이 managed라는 뜻이라, 드라이버가 떨어질 때 커널이 알아서 되돌린다.&lt;br&gt;
unbind했다가 다시 bind해서 정말 풀리는지 봤다.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[   13.877076] vnpu 0000:00:04.0: ID 0x564e5055
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;~ # grep vnpu /proc/iomem
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    febf1000-febf1fff : vnpu
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;~ # echo 0000:00:04.0 &amp;gt; /sys/bus/pci/drivers/vnpu/unbind
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;~ # grep -c vnpu /proc/iomem
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;~ # echo 0000:00:04.0 &amp;gt; /sys/bus/pci/drivers/vnpu/bind
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[   13.893207] vnpu 0000:00:04.0: ID 0x564e5055
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;풀어 주는 코드를 따로 안 썼는데, unbind하자 범위가 풀렸고 다시 bind해도 문제없이 잡혔다.&lt;/p&gt;</description>
    </item>
    <item>
      <title>vnpu 개발기 #5 - 첫 레지스터</title>
      <link>https://donggeunyoo.github.io/posts/vnpu-05/</link>
      <pubDate>Mon, 28 Sep 2026 21:01:00 +0900</pubDate>
      <guid>https://donggeunyoo.github.io/posts/vnpu-05/</guid>
      <description>&lt;p&gt;BAR 0 창에 첫 레지스터를 넣었다.&lt;br&gt;
오프셋 0x00을 읽으면 0x564e5055가 나오는 ID 레지스터다.&lt;/p&gt;
&lt;p&gt;드라이버가 장치를 잡고 가장 먼저 할 일은 이 창 너머에 정말 vnpu가 있는지 확인하는 것이다.&lt;br&gt;
창 연결이 어딘가 잘못돼 있으면 이 값이 안 나오니까, 이 값이 나오면 BAR 배치부터 매핑까지 제대로 됐다는 뜻이 된다.&lt;br&gt;
값은 16진수로 56 4e 50 55, ASCII로 &amp;ldquo;VNPU&amp;quot;다.&lt;/p&gt;
&lt;p&gt;게스트가 창을 읽으면 KVM이 그 접근을 QEMU로 넘기고, QEMU는 그 창의 MemoryRegionOps에 등록된 read 콜백을 부른다.&lt;br&gt;
콜백은 창 시작 기준 오프셋을 받아서, 0x00이면 ID를 돌려준다.&lt;br&gt;
레지스터는 전부 32비트라서 4바이트 접근만 받게 했다.&lt;/p&gt;</description>
    </item>
    <item>
      <title>vnpu 개발기 #4 - BAR 0 달기</title>
      <link>https://donggeunyoo.github.io/posts/vnpu-04/</link>
      <pubDate>Mon, 28 Sep 2026 19:12:00 +0900</pubDate>
      <guid>https://donggeunyoo.github.io/posts/vnpu-04/</guid>
      <description>&lt;p&gt;장치에 드라이버가 읽고 쓸 메모리 창을 달았다.&lt;br&gt;
지금까지 장치는 PCI 설정 공간, 그러니까 자기소개만 있었다.&lt;/p&gt;
&lt;p&gt;PCI 장치는 설정 공간의 BAR(Base Address Register)로 &amp;ldquo;이만한 크기의 메모리 창이 필요하다&amp;quot;고 알린다.&lt;br&gt;
부팅할 때 이 창이 물리 주소 공간 어딘가에 배치되고, 그 뒤로 CPU가 그 주소를 읽고 쓰면 RAM이 아니라 장치로 간다.&lt;br&gt;
이게 MMIO이고, 레지스터 접근은 전부 이 창을 거친다.&lt;/p&gt;
&lt;p&gt;QEMU에서는 이 창을 MemoryRegion으로 만들고 pci_register_bar로 BAR에 연결한다.&lt;br&gt;
크기는 4 KiB로 잡았다. CPU가 메모리를 페이지 단위로 매핑하니 이보다 작게 잡을 이유가 없었다.&lt;br&gt;
읽고 쓸 때 부를 콜백은 아직 안 넣었다. QEMU는 콜백이 없는 창을 어떤 접근도 받지 않는 영역으로 처리한다.&lt;/p&gt;</description>
    </item>
    <item>
      <title>vnpu 개발기 #3 - 빈 NPU 디바이스를 꽂고 드라이버로 잡기</title>
      <link>https://donggeunyoo.github.io/posts/vnpu-03/</link>
      <pubDate>Mon, 28 Sep 2026 18:55:00 +0900</pubDate>
      <guid>https://donggeunyoo.github.io/posts/vnpu-03/</guid>
      <description>&lt;p&gt;드라이버보다 디바이스를 먼저 만들었다.&lt;br&gt;
드라이버가 probe할 대상이 없으면, 드라이버를 짜도 돌려 볼 방법이 없어서다.&lt;/p&gt;
&lt;p&gt;QEMU에서 PCI 디바이스를 만드는 건 PCI 설정 공간을 채우는 일부터 시작한다.&lt;br&gt;
리눅스는 부팅할 때 PCI 버스를 훑으면서 장치마다 벤더 ID, 디바이스 ID, 클래스 코드를 읽고, 그 값에 맞는 드라이버를 찾아 붙인다.&lt;/p&gt;
&lt;p&gt;벤더 ID는 QEMU가 자체 가상 장치에 쓰는 0x1234를 썼다.&lt;br&gt;
디바이스 ID는 0x4e50으로 직접 골랐다. 0x1234 안에서 비어 있는 번호이고, ASCII로 읽으면 &amp;ldquo;NP&amp;quot;다.&lt;br&gt;
클래스 코드는 처리 가속기를 뜻하는 0x1200이다.&lt;br&gt;
리눅스에는 이 값의 이름(PCI_CLASS_ACCELERATOR_PROCESSING)이 있는데 QEMU에는 없어서, 같은 이름으로 하나 추가했다.&lt;/p&gt;</description>
    </item>
    <item>
      <title>vnpu 개발기 #2 - 개발 환경 세팅</title>
      <link>https://donggeunyoo.github.io/posts/vnpu-02/</link>
      <pubDate>Mon, 28 Sep 2026 18:22:00 +0900</pubDate>
      <guid>https://donggeunyoo.github.io/posts/vnpu-02/</guid>
      <description>&lt;p&gt;NPU 디바이스를 어디에 만들지부터 정해야 했다.&lt;br&gt;
앞으로 만들 NPU 드라이버가 잡을 NPU는 QEMU에 없으므로, 드라이버를 돌리려면 디바이스 모델부터 직접 만들어야 한다.&lt;/p&gt;
&lt;p&gt;방법은 두 가지였다.&lt;br&gt;
하나는 QEMU 소스를 받아서 hw/misc 아래에 PCI 디바이스로 직접 넣는 것이고, 다른 하나는 libvfio-user로 디바이스를 별도 프로세스로 만들고 QEMU는 vfio-user로 붙이는 것이다.&lt;/p&gt;
&lt;p&gt;QEMU 포크로 정했다.&lt;br&gt;
QEMU에 디바이스가 들어가는 원래 방식 그대로라서다.&lt;br&gt;
대신 QEMU 전체를 소스에서 빌드해야 한다.&lt;br&gt;
기준은 QEMU 최신 안정판인 11.1.1로 잡았다.&lt;/p&gt;
&lt;p&gt;VM에서 돌릴 커널도 최신 릴리스인 7.2로 새로 빌드했다.&lt;br&gt;
이전 프로젝트에서 쓰던 커널은 accel 서브시스템(CONFIG_DRM_ACCEL)이 꺼져 있어서 그대로 쓸 수 없었다.&lt;/p&gt;</description>
    </item>
    <item>
      <title>vnpu 개발기 #1 - NPU 디바이스와 드라이버를 밑바닥부터 만들기</title>
      <link>https://donggeunyoo.github.io/posts/vnpu-01/</link>
      <pubDate>Mon, 28 Sep 2026 17:42:00 +0900</pubDate>
      <guid>https://donggeunyoo.github.io/posts/vnpu-01/</guid>
      <description>&lt;p&gt;이 프로젝트는 NPU 디바이스와 그 리눅스 드라이버를 밑바닥부터 직접 만드는 것이다.&lt;br&gt;
디바이스는 QEMU 안에 에뮬레이션 모델로 만들고, 드라이버는 그 디바이스를 잡는 accel 드라이버로 만든다.&lt;/p&gt;
&lt;p&gt;레퍼런스로는 인텔 NPU 드라이버인 drivers/accel/ivpu를 삼았다. 헤더 포함 소스 코드가 1만 5천 줄 정도 되는데, 이 드라이버가 할 수 있는 건 전부 할 수 있게 만드는 게 목표다.&lt;br&gt;
ivpu는 NPU 드라이버가 어떤 일들을 처리해야 하는지 파악하는 용도로만 보고, 코드는 전부 새로 짠다.&lt;br&gt;
보면서 더 낫게 만들 수 있겠다 싶은 부분은 바꿔 볼 거고, 결국 ivpu랑 같은 방식을 택하게 되는 곳도 당연히 있을 수 있다.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
