<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.5">Jekyll</generator><link href="https://www.linhao.me/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.linhao.me/" rel="alternate" type="text/html" /><updated>2024-07-04T06:59:08-07:00</updated><id>https://www.linhao.me/feed.xml</id><title type="html">Hao Lin’s Home Page</title><subtitle>A PhD student at Tsinghua University, who is interested in mobile operating systems and virtualization systems.</subtitle><author><name>Hao Lin</name><email>linhaomails@gmail.com</email></author><entry><title type="html">Building A Sideload Shell Program in Android</title><link href="https://www.linhao.me/posts/android-shell" rel="alternate" type="text/html" title="Building A Sideload Shell Program in Android" /><published>2022-10-26T00:00:00-07:00</published><updated>2022-10-26T00:00:00-07:00</updated><id>https://www.linhao.me/posts/Building-A-Shell-Android</id><content type="html" xml:base="https://www.linhao.me/posts/android-shell"><![CDATA[<p>A normal Android app almost always comes with graphics interfaces (except for services) and limited privileges.
In this post I’ll show how to write a program (in Java or C/C++) that runs in headless mode and possesses system-level privileges,
which can be used to perform various tasks in practice such as system service monitoring, debugging and performance profiling.</p>

<p>Such a program can be side-loaded into the system at run time (which requires the shell privilege of <code class="language-plaintext highlighter-rouge">adb</code> but does not require root), 
and executed as a daemon process in the background, 
thus providing immense flexibility if you wish to access certain hidden system service interfaces.
For example, Android Studio uses a similar shell program to perform its debugging and CPU/memory monitoring in practice.</p>

<h2 id="building-a-java-shell">Building a Java Shell</h2>

<h3 id="writing-makefile">Writing Makefile</h3>
<p>A normal Java shell program is easy to build in Android.
All you need to do is write a simple <code class="language-plaintext highlighter-rouge">Main.java</code> file, and provide a <code class="language-plaintext highlighter-rouge">main</code> function there.
Everything is exactly the same as you would do in building a Java program in PC/server platforms.
To make the Java program, you’ll need an <code class="language-plaintext highlighter-rouge">Android.mk</code> (or <code class="language-plaintext highlighter-rouge">Android.bp</code> for higher Android versions), which should look like this:</p>
<div class="language-make highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># For Android.bp you can always use the androidmk tool provided by AOSP to convert the Android.mk file into bp file
</span><span class="nv">LOCAL_PATH</span><span class="o">:=</span> <span class="nf">$(</span><span class="nb">call</span> my-dir<span class="nf">)</span>

<span class="k">include</span><span class="sx"> $(CLEAR_VARS)</span>
<span class="nv">LOCAL_SRC_FILES</span> <span class="o">:=</span> <span class="nf">$(</span><span class="nb">call</span> all-subdir-java-files<span class="nf">)</span>
<span class="nv">LOCAL_MODULE</span> <span class="o">:=</span> Mainlib
<span class="nv">LOCAL_MODULE_STEM</span> <span class="o">:=</span> Main
<span class="k">include</span><span class="sx"> $(BUILD_JAVA_LIBRARY)</span>
</code></pre></div></div>

<h3 id="creating-android-context">Creating Android Context</h3>

<p>In many cases, your Java program may need to interface with system services.
To do this, your program should be more than a traditional Java program but possess an Android context.
However, normally such a context is only accessible to an Android app.
What you need to do is thus disguising your program as an app process (without loosing higher privileges).</p>

<p>To this end, you should first prepare a main looper upon start and acquire the system context.
Note that you should also ensure that the main thread is still running when you use this context to acquire system services.
The code should look like this.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
    <span class="c1">// Prepare the main looper</span>
    <span class="nc">Looper</span><span class="o">.</span><span class="na">prepareMainLooper</span><span class="o">();</span>
    <span class="nc">Context</span> <span class="n">systemContext</span> <span class="o">=</span> <span class="nc">ActivityThread</span><span class="o">.</span><span class="na">systemMain</span><span class="o">().</span><span class="na">getSystemContext</span><span class="o">();</span>
    <span class="nc">PackageManager</span> <span class="n">packageManager</span> <span class="o">=</span> <span class="n">systemContext</span><span class="o">.</span><span class="na">getPackageManager</span><span class="o">();</span>

    <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"Hello World"</span><span class="o">);</span>

    <span class="c1">// This ensures that the main thread keeps running</span>
    <span class="c1">// You can also execute your logic in a separate thread </span>
    <span class="c1">// and wait for the thread to finish here, </span>
    <span class="c1">// so that you don't need to call the loop() func</span>
    <span class="nc">Looper</span><span class="o">.</span><span class="na">loop</span><span class="o">();</span>
<span class="o">}</span>

</code></pre></div></div>

<h3 id="building">Building</h3>
<p>To build the Java program, you’ll need the compile utilities in Android source.
Thus, it’s necessary that you build the program within AOSP’s code base.
To this end, you’ll need first to download and build AOSP’s source code (see the instructions <a href="https://source.android.com/setup/build/downloading">here</a>).
And put your Java source and <code class="language-plaintext highlighter-rouge">Android.mk</code> in the framework directory.
The recommended directory is in <code class="language-plaintext highlighter-rouge">frameworks/base/cmds</code> where several built-in shell programs reside.
Make your own sub-directory there and put everything in it.
And then you can build the program by <code class="language-plaintext highlighter-rouge">mmm frameworks/base/cmds/XXX</code>, where <code class="language-plaintext highlighter-rouge">XXX</code> is your sub-directory for holding your source and makefile.</p>

<h3 id="executing-the-java-program">Executing the Java Program</h3>

<p>To run the Java program you just build,
you should write a shell program like this.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">base</span><span class="o">=</span>/data/local/tmp

<span class="nb">mkdir</span> /data/local/tmp/dalvik-cache
<span class="nb">trap</span> <span class="s2">""</span> HUP
<span class="c"># com.example.hello should be replaced with your actual Java class</span>
<span class="nv">ANDROID_DATA</span><span class="o">=</span><span class="nv">$base</span> app_process <span class="nt">-Djava</span>.class.path<span class="o">=</span><span class="nv">$base</span>/classes.dex <span class="nv">$base</span> com.example.hello <span class="s2">"</span><span class="nv">$@</span><span class="s2">"</span>
</code></pre></div></div>
<p>Now push the shell script to your phone’s <code class="language-plaintext highlighter-rouge">/data/local/tmp</code> path.
Then push the <code class="language-plaintext highlighter-rouge">classes.dex</code> in your Android build output path (<code class="language-plaintext highlighter-rouge">out/target/common/obj/JAVA_LIBRARIES/Mainlib_intermediates</code>) to your phone’s <code class="language-plaintext highlighter-rouge">/data/local/tmp</code> path as well.
Next you should enter <code class="language-plaintext highlighter-rouge">adb shell</code> and <code class="language-plaintext highlighter-rouge">chmod +x</code> to grant your shell script the execution right.
Finally you can execute the shell script to run the Java program.
If you wish to execute your program as a daemon process in the background,
you can use the following command to run the shell script.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setsid XXX.sh <span class="o">&gt;</span>/dev/null 2&gt;&amp;1 &lt; /dev/null
</code></pre></div></div>

<h2 id="building-a-native-shell">Building a Native Shell</h2>

<p>For a native shell, the steps should remain largely unchanged.
One difference is the makefile, which should look like this now.</p>
<div class="language-make highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">LOCAL_PATH</span><span class="o">:=</span> <span class="nf">$(</span><span class="nb">call</span> my-dir<span class="nf">)</span>
<span class="k">include</span><span class="sx"> $(CLEAR_VARS)</span>

<span class="nv">LOCAL_SRC_FILES</span><span class="o">:=</span> <span class="se">\</span>
	main.cpp

<span class="nv">LOCAL_SHARED_LIBRARIES</span> <span class="o">:=</span> <span class="se">\</span>
	libbase <span class="se">\</span>
	libutils <span class="se">\</span>
	liblog <span class="se">\</span>
    libcutils <span class="se">\</span>
	libbinder

<span class="nv">LOCAL_MODULE</span><span class="o">:=</span> Main

<span class="nv">LOCAL_MULTILIB</span> <span class="o">:=</span> both
<span class="nv">LOCAL_MODULE_STEM_32</span> <span class="o">:=</span> <span class="nv">$(LOCAL_MODULE)</span>32
<span class="nv">LOCAL_MODULE_STEM_64</span> <span class="o">:=</span> <span class="nv">$(LOCAL_MODULE)</span>64

<span class="k">include</span><span class="sx"> $(BUILD_EXECUTABLE)</span>
</code></pre></div></div>

<p>This makefile should allow building both 32-bit and 64-bit shell programs simultaneously.
You can find the built binary (named <code class="language-plaintext highlighter-rouge">Main32</code> and <code class="language-plaintext highlighter-rouge">Main64</code>) in <code class="language-plaintext highlighter-rouge">out/target/product/generic_x86_64/system/bin</code> .
Note that depending on your AOSP lunch target, <code class="language-plaintext highlighter-rouge">generic_x86_64</code> could be other paths.</p>

<p>This binary program does not require an additional shell script to execute.
You can directly push the binary to <code class="language-plaintext highlighter-rouge">/data/local/tmp</code> and execute it there.
To daemonize your program, you can simply call the <code class="language-plaintext highlighter-rouge">daemon()</code> function of Linux in your source.</p>]]></content><author><name>Hao Lin</name><email>linhaomails@gmail.com</email></author><category term="Android" /><category term="Java" /><category term="C/C++" /><category term="Shell" /><summary type="html"><![CDATA[A normal Android app almost always comes with graphics interfaces (except for services) and limited privileges. In this post I’ll show how to write a program (in Java or C/C++) that runs in headless mode and possesses system-level privileges, which can be used to perform various tasks in practice such as system service monitoring, debugging and performance profiling.]]></summary></entry><entry><title type="html">Step Debugging Android Native Framework in VS Code</title><link href="https://www.linhao.me/posts/debug-android" rel="alternate" type="text/html" title="Step Debugging Android Native Framework in VS Code" /><published>2022-10-26T00:00:00-07:00</published><updated>2022-10-26T00:00:00-07:00</updated><id>https://www.linhao.me/posts/Debugging-Android-Native-Framework-VSCode</id><content type="html" xml:base="https://www.linhao.me/posts/debug-android"><![CDATA[<p>Tracing the execution flow of Android Framework and debugging your own system components are significantly more convenient with the step debugging capability.
It saves the trouble of frequently instrumenting the Android sources.
This post will show how to enable this capability using LLDB and VS Code.</p>

<h2 id="basic-environment">Basic Environment</h2>

<p>I test the debugging method in a Linux host environment running an Android emulator (both <a href="https://github.com/TrinityEmulator/TrinityEmulator/wiki/Setup-of-Other-Emulators#qemu-kvm">QEMU-KVM with Android-x86</a> and Google Android Emulator are fine).</p>

<p>For the host environment, you need to install both LLDB (<code class="language-plaintext highlighter-rouge">sudo apt install -y lldb</code>) and Android SDK (installed via Android Studio and the SDK commandline tool).</p>

<p>For the guest (i.e., emulators or real devices running your own Android OS) environment,
    it’s better if you have root access.
No root access is also fine but you cannot debug via apps that are not debuggable.
Of course, the guest OS should be compiled by you whose symbol files are thus available to you in the output path of the source base.</p>

<h2 id="guest-setup">Guest Setup</h2>

<p>1) Push the <code class="language-plaintext highlighter-rouge">lldb-server</code> executable in your Android SDK to the (emulated/real) device you wish to debug. The executable can be found in <code class="language-plaintext highlighter-rouge">Sdk/ndk/${NDK_VERSION}/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/${clang VERSION}/lib/linux/x86_64</code> as long as you have installed NDK in your SDK path (select NDK in your SDK management page in Android Studio). To push the executable, use the following command:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>adb push lldb-server /data/local/tmp
</code></pre></div></div>

<p>2) Type the following commands to start the server and listens to a unix socket for connections:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>adb root <span class="c"># if your device has root access</span>
adb shell <span class="s2">"chmod 777 /data/local/tmp/lldb-server"</span>
adb shell <span class="s2">"/data/local/tmp/lldb-server p --server --listen unix-abstract:///data/local/tmp/debug.sock"</span>
</code></pre></div></div>

<p>3) For native components, we are either dealing with a native executable, e.g., a native system service, or a native library.
For the former, you can directly obtain its process ID (PID) via <code class="language-plaintext highlighter-rouge">top</code> or <code class="language-plaintext highlighter-rouge">ps</code>. 
For the latter, you need to run an app that uses the library and acquire the app’s PID.
This post focuses on the latter case.</p>

<p>4) Attaching the debugger to an already running app will be detailed later. Here we’ll first look at <em>how to pause an app process’ startup before the LLDB debugger is attached to it</em>.</p>

<p>This is useful when the component you wish to debug is still quite buggy and can easily crash the app before the debugger can even attach.
The Android developer blog gives a detailed introduction of how to realize this via the jdb Java debugger. You can find it <a href="https://source.android.com/docs/core/tests/debug/gdb#app-startup">here</a>.</p>

<p>An important note is that the <code class="language-plaintext highlighter-rouge">jdb attach</code> command will not work if your Android Studio is also running.
Because Android Studio continuously monitors and preempts any available jdb connections,
    rendering your connection attempts useless.</p>

<p>Also, naturally, to realize pausing before LLDB attaching, you should always attach jdb (and let the app continues in the Java layer) after LLDB is attached (to be detailed later).</p>

<h2 id="host-setup">Host Setup</h2>
<p>At the host side, you should have an Android/AOSP code base which matches the Android OS run in your guest machine.</p>

<p>1) Open the Android code base using VS Code.</p>

<p>2) Install the <a href="https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb">CodeLLDB</a> extension in your VS Code.</p>

<p>3) Click the <code class="language-plaintext highlighter-rouge">Run</code> tab of VS Code, select <code class="language-plaintext highlighter-rouge">Add configuration</code>, and select LLDB. This will create a <code class="language-plaintext highlighter-rouge">launch.json</code> file in your <code class="language-plaintext highlighter-rouge">.vscode</code> folder. Replace its content as follows</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"configurations"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
    </span><span class="nl">"initCommands"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="s2">"settings append target.exec-search-paths ${ANDROID_SRC}/out/target/product/x86_64/symbols/system/lib64/ ${ANDROID_SRC}/out/target/product/x86_64/symbols/system/lib64/hw ${ANDROID_SRC}/out/target/product/x86_64/symbols/system/lib64/egl ${ANDROID_SRC}/out/target/product/x86_64/symbols/system/lib64/extractors ${ANDROID_SRC}/out/target/product/x86_64/symbols/system/vendor/lib64/ ${ANDROID_SRC}/out/target/product/x86_64/symbols/system/vendor/lib64/hw ${ANDROID_SRC}/out/target/product/x86_64/symbols/system/vendor/lib64/egl ${ANDROID_SRC}/out/target/product/x86_64/symbols/system/vendor/lib64/dri ${ANDROID_SRC}/out/target/product/x86_64/symbols/system/vendor/lib64/extractors ${ANDROID_SRC}/out/target/product/x86_64/symbols/system/vendor/lib64/mediacas ${ANDROID_SRC}/out/target/product/x86_64/symbols/system/vendor/lib64/mediadrm ${ANDROID_SRC}/out/target/product/x86_64/symbols/system/vendor/lib64/soundfx"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"platform select remote-android"</span><span class="p">,</span><span class="w"> 
        </span><span class="s2">"platform connect unix-abstract-connect:///data/local/tmp/debug.sock"</span><span class="p">,</span><span class="w">
    </span><span class="p">],</span><span class="w"> 
    </span><span class="err">//</span><span class="w"> </span><span class="err">Map</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">current</span><span class="w"> </span><span class="err">path</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">Android</span><span class="w"> </span><span class="err">code</span><span class="w"> </span><span class="err">base</span><span class="w">
    </span><span class="nl">"sourceMap"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">""</span><span class="p">:</span><span class="w"> </span><span class="s2">"${ANDROID_SRC}"</span><span class="p">,</span><span class="w"> 
        </span><span class="nl">"/b/f/w"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${ANDROID_SRC}"</span><span class="p">,</span><span class="w"> 
        </span><span class="nl">"."</span><span class="p">:</span><span class="w"> </span><span class="s2">"${ANDROID_SRC}"</span><span class="w">
    </span><span class="p">},</span><span class="w"> 
    </span><span class="nl">"pid"</span><span class="p">:</span><span class="w"> </span><span class="err">$</span><span class="p">{</span><span class="err">PID</span><span class="p">},</span><span class="w"> 
    </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Remote Android Debug"</span><span class="p">,</span><span class="w"> 
    </span><span class="nl">"relativePathBase"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${ANDROID_SRC}"</span><span class="p">,</span><span class="w"> 
    </span><span class="nl">"request"</span><span class="p">:</span><span class="w"> </span><span class="s2">"attach"</span><span class="p">,</span><span class="w"> 
    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"lldb"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>where <code class="language-plaintext highlighter-rouge">${ANDROID_SRC}</code> is the path to your Android code base and <code class="language-plaintext highlighter-rouge">${PID}</code> is the process ID of the process you wish to debug.
Also, note that the <code class="language-plaintext highlighter-rouge">target.exec-search-paths</code> appended in the <code class="language-plaintext highlighter-rouge">initCommands</code> are not always the same for different Android versions. You need to check the paths and see if they actually exist, and whether there’re missing folders that are not included.</p>

<p>4) Click the debug button of VS Code to begin debugging. Remember to change the <code class="language-plaintext highlighter-rouge">${PID}</code> for different debuggees.</p>

<p>5) Once the LLDB is attached, your can attach jdb to let the app continue execution. Now you are put breakpoints anywhere in VS Code to realize step debugging.</p>]]></content><author><name>Hao Lin</name><email>linhaomails@gmail.com</email></author><category term="Android" /><category term="Debugging" /><category term="C/C++" /><category term="VS Code" /><summary type="html"><![CDATA[Tracing the execution flow of Android Framework and debugging your own system components are significantly more convenient with the step debugging capability. It saves the trouble of frequently instrumenting the Android sources. This post will show how to enable this capability using LLDB and VS Code.]]></summary></entry><entry><title type="html">Enable VoLTE/ViLTE on Pixel in Unlisted Regions</title><link href="https://www.linhao.me/posts/volte-pixel" rel="alternate" type="text/html" title="Enable VoLTE/ViLTE on Pixel in Unlisted Regions" /><published>2022-10-25T00:00:00-07:00</published><updated>2022-10-25T00:00:00-07:00</updated><id>https://www.linhao.me/posts/Enable-VoLTE-Pixel</id><content type="html" xml:base="https://www.linhao.me/posts/volte-pixel"><![CDATA[<p>In this post I’ll document the steps to enable VoLTE/ViLTE (or their counterparts in 5G such as VoNR) on Google Pixel devices in unlisted regions, e.g., China.
For Chinese operators (e.g., China Unicom),
Pixel devices are already shipped with the corresponding Qualcomm modem mbn files,
which are placed under <code class="language-plaintext highlighter-rouge">/system/vendor/rfs/msm/mpss/readonly/vendor/mbn/mcfg_sw/generic/China/</code>.
However, such files are not enabled by default, which renders VoLTE/ViLTE unusable in China.</p>

<p>The solution I use is based on Magisk, which enables patching the readonly system partition and modifies system properties to turn on VoLTE/ViLTE.
This method does not require flashing a new Android image, therefore is ready-to-use for stock Pixel devices.
However, if you’re interested in building an Android image from source with VoLTE/ViLTE enabled by default, the required steps will also be discussed in the post.</p>

<p>I’ll use a Pixel 3 XL running Android 11 as an example throughout the post. 
Though the method should be generalizable to other Pixel models and Android versions.</p>

<h2 id="enable-voltevilte-with-magisk">Enable VoLTE/ViLTE with Magisk</h2>

<p>Magisk enables systemless root and thus provide a means to modifying the readonly system partition and many system properties.
Follow the steps below to install Magisk on your Pixel device and make your own patch to turn on VoLTE/ViLTE.</p>

<h3 id="magisk-installation">Magisk Installation</h3>
<p>To install Magisk, first make sure you can get your hands on your Pixel’s <code class="language-plaintext highlighter-rouge">boot.img</code> image.
To this end, I recommend that you download and flash a factory image of your Pixel following the instructions <a href="https://developers.google.com/android/images">here</a>.
After downloading the factory image, 
you should extract the <code class="language-plaintext highlighter-rouge">boot.img</code> file from the <code class="language-plaintext highlighter-rouge">image-XXX.zip</code> archive.
Once you finish flashing the factory image,
install the Magisk app provided <a href="https://github.com/topjohnwu/Magisk/releases">here</a> on your Pixel.
Then use <code class="language-plaintext highlighter-rouge">adb push</code> to send the <code class="language-plaintext highlighter-rouge">boot.img</code> file extracted above to your phone’s <code class="language-plaintext highlighter-rouge">/sdcard</code> directory (or any writable directory as you see fit).
Next open the Magisk app, click <code class="language-plaintext highlighter-rouge">install</code>, select <code class="language-plaintext highlighter-rouge">Select and Patch a File</code>, and then select the <code class="language-plaintext highlighter-rouge">boot.img</code> file sent to the phone to begin installation.
Now reboot your device and you’re all set.</p>

<h3 id="making-magisk-patch">Making Magisk Patch</h3>

<p>With the above steps, you should be able to see that Magisk is installed and enabled on the device.
The next step would be to make a Magisk patch that can be installed to the system through Magisk.
I provide an example for Pixel 3 XL in this GitHub [repo] (https://github.com/andylin-hao/Pixel-VoLTE-Patch).
Generally, the patch involve modifying system properties and Qualcomm’s <code class="language-plaintext highlighter-rouge">mbn</code> component.
For system properties, the corresponding modifications are shown in the <code class="language-plaintext highlighter-rouge">common/system.prop</code> file of the GitHub repo, which are (inspired by voltenabler)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Debug Options
persist.dbg.ims_volte_enable=1
persist.dbg.volte_avail_ovr=1
persist.dbg.vt_avail_ovr=1
persist.dbg.wfc_avail_ovr=1

# Data Options
persist.data.iwlan.enable=true
persist.data.iwlan=1
persist.data.iwlan.ipsec.ap=1

# Radio Options
persist.radio.volte.dan_support=true
persist.radio.rat_on=combine
persist.radio.data_ltd_sys_ind=1
persist.radio.data_con_rprt=1
persist.radio.calls.on.ims=1
persist.radio.VT_ENABLE=1

# Other Options
persist.sys.cust.lte_config=true
persist.rcs.supported=1
</code></pre></div></div>

<p>Apart from system properties, <code class="language-plaintext highlighter-rouge">/system/vendor/rfs/msm/mpss/readonly/vendor/mbn/mcfg_sw/mbn_sw.txt</code> should also be modified to include Chinese operators.
For example, if you are using a China Unicom SIM card, just add the following to the <code class="language-plaintext highlighter-rouge">mbn_sw.txt</code> file.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mcfg_sw/generic/China/CU/Commercial/OpenMkt/mcfg_sw.mbn
mcfg_sw/generic/China/CU/Commercial/VoLTE/mcfg_sw.mbn
mcfg_sw/generic/China/CU/Lab/Test/mcfg_sw.mbn
</code></pre></div></div>
<p>However, for different devices, the <code class="language-plaintext highlighter-rouge">mcfg_sw.mbn</code> files may not lie in the same paths.
You should extract the whole <code class="language-plaintext highlighter-rouge">mcfg_sw</code> folder from your own phone (which can be found in the same path, i.e., <code class="language-plaintext highlighter-rouge">/system/vendor/rfs/msm/mpss/readonly/vendor/mbn/mcfg_sw/</code> in the phone) and modify the <code class="language-plaintext highlighter-rouge">mbn_sw.txt</code> file in the folder according to the <code class="language-plaintext highlighter-rouge">mcfg_sw.mbn</code> files included in your <code class="language-plaintext highlighter-rouge">generic</code> folder.
Also, the <code class="language-plaintext highlighter-rouge">mcfg_sw.mbn</code> files provided in my repo are extracted from Pixel 3 XL and not tested on other devices.
You should always extract the <code class="language-plaintext highlighter-rouge">mcfg_sw.mbn</code> files from your own device, and replace the corresponding <code class="language-plaintext highlighter-rouge">mcfg_sw.mbn</code> files in my repo with your own <code class="language-plaintext highlighter-rouge">mcfg_sw.mbn</code> files.</p>

<h3 id="patch-installation">Patch Installation</h3>

<p>Compress the repo into a zip archive and push it to your device.
Open Magisk, click <code class="language-plaintext highlighter-rouge">Modules</code>, and install the zip archive you just push.
Now reboot, you should see that VoLTE/ViLTE are enabled in your data connection settings.
You can also check this by typing <code class="language-plaintext highlighter-rouge">*#*#4636#*#*</code> in your Dialer app.
However, although everything seems to be on, you may still fail to connect when initializing a video call.
This is because you still do not have an <code class="language-plaintext highlighter-rouge">ims</code> APN.
You should manually add an <code class="language-plaintext highlighter-rouge">ims</code> APN in your data connection settings.
To do this, fill the <code class="language-plaintext highlighter-rouge">Name</code>, <code class="language-plaintext highlighter-rouge">APN</code>, <code class="language-plaintext highlighter-rouge">APN Type</code> fields with <code class="language-plaintext highlighter-rouge">ims</code>, <code class="language-plaintext highlighter-rouge">APN protocol</code> filed with <code class="language-plaintext highlighter-rouge">IPV4/IPV6</code>, <code class="language-plaintext highlighter-rouge">APN roaming protocol</code> field with <code class="language-plaintext highlighter-rouge">IPV4</code>, and the other fields as default.
Turn on/off airplane mode, and you should be able to make a video call now.</p>

<h2 id="enable-voltevilte-when-building-an-android-image">Enable VoLTE/ViLTE when Building an Android Image</h2>

<p>When building Android from source, you should be able to do similar modifications to your built images.
I’ve successfully done this with LineageOS, but am still working to port to AOSP.
For LineageOS, the steps are quite simple.
First, modify the <code class="language-plaintext highlighter-rouge">device.mk</code> file of your Pixel model and add the above system properties.
Next, modify the <code class="language-plaintext highlighter-rouge">mbn_sw.txt</code> in the <code class="language-plaintext highlighter-rouge">vendor</code> folder (LineageOS will extract this file from your device as well before building).</p>]]></content><author><name>Hao Lin</name><email>linhaomails@gmail.com</email></author><category term="Android" /><category term="VoLTE" /><category term="ViLTE" /><category term="Cellular" /><category term="Video Call" /><category term="Pixel" /><summary type="html"><![CDATA[In this post I’ll document the steps to enable VoLTE/ViLTE (or their counterparts in 5G such as VoNR) on Google Pixel devices in unlisted regions, e.g., China. For Chinese operators (e.g., China Unicom), Pixel devices are already shipped with the corresponding Qualcomm modem mbn files, which are placed under /system/vendor/rfs/msm/mpss/readonly/vendor/mbn/mcfg_sw/generic/China/. However, such files are not enabled by default, which renders VoLTE/ViLTE unusable in China.]]></summary></entry><entry><title type="html">Include Intel Houdini in Android-x86</title><link href="https://www.linhao.me/posts/houdini-x86" rel="alternate" type="text/html" title="Include Intel Houdini in Android-x86" /><published>2022-02-13T00:00:00-08:00</published><updated>2022-02-13T00:00:00-08:00</updated><id>https://www.linhao.me/posts/Intel-Houdini</id><content type="html" xml:base="https://www.linhao.me/posts/houdini-x86"><![CDATA[<p>In this post I’ll document the necessary steps for incorporating Intel Houdini in Android-x86 9. Note that this does not work on Android 10+.</p>

<p>Prior methods of <code class="language-plaintext highlighter-rouge">enable_nativebridge</code> no longer works on Android 8+ of Android-x86 due to missing libraries for downloads. Fortunately, <a href="https://github.com/BlissRoms-x86/android_vendor_google_chromeos-x86">BlissOS</a> has provided an alternative solution by extracting the Houdini libraries from ChromeOS. Here’s how it’s done.</p>

<p>1) Create a <code class="language-plaintext highlighter-rouge">vendor/google</code> folder at the root directory if necessary, and run <code class="language-plaintext highlighter-rouge">git clone https://github.com/BlissRoms-x86/android_vendor_google_chromeos-x86 vendor/google/chromeos-x86</code></p>

<p>2) Add the following to <code class="language-plaintext highlighter-rouge">device/generic/common/BoardConfig.mk</code>.</p>

<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nv">BOARD_SEPOLICY_DIRS</span> <span class="o">+=</span> vendor/google/chromeos-x86/sepolicy
  <span class="k">-include</span><span class="sx"> vendor/google/chromeos-x86/board/native_bridge_arm_on_x86.mk</span>
</code></pre></div></div>

<p>3) Remove the <code class="language-plaintext highlighter-rouge">hal_drm_widevine.te</code> file at <code class="language-plaintext highlighter-rouge">device/generic/common/sepolicy/nonplat</code>.</p>

<p>4) Add the following to <code class="language-plaintext highlighter-rouge">device/generic/common/device.mk</code>.</p>

<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="err">$(call</span> <span class="err">inherit-product-if-exists,</span> <span class="err">vendor/google/chromeos-x86/target/houdini.mk)</span>
  <span class="err">$(call</span> <span class="err">inherit-product-if-exists,</span> <span class="err">vendor/google/chromeos-x86/target/native_bridge_arm_on_x86.mk)</span>
  <span class="err">$(call</span> <span class="err">inherit-product-if-exists,</span> <span class="err">vendor/google/chromeos-x86/target/widevine.mk)</span>
</code></pre></div></div>

<p>5) Modify <code class="language-plaintext highlighter-rouge">device/generic/common/nativebridge/nativebridge.mk</code> to the following.</p>

<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c">#
</span>  <span class="c"># Copyright (C) 2015 The Android-x86 Open Source Project
</span>  <span class="c">#
</span>  <span class="c"># Licensed under the GNU General Public License Version 2 or later.
</span>  <span class="c"># You may not use this file except in compliance with the License.
</span>  <span class="c"># You may obtain a copy of the License at
</span>  <span class="c">#
</span>  <span class="c">#      http://www.gnu.org/licenses/gpl.html
</span>  <span class="c">#
</span>  
  <span class="c"># Enable native bridge
</span>  <span class="nv">WITH_NATIVE_BRIDGE</span> <span class="o">:=</span> <span class="nb">true</span>
  
  <span class="c"># Native Bridge ABI List
</span>  <span class="nv">NATIVE_BRIDGE_ABI_LIST_32_BIT</span> <span class="o">:=</span> armeabi-v7a armeabi
  <span class="nv">NATIVE_BRIDGE_ABI_LIST_64_BIT</span> <span class="o">:=</span> arm64-v8a
  <span class="nv">NATIVE_BRIDGE_ABI_LIST</span> <span class="o">:=</span> x86_64 x86 arm64-v8a
  
  <span class="nv">LOCAL_SRC_FILES</span> <span class="o">:=</span> bin/enable_nativebridge
  
  <span class="nv">PRODUCT_COPY_FILES</span> <span class="o">:=</span> <span class="nf">$(</span><span class="nb">foreach</span> f,<span class="nv">$(LOCAL_SRC_FILES)</span>,<span class="nv">$(LOCAL_PATH)</span>/<span class="nv">$(f)</span>:system/<span class="nv">$(f)</span><span class="nf">)</span> <span class="se">\</span>
      <span class="nv">$(LOCAL_PATH)</span>/OEMBlackList:<span class="nv">$(TARGET_COPY_OUT_VENDOR)</span>/etc/misc/.OEMBlackList <span class="se">\</span>
      <span class="nv">$(LOCAL_PATH)</span>/OEMWhiteList:<span class="nv">$(TARGET_COPY_OUT_VENDOR)</span>/etc/misc/.OEMWhiteList <span class="se">\</span>
      <span class="nv">$(LOCAL_PATH)</span>/ThirdPartySO:<span class="nv">$(TARGET_COPY_OUT_VENDOR)</span>/etc/misc/.ThirdPartySO <span class="se">\</span>
  
  <span class="nv">PRODUCT_PROPERTY_OVERRIDES</span> <span class="o">:=</span> <span class="se">\</span>
      ro.dalvik.vm.isa.arm<span class="o">=</span>x86 <span class="se">\</span>
      ro.enable.native.bridge.exec<span class="o">=</span>1 <span class="se">\</span>
  
  <span class="c"># ifeq ($(TARGET_SUPPORTS_64_BIT_APPS),true)
</span>  <span class="nv">PRODUCT_PROPERTY_OVERRIDES</span> <span class="o">+=</span> <span class="se">\</span>
      ro.dalvik.vm.isa.arm64<span class="o">=</span>x86_64 <span class="se">\</span>
      ro.enable.native.bridge.exec64<span class="o">=</span>1
  <span class="c"># endif
</span>  
  <span class="nv">PRODUCT_PROPERTY_OVERRIDES</span> <span class="o">:=</span> ro.dalvik.vm.native.bridge<span class="o">=</span>libhoudini.so
  
  <span class="c"># ifneq ($(HOUDINI_PREINSTALL),intel)
</span>  <span class="c"># PRODUCT_DEFAULT_PROPERTY_OVERRIDES := ro.dalvik.vm.native.bridge=libnb.so
</span>  
  <span class="c"># PRODUCT_PACKAGES := libnb
</span>  <span class="c"># endif
</span>  
  <span class="err">$(call</span> <span class="err">inherit-product-if-exists,vendor/intel/houdini/houdini.mk)</span>
</code></pre></div></div>]]></content><author><name>Hao Lin</name><email>linhaomails@gmail.com</email></author><category term="Android-x86" /><category term="Intel Houdini" /><summary type="html"><![CDATA[In this post I’ll document the necessary steps for incorporating Intel Houdini in Android-x86 9. Note that this does not work on Android 10+.]]></summary></entry><entry><title type="html">Modify Board and CPU Info in Android-x86</title><link href="https://www.linhao.me/posts/modify-board" rel="alternate" type="text/html" title="Modify Board and CPU Info in Android-x86" /><published>2021-11-18T00:00:00-08:00</published><updated>2021-11-18T00:00:00-08:00</updated><id>https://www.linhao.me/posts/Modify-Board-Info</id><content type="html" xml:base="https://www.linhao.me/posts/modify-board"><![CDATA[<p>In this post I’ll introduce how to make your Android-x86 more like a real device by changing its board and CPU configurations. I’ll use the configurations of Samsung Galaxy S20 Ultra as example.</p>

<p>1) Modify the device information part of <code class="language-plaintext highlighter-rouge">init_misc</code> at <code class="language-plaintext highlighter-rouge">device/generic/common/init.sh</code> to the following.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  setprop ro.product.manufacturer <span class="s2">"samsung"</span>
  setprop ro.product.model <span class="s2">"SM-G988N"</span>
  setprop ro.serialno <span class="s2">"15c4e1ebc491"</span>
</code></pre></div></div>

<p>2) Modify the <code class="language-plaintext highlighter-rouge">on fs</code> part at <code class="language-plaintext highlighter-rouge">device/generic/common/init_x86.rc</code> to <code class="language-plaintext highlighter-rouge">mount_all /fstab.android_x86_64</code>.</p>

<p>3) Modify the <code class="language-plaintext highlighter-rouge">export_kernel_boot_props</code> at <code class="language-plaintext highlighter-rouge">system/core/init/init.cpp</code> by changing <code class="language-plaintext highlighter-rouge">ro.hardware</code>’s value to <code class="language-plaintext highlighter-rouge">exynos8895</code>.</p>

<p>4) Modify <code class="language-plaintext highlighter-rouge">${ro.hardware}</code> to <code class="language-plaintext highlighter-rouge">android_x86_64</code> at <code class="language-plaintext highlighter-rouge">bootable/recovery/etc/init.rc</code>.</p>

<p>5) Make the same modification to <code class="language-plaintext highlighter-rouge">system/core/rootdir/init.rc</code>.</p>

<p>6) Modify the following parameters at <code class="language-plaintext highlighter-rouge">build/tools/buildinfo.sh</code>.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nb">echo</span> <span class="s2">"ro.product.model=SM-G988N"</span>
  <span class="nb">echo</span> <span class="s2">"ro.product.brand=samsung"</span>
  <span class="nb">echo</span> <span class="s2">"ro.product.name=z3qksx"</span>
  <span class="nb">echo</span> <span class="s2">"ro.product.device=z3q"</span>
</code></pre></div></div>

<p>7) Modify <code class="language-plaintext highlighter-rouge">build/tools/vendor_buildinfo.sh</code> to the following.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c">#!/bin/bash</span>
  
  <span class="nb">echo</span> <span class="s2">"# begin build properties"</span>
  <span class="nb">echo</span> <span class="s2">"# autogenerated by vendor_buildinfo.sh"</span>
  
  <span class="nb">echo</span> <span class="s2">"ro.product.board=SM-G988N"</span>
  <span class="nb">echo</span> <span class="s2">"ro.board.platform=exynos9"</span>
  
  <span class="nb">echo</span> <span class="s2">"ro.product.vendor.manufacturer=samsung"</span>
  <span class="nb">echo</span> <span class="s2">"ro.product.vendor.model=SM-G988N"</span>
  <span class="nb">echo</span> <span class="s2">"ro.product.vendor.brand=samsung"</span>
  <span class="nb">echo</span> <span class="s2">"ro.product.vendor.name=SM-G988N"</span>
  <span class="nb">echo</span> <span class="s2">"ro.product.vendor.device=SM-G988N"</span>
  
  <span class="nb">echo</span> <span class="s2">"# end build properties"</span>
</code></pre></div></div>]]></content><author><name>Hao Lin</name><email>linhaomails@gmail.com</email></author><category term="Android-x86" /><summary type="html"><![CDATA[In this post I’ll introduce how to make your Android-x86 more like a real device by changing its board and CPU configurations. I’ll use the configurations of Samsung Galaxy S20 Ultra as example.]]></summary></entry><entry><title type="html">Incorporate OpenGApps into Android-x86</title><link href="https://www.linhao.me/posts/incorporate-opengapps" rel="alternate" type="text/html" title="Incorporate OpenGApps into Android-x86" /><published>2021-11-15T00:00:00-08:00</published><updated>2021-11-15T00:00:00-08:00</updated><id>https://www.linhao.me/posts/Incorporate-OpenGApps</id><content type="html" xml:base="https://www.linhao.me/posts/incorporate-opengapps"><![CDATA[<p>In this post I’ll show the configurations required to build Android-x86 source with OpenGApps,
    which provides Google Play and GMS so that your customized Android-x86 can enjoy apps from Google Play.</p>

<p>1) Add the following contents to the end of <code class="language-plaintext highlighter-rouge">.repo/manifests/defaults.xml</code>.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nt">&lt;remote</span> <span class="na">name=</span><span class="s">"opengapps"</span> <span class="na">fetch=</span><span class="s">"https://github.com/opengapps/"</span>  <span class="nt">/&gt;</span>
  <span class="nt">&lt;remote</span> <span class="na">name=</span><span class="s">"opengapps-gitlab"</span> <span class="na">fetch=</span><span class="s">"https://gitlab.opengapps.org/opengapps/"</span>  <span class="nt">/&gt;</span>
  <span class="nt">&lt;project</span> <span class="na">path=</span><span class="s">"vendor/opengapps/build"</span> <span class="na">name=</span><span class="s">"aosp_build"</span> <span class="na">revision=</span><span class="s">"master"</span> <span class="na">remote=</span><span class="s">"opengapps"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;project</span> <span class="na">path=</span><span class="s">"vendor/opengapps/sources/all"</span> <span class="na">name=</span><span class="s">"all"</span> <span class="na">clone-depth=</span><span class="s">"1"</span> <span class="na">revision=</span><span class="s">"master"</span> <span class="na">remote=</span><span class="s">"opengapps-gitlab"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;project</span> <span class="na">path=</span><span class="s">"vendor/opengapps/sources/x86"</span> <span class="na">name=</span><span class="s">"x86"</span> <span class="na">clone-depth=</span><span class="s">"1"</span> <span class="na">revision=</span><span class="s">"master"</span> <span class="na">remote=</span><span class="s">"opengapps-gitlab"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;project</span> <span class="na">path=</span><span class="s">"vendor/opengapps/sources/x86_64"</span> <span class="na">name=</span><span class="s">"x86_64"</span> <span class="na">clone-depth=</span><span class="s">"1"</span> <span class="na">revision=</span><span class="s">"master"</span> <span class="na">remote=</span><span class="s">"opengapps-gitlab"</span> <span class="nt">/&gt;</span>
</code></pre></div></div>

<p>Note that the above contents may change as OpenGApps upgrades. Check the latest <a href="https://github.com/opengapps/aosp_build#getting-started">here</a>.</p>

<p>2) Locate the following content at <code class="language-plaintext highlighter-rouge">device/generic/common/device.mk</code>.</p>

<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c"># Get GMS
</span>  <span class="nv">GAPPS_VARIANT</span> <span class="o">?=</span> pico
  <span class="err">$(call</span> <span class="err">inherit-product-if-exists,$(if</span> <span class="err">$(wildcard</span> <span class="err">vendor/google/products/gms.mk),vendor/google/products/gms.mk,vendor/opengapps/build/opengapps-packages.mk))</span>
</code></pre></div></div>

<p>3) Modify it to the following.</p>

<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c"># Get GMS
</span>  <span class="nv">GAPPS_VARIANT</span> <span class="o">:=</span> pico
  <span class="err">$(call</span> <span class="err">inherit-product-if-exists,$(if</span> <span class="err">$(wildcard</span> <span class="err">vendor/google/products/gms.mk),vendor/google/products/gms.mk,vendor/opengapps/build/opengapps-packages.mk))</span>
</code></pre></div></div>

<p>4) Run <code class="language-plaintext highlighter-rouge">repo sync</code>, <code class="language-plaintext highlighter-rouge">git lfs install</code>, and <code class="language-plaintext highlighter-rouge">repo forall -c git lfs pull</code> at the root directory of your Android-x86 source.</p>

<p>5) Add a <code class="language-plaintext highlighter-rouge">CleanSpec.mk</code> at <code class="language-plaintext highlighter-rouge">vendor/opengapps/build</code> with the following contents.</p>

<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="err">$(call</span> <span class="err">add-clean-step,</span> <span class="err">rm</span> <span class="err">-rf</span> <span class="err">$(PRODUCT_OUT)/system/priv-app/*)</span>
  <span class="err">$(call</span> <span class="err">add-clean-step,</span> <span class="err">rm</span> <span class="err">-rf</span> <span class="err">$(PRODUCT_OUT)/system/app/*)</span>
</code></pre></div></div>

<p>6) Add a <code class="language-plaintext highlighter-rouge">Android.mk</code> at <code class="language-plaintext highlighter-rouge">vendor/opengapps/build/modules</code> with the following contents so as to avoid building <code class="language-plaintext highlighter-rouge">ActionServices</code> which only provides ARM source.</p>

<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="k">include</span><span class="sx"> $(call all-named-subdir-makefiles,$(GAPPS_PRODUCT_PACKAGES))</span>
</code></pre></div></div>

<p>7) Remove the following from <code class="language-plaintext highlighter-rouge">vendor/opengapps/build/opengapps-packages.mk</code>.</p>

<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="err">MarkupGoogle</span> <span class="err">\</span>
  <span class="err">GoogleCamera</span> <span class="err">\</span>
  <span class="err">SetupWizard</span> <span class="err">\</span> <span class="c"># This is the boot-time setup wizard. You can keep it in Android 9, but do remove it in Android 10 as it incurs issues at boot time.
</span></code></pre></div></div>

<p>8) <code class="language-plaintext highlighter-rouge">make</code> your Android-x86 image.</p>]]></content><author><name>Hao Lin</name><email>linhaomails@gmail.com</email></author><category term="Android-x86" /><category term="OpenGApps" /><summary type="html"><![CDATA[In this post I’ll show the configurations required to build Android-x86 source with OpenGApps, which provides Google Play and GMS so that your customized Android-x86 can enjoy apps from Google Play.]]></summary></entry><entry><title type="html">Linux Graphics Stack</title><link href="https://www.linhao.me/posts/linux-graphics-stack" rel="alternate" type="text/html" title="Linux Graphics Stack" /><published>2021-04-25T10:00:55-07:00</published><updated>2021-04-25T10:00:55-07:00</updated><id>https://www.linhao.me/posts/Linux-Graphics-Stack</id><content type="html" xml:base="https://www.linhao.me/posts/linux-graphics-stack"><![CDATA[<p>Due to heavy historical burden, the Linux graphics stack is extremely complex with fragmented and intricate software components. As a result, it’s quite exhausting to fully grasp its core idea and principles. This has posed significant challenges to my recent work on graphics virtualization of Android. Fortunately, after days of struggling with its sources and prior technical posts, I may have obtained some levels of understandings regarding the framework of the Linux graphics stack. SO, before I completely lose track of the story and to benefit other unfortunate comrades, I decide to document everything here in this post.</p>

<p>Hope you’ll find it useful.</p>

<h1 id="overview">Overview</h1>
<p>Before we get started, I’ll just throw an architectural graph at you for three purposes:</p>

<p>1) for you to have some basic ideas of the graphics stack’s various components to be discussed in this post, 
2) for you to reference each component’s position in the whole stack when reading the parts to follow, and
3) for you to understand the chaos and challenges ahead.</p>

<p>Here it comes:</p>

<p><img src="https://www.linhao.me/images/posts/graphics stack.svg" /></p>

<p>Note that this graph only demonstrates a common architecture of today’s Linux graphics stack built upon the Direct Rendering Infrastructure (DRI).
As other architectures are now rarely used, they are out of this post’s scope.</p>

<p>Now you are familiarized with the architecture, I’ll next introduce the components within and their rationale behind by walking you through the history and showing you the reasons they came to be in the first place.</p>

<h1 id="indirect-rendering">Indirect Rendering</h1>

<p>Nothing is complex in the beginning. The same goes for the Linux graphics. In the early days, only one software in the system is responsible for graphics rendering and communicating with the GPU — X server. Applications use <code class="language-plaintext highlighter-rouge">libX11</code> to tell the X server how to render graphics. After the X server receives hardware-independent rendering commands (defined in the X11 protocol, thus known as Device-Independent X, or <code class="language-plaintext highlighter-rouge">DIX</code>) from applications, it then translates these commands into hardware languages that the GPU can understand (e.g., register writes and memory allocations). This translation software module inside the X server is commonly referred to as Device-Dependent X driver, or <code class="language-plaintext highlighter-rouge">DDX</code> driver. Here “driver” is a graphic argot that differs from what you have probably learned in OS design — the <code class="language-plaintext highlighter-rouge">DDX</code> driver actually operates in the user space rather than in the kernel space as most OS drivers do. Intuitively, to accommodate heterogeneous GPUs, many <code class="language-plaintext highlighter-rouge">DDX</code> drivers should be developed accordingly.</p>

<p>At that time, only 2D rendering is necessary for computer graphics. Therefore, the initial <code class="language-plaintext highlighter-rouge">libX11</code> only includes 2D-related operations. However, with the advent of 3D graphics, the above-described architecture soon fails to satisfy people’s needs. To perform 3D rendering on heterogeneous hardware,
OpenGL is developed as the new GPU-agnostic interfaces for 3D operations. Specifically, OpenGL is only a series of function specifications that dictate only “what they do” but do not restrict “how to do it”. Thus, the actual OpenGL implementation is totally delegated to the system, which normally can be found in the <code class="language-plaintext highlighter-rouge">libGL.so</code> your 3D applications link to.</p>

<p>To avoid complete refactoring of the whole stack, the initial implementation of OpenGL is built upon the 2D X server architecture. The technique that makes this possible is what we call the GL eXtenstion (<code class="language-plaintext highlighter-rouge">GLX</code>) of the X11 protocol. <code class="language-plaintext highlighter-rouge">GLX</code> is a wapper that wraps OpenGL function calls in X11. The X server then similarly receives X11 commands and translates them into the hardware language as before. Since this process involves wrapping into X11 and intervention of the X server rather than directly asks GPU to perform 3D operations, it is thus later known as indirect rendering (the left path of our architecture graph).</p>

<p>Unfortunately, soon this indirect approach became insufficient for emerging 3D-intensive applications (e.g., 3D games) as it takes unnecessary detour to reach hardware. To address this, direct rendering is proposed.</p>

<h1 id="direct-rendering">Direct Rendering</h1>

<h3 id="direct-rendering-infrastructure">Direct Rendering Infrastructure</h3>

<p>The idea of direct rendering is allowing OpenGL implementation 
    to directly interface the hardware GPU without the intervention of X.
To this end, the OpenGL library <code class="language-plaintext highlighter-rouge">libGL</code>’s implementation should know how to deal with the hardware interfaces.
However, <code class="language-plaintext highlighter-rouge">libGL</code> is often a user-space library, 
    which should not be given the privilege to handle hardware interactions.
Instead, a dedicated kernel-space driver is developed to manipulate the GPU instead,
    while providing user-space interfaces for <code class="language-plaintext highlighter-rouge">libGL</code> and other graphics libraries to use.
Given that the driver is devised under the direct rendering framework,
    it is often referred to as Direct Rendering Manager, or DRM for short.
The above framework, including the user-space OpenGL library and the kernel-space DRM,
    is often dubbed as Direct Rendering Infrastructure (DRI).</p>

<h3 id="kernel-space-direct-rendering-manager">Kernel Space: Direct Rendering Manager</h3>

<p>The kernel-space DRM driver is responsible for allocating graphics memory, submitting drawing commands, and transferring graphics data, etc.
Nowadays, even X interacts with the GPU through DRM.
With DRM,
First, the OpenGL library and other graphics libraries directly used by applications can still reside in the user space so that applications’ frequent interactions with it do not incur frequent user/kernel switch, and
Note that DRM is not the only implementation exist out there.
Hardware GPU vendors like NVIDIA also has their own implementations of kernel-space drivers for interacting with the hardware,
    which share similar functionalities as those of DRM.
For example, NVIDIA has open-sourced its <a href="https://github.com/NVIDIA/open-gpu-kernel-modules">Linux kernel driver</a>.</p>

<h3 id="user-space-mesa-opengl-library">User Space: Mesa OpenGL Library</h3>

<p>Apart from the kernel-space driver, another important part of DRI is the user-space library <code class="language-plaintext highlighter-rouge">libGL</code>.
To implement the user-space library,
    an issue is that different hardware platforms expose different interfaces and thus should come with different <code class="language-plaintext highlighter-rouge">libGL</code> implementations.
However, naturally, different implementations still share a large amount of code and incur severe code duplications if managed separately.</p>

<p>To address this, a generic framework is proposed to hold all open-source graphics libraries of Linux, which is Mesa.
Mesa abstracts capabilities of a graphics library so that different implementations for different hardware platforms can share as much code as possible.
As shown in the graph above,
    the Mesa framework usually involves state tracker, pipe driver and WinSys driver.
Specifically, state tracker abstracts the state management in various graphics languages (e.g., OpenGL and Direct3D), allowing Mesa to extend supports for languages beyond OpenGL.
Pipe driver and WinSys driver abstract OS-specific interactions such as managing window.
This unique architecture allows Mesa to hold OpenGL (and even Direct3D) libraries for GPUs of myriad vendors such as Intel, AMD, and NVIDIA.
In fact, many virtualization-based GPU drivers such as those of VMware and QEMU virtio-gpu are also built within the Mesa framework.
What a miracle!</p>]]></content><author><name>Hao Lin</name><email>linhaomails@gmail.com</email></author><category term="Linux" /><category term="Graphics" /><summary type="html"><![CDATA[Due to heavy historical burden, the Linux graphics stack is extremely complex with fragmented and intricate software components. As a result, it’s quite exhausting to fully grasp its core idea and principles. This has posed significant challenges to my recent work on graphics virtualization of Android. Fortunately, after days of struggling with its sources and prior technical posts, I may have obtained some levels of understandings regarding the framework of the Linux graphics stack. SO, before I completely lose track of the story and to benefit other unfortunate comrades, I decide to document everything here in this post.]]></summary></entry><entry><title type="html">Importing Linux Kernel Code to VS Code</title><link href="https://www.linhao.me/posts/import-kernel" rel="alternate" type="text/html" title="Importing Linux Kernel Code to VS Code" /><published>2020-10-16T00:00:00-07:00</published><updated>2020-10-16T00:00:00-07:00</updated><id>https://www.linhao.me/posts/Import-Kernel</id><content type="html" xml:base="https://www.linhao.me/posts/import-kernel"><![CDATA[<p>In this post I’ll take you to import kernel code to VS Code for a nice code viewing and developing experience. You can say goodbye to CLion which eats your memory without mercy. This tutorial will be based on the Android-x86 source tree which contains Linux kernel almost identical to the upstream kernel.</p>

<ul>
  <li>The first step is installing VS Code of course. I’ll simply skip this step for any experienced developers.</li>
  <li>Now search and install the C/C++ Extension in VS Code’s extension market.</li>
  <li>Build the kernel before you do anything else.</li>
  <li>Use a magical script named <code class="language-plaintext highlighter-rouge">gen_compile_commands.py</code> in the <code class="language-plaintext highlighter-rouge">kernel/script</code> file. If it does not exist, you can find it in the upstream Linux kernel repository (https://github.com/torvalds/linux/blob/master/scripts/gen_compile_commands.py).</li>
  <li>Run the command by <code class="language-plaintext highlighter-rouge">python gen_compile_commands.py -d ${path-to-kernel-build-directory}</code>. In the case of Android-x86, the corresponding kernel build path is <code class="language-plaintext highlighter-rouge">out/target/product/{x86_64}/obj/kernel</code> ({} denotes that this name can vary depending on your chosen build target).</li>
  <li>After that you can find you <code class="language-plaintext highlighter-rouge">compile_commands.json</code> in the kernel build path.</li>
  <li>Now go back to VS Code and enter <code class="language-plaintext highlighter-rouge">Ctrl+Shift+P</code> to initiate command execution.</li>
  <li>Enter <code class="language-plaintext highlighter-rouge">&gt;C/C++: Edit Configurations (UI)</code> (you should get prompted even if you have not finished the command).</li>
  <li>Now open <code class="language-plaintext highlighter-rouge">Advanced Settings</code> and find the <code class="language-plaintext highlighter-rouge">Compile commands</code> setting.</li>
  <li>Enter the path to your <code class="language-plaintext highlighter-rouge">compile_commands.json</code> and you are good to go! Enjoy  your VS Code journey.</li>
</ul>]]></content><author><name>Hao Lin</name><email>linhaomails@gmail.com</email></author><category term="Kernel" /><category term="Code" /><summary type="html"><![CDATA[In this post I’ll take you to import kernel code to VS Code for a nice code viewing and developing experience. You can say goodbye to CLion which eats your memory without mercy. This tutorial will be based on the Android-x86 source tree which contains Linux kernel almost identical to the upstream kernel.]]></summary></entry><entry><title type="html">Write A Demo Android Kernel Driver</title><link href="https://www.linhao.me/posts/demo-driver" rel="alternate" type="text/html" title="Write A Demo Android Kernel Driver" /><published>2020-10-07T06:57:45-07:00</published><updated>2020-10-07T06:57:45-07:00</updated><id>https://www.linhao.me/posts/Write-A-Demo-Android-Kernel-Driver</id><content type="html" xml:base="https://www.linhao.me/posts/demo-driver"><![CDATA[<p>This post will talk about how to write a small “hello-world” driver in Android kernel.</p>

<h2 id="environment">Environment</h2>

<ul>
  <li>Android version: Android-x86 (q-x86, x86_64_userdebug)</li>
  <li>QEMU: 5.1</li>
  <li>Host: Ubuntu 20.04</li>
</ul>

<h2 id="a-demo-driver">A Demo Driver</h2>

<ul>
  <li>
    <p>Create a new directory in ${android-source-root}/kernel/drivers named <code class="language-plaintext highlighter-rouge">hello</code></p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">cd ${android-source-root}/kernel/drivers/hello</code></p>
  </li>
  <li>
    <p>Create a new C file named <code class="language-plaintext highlighter-rouge">hello.c</code>, with the following content:</p>

    <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;linux/module.h&gt;</span><span class="c1">  </span><span class="cp">
#include</span> <span class="cpf">&lt;linux/kernel.h&gt;</span><span class="c1">  </span><span class="cp">
#include</span> <span class="cpf">&lt;linux/fs.h&gt;</span><span class="c1">  </span><span class="cp">
#include</span> <span class="cpf">&lt;linux/miscdevice.h&gt;</span><span class="c1">  </span><span class="cp">
</span>  
<span class="n">MODULE_LICENSE</span><span class="p">(</span><span class="s">"GPL"</span><span class="p">);</span>  
<span class="n">MODULE_AUTHOR</span><span class="p">(</span><span class="s">"AndyLin"</span><span class="p">);</span>  
<span class="n">MODULE_DESCRIPTION</span><span class="p">(</span><span class="s">"Hello Kernel Device"</span><span class="p">);</span>  
<span class="n">MODULE_VERSION</span><span class="p">(</span><span class="s">"1.0"</span><span class="p">);</span>  
  
<span class="cp">#define CMD_COMMAND 0x1336  
</span>  
<span class="kt">long</span> <span class="nf">hello_ioctl</span><span class="p">(</span><span class="k">struct</span> <span class="n">file</span> <span class="o">*</span><span class="n">filp</span><span class="p">,</span>
    <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">cmd</span><span class="p">,</span>  
    <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">arg</span><span class="p">){</span>  
    <span class="k">switch</span><span class="p">(</span><span class="n">cmd</span><span class="p">){</span>  
    <span class="k">case</span> <span class="n">CMD_COMMAND</span><span class="p">:</span>  
        <span class="n">printk</span><span class="p">(</span><span class="s">"Hello Module hello_ioctl() exced"</span><span class="p">);</span>  
        <span class="k">break</span><span class="p">;</span>  
    <span class="nl">default:</span>  
        <span class="n">printk</span><span class="p">(</span><span class="s">"Hello Module unknown ioctl cmd"</span><span class="p">);</span>  
    <span class="p">}</span>  
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>  
<span class="p">}</span>  
  
<span class="k">struct</span> <span class="n">file_operations</span> <span class="n">hello_fops</span> <span class="o">=</span> <span class="p">{</span>
    <span class="nl">unlocked_ioctl:</span> <span class="n">hello_ioctl</span>  
<span class="p">};</span>  
  
<span class="k">static</span> <span class="k">struct</span> <span class="n">miscdevice</span> <span class="n">hello_device</span> <span class="o">=</span> <span class="p">{</span>
    <span class="nl">minor:</span> <span class="n">MISC_DYNAMIC_MINOR</span><span class="p">,</span>  
    <span class="nl">name:</span> <span class="s">"hello"</span><span class="p">,</span>  
    <span class="nl">fops:</span> <span class="o">&amp;</span><span class="n">hello_fops</span><span class="p">,</span>  
    <span class="nl">mode:</span> <span class="mi">777</span>
<span class="p">};</span>  
  
<span class="k">static</span> <span class="kt">int</span> <span class="nf">hello_begin</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>  
    <span class="kt">int</span> <span class="n">ret</span><span class="p">;</span>  
    <span class="n">ret</span> <span class="o">=</span> <span class="n">misc_register</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hello_device</span><span class="p">);</span>
    <span class="k">if</span><span class="p">(</span><span class="n">ret</span><span class="p">)</span>  
        <span class="n">printk</span><span class="p">(</span><span class="s">"Failed to register misc device"</span><span class="p">);</span>  
    <span class="k">else</span>  
        <span class="n">printk</span><span class="p">(</span><span class="s">"Hello Module successfully loaded"</span><span class="p">);</span>  
  
    <span class="k">return</span> <span class="n">ret</span><span class="p">;</span>  
<span class="p">}</span>  
  
<span class="k">static</span> <span class="kt">void</span> <span class="nf">hello_exit</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>  
    <span class="n">misc_deregister</span><span class="p">(</span><span class="o">&amp;</span><span class="n">hello_device</span><span class="p">);</span>
    <span class="n">printk</span><span class="p">(</span><span class="s">"Hello module exit"</span><span class="p">);</span> 
    <span class="k">return</span><span class="p">;</span> 
<span class="p">}</span>  
  
<span class="n">module_init</span><span class="p">(</span><span class="n">hello_begin</span><span class="p">);</span>
<span class="n">module_exit</span><span class="p">(</span><span class="n">hello_exit</span><span class="p">);</span> 
</code></pre></div>    </div>
  </li>
  <li>
    <p>In the <code class="language-plaintext highlighter-rouge">${android-source-root}</code>, run the following commands to build the kernel first:</p>

    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">source </span>build/envsetup.sh
lunch android_x86_64-userdebug
make kernel <span class="nt">-j</span><span class="k">${</span><span class="nv">thread_num</span><span class="k">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>Create a <code class="language-plaintext highlighter-rouge">MakeFile</code> in the <code class="language-plaintext highlighter-rouge">hello</code> directory:</p>

    <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">obj</span><span class="o">-</span><span class="n">m</span> <span class="o">:=</span> <span class="n">hello</span><span class="p">.</span><span class="n">o</span>  
  
<span class="c1">// This is why we build kernel first -- in order to generate the kernel directory</span>
<span class="n">KERNELDIR</span> <span class="o">:=</span> <span class="err">$</span><span class="p">{</span><span class="n">android</span><span class="o">-</span><span class="n">source</span><span class="o">-</span><span class="n">root</span><span class="p">}</span><span class="o">/</span><span class="n">out</span><span class="o">/</span><span class="n">target</span><span class="o">/</span><span class="n">product</span><span class="o">/</span><span class="n">x86_64</span><span class="o">/</span><span class="n">obj</span><span class="o">/</span><span class="n">kernel</span><span class="o">/</span>
<span class="n">PWD</span> <span class="o">:=</span><span class="err">$</span><span class="p">(</span><span class="n">shell</span> <span class="n">pwd</span><span class="p">)</span>  
<span class="n">ARCH</span><span class="o">=</span><span class="n">x86_64</span>
<span class="n">CROSS_COMPILE</span><span class="o">=</span><span class="err">$</span><span class="p">{</span><span class="n">android</span><span class="o">-</span><span class="n">source</span><span class="o">-</span><span class="n">root</span><span class="p">}</span><span class="o">/</span><span class="n">prebuilts</span><span class="o">/</span><span class="n">gcc</span><span class="o">/</span><span class="n">linux</span><span class="o">-</span><span class="n">x86</span><span class="o">/</span><span class="n">x86</span><span class="o">/</span><span class="n">x86_64</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">android</span><span class="o">-</span><span class="mi">4</span><span class="p">.</span><span class="mi">9</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">x86_64</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">android</span><span class="o">-</span>  
<span class="n">CC</span><span class="o">=</span><span class="err">$</span><span class="p">(</span><span class="n">CROSS_COMPILE</span><span class="p">)</span><span class="n">gcc</span>  
<span class="n">LD</span><span class="o">=</span><span class="err">$</span><span class="p">(</span><span class="n">CROSS_COMPILE</span><span class="p">)</span><span class="n">ld</span>  
<span class="n">CFLAGS_MODULE</span><span class="o">=-</span><span class="n">fno</span><span class="o">-</span><span class="n">pic</span>  
  
<span class="n">all</span><span class="o">:</span>  
	<span class="n">make</span> <span class="o">-</span><span class="n">C</span> <span class="err">$</span><span class="p">(</span><span class="n">KERNELDIR</span><span class="p">)</span> <span class="n">ARCH</span><span class="o">=</span><span class="err">$</span><span class="p">(</span><span class="n">ARCH</span><span class="p">)</span> <span class="n">CROSS_COMPILE</span><span class="o">=</span><span class="err">$</span><span class="p">(</span><span class="n">CROSS_COMPILE</span><span class="p">)</span> <span class="n">M</span><span class="o">=</span><span class="err">$</span><span class="p">(</span><span class="n">PWD</span><span class="p">)</span> <span class="n">modules</span>
<span class="n">clean</span><span class="o">:</span>    
	<span class="n">rm</span> <span class="o">*</span><span class="p">.</span><span class="n">o</span> <span class="o">*</span><span class="p">.</span><span class="n">ko</span> <span class="o">*</span><span class="p">.</span><span class="n">mod</span><span class="p">.</span><span class="n">c</span> <span class="o">*</span><span class="p">.</span><span class="n">order</span> <span class="o">*</span><span class="p">.</span><span class="n">symvers</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>Now we can finally <code class="language-plaintext highlighter-rouge">make</code></p>
  </li>
  <li>
    <p>After <code class="language-plaintext highlighter-rouge">make</code>, you can see a <code class="language-plaintext highlighter-rouge">hello.ko</code> file in the directory</p>
  </li>
  <li>
    <p>Now you can build your Android-x86 and run it atop QEMU (you might run into compilation errors due to dirty <code class="language-plaintext highlighter-rouge">kernel</code> directory caused by <code class="language-plaintext highlighter-rouge">make kernel</code>; if that happens, you can first run <code class="language-plaintext highlighter-rouge">make mrproper</code> in the <code class="language-plaintext highlighter-rouge">kernel</code> directory before building the entire system)</p>
  </li>
  <li>
    <p>Connect to the system via <code class="language-plaintext highlighter-rouge">adb</code> (note that you should first turn on your WiFi in Android, and you can connect to it by running <code class="language-plaintext highlighter-rouge">adb connect localhost:4444</code>, if your QEMU booting command contains the following: <code class="language-plaintext highlighter-rouge">-net nic -net user,hostfwd=tcp::4444-:555</code></p>
  </li>
  <li>
    <p>Use <code class="language-plaintext highlighter-rouge">adb</code> to push the <code class="language-plaintext highlighter-rouge">hello.ko</code> to Android’s any directory as you please</p>
  </li>
  <li>
    <p>In <code class="language-plaintext highlighter-rouge">adb shell</code>, run <code class="language-plaintext highlighter-rouge">insmod ${path-to-the-hello.ko-file}</code></p>
  </li>
  <li>
    <p>Now you are all done! You can use <code class="language-plaintext highlighter-rouge">dmesg | grep Hello</code> to check the output in the driver’s code</p>
  </li>
</ul>]]></content><author><name>Hao Lin</name><email>linhaomails@gmail.com</email></author><category term="Android" /><category term="Kernel" /><category term="Driver" /><summary type="html"><![CDATA[This post will talk about how to write a small “hello-world” driver in Android kernel.]]></summary></entry><entry><title type="html">VIRTIO-GPU Work Flow</title><link href="https://www.linhao.me/posts/virtio-gpu" rel="alternate" type="text/html" title="VIRTIO-GPU Work Flow" /><published>2020-07-15T10:51:04-07:00</published><updated>2020-07-15T10:51:04-07:00</updated><id>https://www.linhao.me/posts/VIRTIO-GPU-Workflow</id><content type="html" xml:base="https://www.linhao.me/posts/virtio-gpu"><![CDATA[<p>virtio-gpu属于virtio系列I/O虚拟化方案，采用类虚拟化设计，Guest端需要内置virtio驱动（&gt;Linux 4.4均已配置，Android x86源码<code class="language-plaintext highlighter-rouge">kernel/drivers/virtio</code>中可查看）。virtio驱动的基本结构为前后端设计，前端为Guest OS驱动，后端为模拟器端逻辑，前后端通过virtqueue队列（由环形缓冲区vring实现）进行数据交换。</p>

<p><img src="https://www.linhao.me/images/posts/virtio.gif" /></p>

<h2 id="guest端">Guest端</h2>

<ul>
  <li>
    <p>操作系统启动时遍历PCI总线树，virtio-gpu作为一个PCI设备被OS检测到并注册</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">virtio_pci_common.c</code>中的<code class="language-plaintext highlighter-rouge">virtio_pci_probe</code>函数会根据PCI设备号创建<code class="language-plaintext highlighter-rouge">virtio_pci_device</code>，并将其添加到<code class="language-plaintext highlighter-rouge">virtio_bus</code></p>
  </li>
  <li>
    <p>在<code class="language-plaintext highlighter-rouge">/sys/bus/virtio/drivers</code>中可以看到<code class="language-plaintext highlighter-rouge">virtio-gpu</code>设备</p>
  </li>
  <li>
    <p>运行一个OpenGL应用，此时该应用会调用OpenGL库进行图形绘制</p>
  </li>
  <li>
    <p>OpenGL指令会送至内核中的DRM（Direct Rendering Management）图形驱动，DRM驱动向GPU下达相应的图形指令（着色、光栅化、贴图什么的），具体的OpenGL-&gt;DRM-&gt;GPU流程看这里https://www.cnblogs.com/shoemaker/p/linux_graphics01.html</p>
  </li>
  <li>
    <p>Guest端的virtio-gpu设备截获DRM的相关指令，然后转换分解为自己定义的一套指令（具体指令类型见后）</p>
  </li>
  <li>
    <p>将指令放到virtqueue中，然后告知QEMU：</p>

    <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">virtqueue_notify</span>
    <span class="n">vq</span><span class="o">-&gt;</span><span class="n">notify</span><span class="p">(</span><span class="n">_vq</span><span class="p">)</span> <span class="o">&lt;--</span> <span class="n">vp_notify</span>
    <span class="n">iowrite16</span><span class="p">(</span><span class="n">vq</span><span class="o">-&gt;</span><span class="n">index</span><span class="p">,</span> <span class="n">vp_dev</span><span class="o">-&gt;</span><span class="n">ioaddr</span> <span class="o">+</span> <span class="n">VIRTIO_PCI_QUEUE_NOTIFY</span><span class="p">)</span>
</code></pre></div>    </div>

    <p>方法是Guest写I/O寄存器，触发VM Exit进入到KVM中处理，进入到Host端流程</p>
  </li>
</ul>

<h2 id="host端">Host端</h2>

<ul>
  <li>
    <p>KVM无法处理而退出，处理退出原因发现是<code class="language-plaintext highlighter-rouge">KVM_EXIT_IO</code>，进而调用相应的Handle函数</p>

    <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">kvm_cpu_exec</span><span class="p">(</span><span class="n">CPUArchState</span> <span class="o">*</span><span class="n">env</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">do</span> <span class="p">{</span>
        <span class="n">run_ret</span> <span class="o">=</span> <span class="n">kvm_vcpu_ioctl</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">KVM_RUN</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
        <span class="k">switch</span> <span class="p">(</span><span class="n">run</span><span class="o">-&gt;</span><span class="n">exit_reason</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* 处理退出原因 */</span>
        <span class="k">case</span> <span class="n">KVM_EXIT_IO</span><span class="p">:</span>
            <span class="n">kvm_handle_io</span><span class="p">();</span>
            <span class="p">...</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">kvm_handle_io</code>通过<code class="language-plaintext highlighter-rouge">write eventfd</code>将等待<code class="language-plaintext highlighter-rouge">poll</code>的QEMU主线程唤醒，然后一步步走到virtio-gpu的I/O handlers。其中与virtio-gpu有关的virtqueue有两个：<code class="language-plaintext highlighter-rouge">ctrl_vq</code>负责交换图形命令，<code class="language-plaintext highlighter-rouge">cursor_vq</code>负责交换光标信息；</p>

    <p>从而相应也有两个handlers：<code class="language-plaintext highlighter-rouge">virtio_gpu_handle_ctrl</code>和<code class="language-plaintext highlighter-rouge">virtio_gpu_handle_cursor</code>，两个函数在<code class="language-plaintext highlighter-rouge">hw/display/virtio-gpu.c</code>；</p>

    <p>handler的注册在GCC编译时设定为在main函数前执行，位于<code class="language-plaintext highlighter-rouge">virtio_gpu_device_realize</code>函数中，具体见QOM编程模型https://blog.csdn.net/u011364612/article/details/53581411和相关的GCC trickhttps://blog.csdn.net/u011364612/article/details/53581501</p>

    <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">main</span>  <span class="n">main_loop</span> <span class="n">main_loop_wait</span>
    <span class="n">os_host_main_loop_wait</span>
        <span class="n">glib_pollfds_poll</span>
            <span class="n">g_main_context_dispatch</span> 
                <span class="n">aio_ctx_dispatch</span>    <span class="n">aio_dispatch</span>
                    <span class="n">virtio_queue_host_notifier_read</span>
                        <span class="n">virtio_queue_notify_vq</span> 
                            <span class="n">virtio_gpu_handle_ctrl</span> <span class="n">virtio_gpu_handle_cursor</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>以<code class="language-plaintext highlighter-rouge">virtio_gpu_handle_ctrl</code>继续向下，到<code class="language-plaintext highlighter-rouge">virtio_gpu_handle_ctrl</code>，其首先从virtqueue中取出Guest端传来的命令，然后进行处理</p>

    <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">virtio_gpu_process_cmdq</span><span class="p">(</span><span class="n">VirtIOGPU</span> <span class="o">*</span><span class="n">g</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">struct</span> <span class="n">virtio_gpu_ctrl_command</span> <span class="o">*</span><span class="n">cmd</span><span class="p">;</span>
  
    <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">QTAILQ_EMPTY</span><span class="p">(</span><span class="o">&amp;</span><span class="n">g</span><span class="o">-&gt;</span><span class="n">cmdq</span><span class="p">))</span> <span class="p">{</span>
        <span class="cm">/* 取命令 */</span>
        <span class="n">cmd</span> <span class="o">=</span> <span class="n">QTAILQ_FIRST</span><span class="p">(</span><span class="o">&amp;</span><span class="n">g</span><span class="o">-&gt;</span><span class="n">cmdq</span><span class="p">);</span>
          
        <span class="p">...</span>
  
        <span class="cm">/* 处理命令 */</span>
        <span class="n">VIRGL</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">virtio_gpu_virgl_process_cmd</span><span class="p">,</span> <span class="n">virtio_gpu_simple_process_cmd</span><span class="p">,</span>
              <span class="n">g</span><span class="p">,</span> <span class="n">cmd</span><span class="p">);</span>
        <span class="n">QTAILQ_REMOVE</span><span class="p">(</span><span class="o">&amp;</span><span class="n">g</span><span class="o">-&gt;</span><span class="n">cmdq</span><span class="p">,</span> <span class="n">cmd</span><span class="p">,</span> <span class="n">next</span><span class="p">);</span>
        <span class="p">...</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>    </div>

    <p><code class="language-plaintext highlighter-rouge">virtio_gpu_ctrl_command</code>为Guest端解析DRM驱动指令重新构成的virtio命令格式</p>

    <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">virtio_gpu_ctrl_command</span> <span class="p">{</span>
    <span class="n">VirtQueueElement</span> <span class="n">elem</span><span class="p">;</span>                       <span class="c1">// 取出的命令被映射到这里</span>
    <span class="n">VirtQueue</span> <span class="o">*</span><span class="n">vq</span><span class="p">;</span>                               <span class="c1">// Host/Guest共享的队列</span>
    <span class="k">struct</span> <span class="n">virtio_gpu_ctrl_hdr</span> <span class="n">cmd_hdr</span><span class="p">;</span>          <span class="c1">// 命令指定的header，指定了命令类型</span>
    <span class="kt">uint32_t</span> <span class="n">error</span><span class="p">;</span>
    <span class="n">bool</span> <span class="n">finished</span><span class="p">;</span>
    <span class="n">QTAILQ_ENTRY</span><span class="p">(</span><span class="n">virtio_gpu_ctrl_command</span><span class="p">)</span> <span class="n">next</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>处理命令的<code class="language-plaintext highlighter-rouge">VIRGL</code>宏如下，这里用了<code class="language-plaintext highlighter-rouge">libvirglrenderer</code></p>

    <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#ifdef CONFIG_VIRGL
#include</span> <span class="cpf">&lt;virglrenderer.h&gt;</span><span class="cp">
#define VIRGL(_g, _virgl, _simple, ...)                     \
    do {                                                    \
        if (_g-&gt;parent_obj.use_virgl_renderer) {            \
            _virgl(__VA_ARGS__);                            \
        } else {                                            \
            _simple(__VA_ARGS__);                           \
        }                                                   \
    } while (0)
#else
#define VIRGL(_g, _virgl, _simple, ...)                 \
    do {                                                \
        _simple(__VA_ARGS__);                           \
    } while (0)
#endif
</span></code></pre></div>    </div>

    <p>有<code class="language-plaintext highlighter-rouge">virgl</code>支持则能进行3D加速，否则只能进行2D加速，在Windows上目前直接使用<code class="language-plaintext highlighter-rouge">virtio-vga</code>会黑屏可能是已经没有所谓的2D加速这种东西了吧</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">_simple</code>对应<code class="language-plaintext highlighter-rouge">virtio_gpu_simple_process_cmd</code>，3D加速对应的函数差不多</p>

    <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">void</span> <span class="nf">virtio_gpu_simple_process_cmd</span><span class="p">(</span><span class="n">VirtIOGPU</span> <span class="o">*</span><span class="n">g</span><span class="p">,</span>
                                          <span class="k">struct</span> <span class="n">virtio_gpu_ctrl_command</span> <span class="o">*</span><span class="n">cmd</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">VIRTIO_GPU_FILL_CMD</span><span class="p">(</span><span class="n">cmd</span><span class="o">-&gt;</span><span class="n">cmd_hdr</span><span class="p">);</span>
    <span class="n">virtio_gpu_ctrl_hdr_bswap</span><span class="p">(</span><span class="o">&amp;</span><span class="n">cmd</span><span class="o">-&gt;</span><span class="n">cmd_hdr</span><span class="p">);</span>
  
    <span class="k">switch</span> <span class="p">(</span><span class="n">cmd</span><span class="o">-&gt;</span><span class="n">cmd_hdr</span><span class="p">.</span><span class="n">type</span><span class="p">)</span> <span class="p">{</span> <span class="c1">//type就是命令类型</span>
    <span class="k">case</span> <span class="n">VIRTIO_GPU_CMD_GET_DISPLAY_INFO</span><span class="p">:</span>
        <span class="n">virtio_gpu_get_display_info</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">cmd</span><span class="p">);</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="k">case</span> <span class="n">VIRTIO_GPU_CMD_GET_EDID</span><span class="p">:</span>
        <span class="n">virtio_gpu_get_edid</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">cmd</span><span class="p">);</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="k">case</span> <span class="n">VIRTIO_GPU_CMD_RESOURCE_CREATE_2D</span><span class="p">:</span>          <span class="c1">// 创建2D资源，仅有长宽和格式，没有像素信息，像素信息单独传</span>
        <span class="n">virtio_gpu_resource_create_2d</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">cmd</span><span class="p">);</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="k">case</span> <span class="n">VIRTIO_GPU_CMD_RESOURCE_UNREF</span><span class="p">:</span>
        <span class="n">virtio_gpu_resource_unref</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">cmd</span><span class="p">);</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="k">case</span> <span class="n">VIRTIO_GPU_CMD_RESOURCE_FLUSH</span><span class="p">:</span>             <span class="c1">// 将处理好的图形送到显示器</span>
        <span class="n">virtio_gpu_resource_flush</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">cmd</span><span class="p">);</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="k">case</span> <span class="n">VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D</span><span class="p">:</span>        <span class="c1">// 交给Host的pixman库进行图形处理，比如明暗、光栅化处理</span>
        <span class="n">virtio_gpu_transfer_to_host_2d</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">cmd</span><span class="p">);</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="k">case</span> <span class="n">VIRTIO_GPU_CMD_SET_SCANOUT</span><span class="p">:</span>
        <span class="n">virtio_gpu_set_scanout</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">cmd</span><span class="p">);</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="k">case</span> <span class="n">VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING</span><span class="p">:</span>    <span class="c1">// 获取像素信息并和2D资源绑定</span>
        <span class="n">virtio_gpu_resource_attach_backing</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">cmd</span><span class="p">);</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="k">case</span> <span class="n">VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING</span><span class="p">:</span>
        <span class="n">virtio_gpu_resource_detach_backing</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">cmd</span><span class="p">);</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="nl">default:</span>
        <span class="n">cmd</span><span class="o">-&gt;</span><span class="n">error</span> <span class="o">=</span> <span class="n">VIRTIO_GPU_RESP_ERR_UNSPEC</span><span class="p">;</span>
        <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">cmd</span><span class="o">-&gt;</span><span class="n">finished</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">virtio_gpu_ctrl_response_nodata</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">cmd</span><span class="p">,</span> <span class="n">cmd</span><span class="o">-&gt;</span><span class="n">error</span> <span class="o">?</span> <span class="n">cmd</span><span class="o">-&gt;</span><span class="n">error</span> <span class="o">:</span>
                                        <span class="n">VIRTIO_GPU_RESP_OK_NODATA</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>    </div>

    <p>具体的各流程和结构体解释见https://blog.csdn.net/huang987246510/article/details/106254294/</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">pixman</code>库处理完后由<code class="language-plaintext highlighter-rouge">dpy_gfx_update</code>刷新显示器</p>
  </li>
</ul>]]></content><author><name>Hao Lin</name><email>linhaomails@gmail.com</email></author><category term="VIRTIO" /><category term="Emulator" /><category term="QEMU" /><category term="GPU" /><summary type="html"><![CDATA[virtio-gpu属于virtio系列I/O虚拟化方案，采用类虚拟化设计，Guest端需要内置virtio驱动（&gt;Linux 4.4均已配置，Android x86源码kernel/drivers/virtio中可查看）。virtio驱动的基本结构为前后端设计，前端为Guest OS驱动，后端为模拟器端逻辑，前后端通过virtqueue队列（由环形缓冲区vring实现）进行数据交换。]]></summary></entry></feed>