First working version of vsock_test

Put this here until we have our own AOSP git repo.

Files in device/google/cuttlefish_kernel should be added to the
corresponding AOSP repo.
diff --git a/device/google/cuttlefish_kernel/5.4-arm64/initramfs.img b/device/google/cuttlefish_kernel/5.4-arm64/initramfs.img
new file mode 100644
index 0000000..0928dce
--- /dev/null
+++ b/device/google/cuttlefish_kernel/5.4-arm64/initramfs.img
Binary files differ
diff --git a/device/google/cuttlefish_kernel/Android.bp b/device/google/cuttlefish_kernel/Android.bp
new file mode 100644
index 0000000..02d4981
--- /dev/null
+++ b/device/google/cuttlefish_kernel/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_prebuilt_binary {
+    name: "extract-vmlinux",
+    srcs: ["scripts/extract-vmlinux"],
+    defaults: ["cuttlefish_host_only"],
+}
+
+filegroup {
+    name: "cf-kernel-5.4-arm64",
+    srcs: [ "5.4-arm64/kernel" ],
+}
+
+filegroup {
+    name: "cf-initramfs-5.4-arm64",
+    srcs: [ "5.4-arm64/initramfs.img" ],
+}
diff --git a/system/virt/tests/hostside/Android.bp b/system/virt/tests/hostside/Android.bp
new file mode 100644
index 0000000..fe4b8b5
--- /dev/null
+++ b/system/virt/tests/hostside/Android.bp
@@ -0,0 +1,70 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+kernel_version = "5.4"
+
+java_test_host {
+    name: "VirtHostTestCases",
+    srcs: [ "java/**/*.java" ],
+    test_suites: [
+        "general-tests",
+    ],
+    libs: [
+        "tradefed",
+    ],
+    java_resources: [
+        ":virt-host-test-host",
+        ":virt-host-test-kernel-arm64",
+        ":virt-host-test-initramfs-arm64",
+    ],
+}
+
+genrule_defaults {
+    name: "virt-host-test-kernel-defaults",
+    tools: [ "lz4" ],
+    cmd: "$(location lz4) -d $(in) $(out)",
+}
+
+genrule {
+    name: "virt-host-test-kernel-arm64",
+    defaults: [ "virt-host-test-kernel-defaults" ],
+    srcs: [ ":cf-kernel-" + kernel_version + "-arm64" ],
+    out: [ "kernel_arm64-v8a" ],
+}
+
+genrule {
+    name: "virt-host-test-initramfs-generic",
+    tools: [ "mkbootfs" ],
+    tool_files: [ "scripts/build_initramfs.sh" ],
+    srcs: [ ":virt-host-test-guest" ],
+    cmd: "GEN_DIR=$(genDir) " +
+         "OUT=$(out) " +
+         "MKBOOTFS=$(location mkbootfs) " +
+         "$(location scripts/build_initramfs.sh) $(in)",
+    out: [ "initramfs_generic.cpio" ],
+}
+
+genrule {
+    name: "virt-host-test-initramfs-arm64",
+    srcs: [
+        ":cf-initramfs-" + kernel_version + "-arm64",
+        ":virt-host-test-initramfs-generic",
+    ],
+    out: [ "initramfs_arm64-v8a.cpio" ],
+    tools: [ "lz4" ],
+    tool_files: [ "scripts/concat_initramfs.sh" ],
+    cmd: "OUT=$(out) " +
+         "LZ4=$(location lz4) " +
+         "$(location scripts/concat_initramfs.sh) $(in)",
+}
diff --git a/system/virt/tests/hostside/AndroidTest.xml b/system/virt/tests/hostside/AndroidTest.xml
new file mode 100644
index 0000000..1385a0f
--- /dev/null
+++ b/system/virt/tests/hostside/AndroidTest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<configuration description="Config for Virtualization host tests">
+    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+        <option name="jar" value="VirtHostTestCases.jar" />
+        <option name="runtime-hint" value="2m" />
+    </test>
+</configuration>
diff --git a/system/virt/tests/hostside/java/android/virt/test/VirtHostTestCase.java b/system/virt/tests/hostside/java/android/virt/test/VirtHostTestCase.java
new file mode 100644
index 0000000..f4ef9ae
--- /dev/null
+++ b/system/virt/tests/hostside/java/android/virt/test/VirtHostTestCase.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.virt.test;
+
+import static org.junit.Assert.*;
+
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IAbi;
+import com.android.tradefed.testtype.IAbiReceiver;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.log.LogUtil.CLog;
+
+import org.apache.commons.compress.compressors.CompressorStreamFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+
+public abstract class VirtHostTestCase extends DeviceTestCase
+                                       implements IBuildReceiver, IAbiReceiver {
+
+    private static final String DEVICE_TMP_DIR = "/data/local/tmp/";
+    private static final String KERNEL_DEVICE_PATH = DEVICE_TMP_DIR + "/virt-host-test-kernel";
+    private static final String RAMDISK_DEVICE_PATH = DEVICE_TMP_DIR + "/virt-host-test-initramfs";
+    private static final String HOST_BINARY_DEVICE_PATH = DEVICE_TMP_DIR + "/virt-host-test-host";
+
+    private static final int EXIT_SUCCESS = 0;
+    private static final int EXIT_FAILURE = 1;
+
+    private static final String CMDLINE_TEST_NAME_PARAM = "virt_test_name";
+
+    private static final int CID_RESERVED = 2;
+
+    private IBuildInfo mBuildInfo = null;
+    private IAbi mAbi = null;
+
+    /**
+     * Waits for device to be online, marks the most recent boottime of the device
+     */
+    @Before
+    public void setUp() throws Exception {
+        getDevice().waitForDeviceAvailable();
+    }
+
+    protected boolean enableRoot() throws Exception {
+        return getDevice().enableAdbRoot();
+    }
+
+    protected boolean disableRoot() throws Exception {
+        return getDevice().disableAdbRoot();
+    }
+
+    protected String runCommand(String cmd) throws Exception {
+        return getDevice().executeShellCommand(cmd);
+    }
+
+    protected int runCommandGetExitCode(String cmd) throws Exception {
+        String output = runCommand("(" + cmd + ") > /dev/null 2>&1; echo $?").trim();
+
+        try {
+            return Integer.parseInt(output);
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException(String.format(
+                    "Could not get the exit status (%s) for '%s'.", output, cmd));
+        }
+    }
+
+    private void extractResource(String resFilePath, File file) throws Exception {
+        try (InputStream in = this.getClass().getResourceAsStream(resFilePath);
+            OutputStream out = new BufferedOutputStream(new FileOutputStream(file))) {
+            if (in == null) {
+                throw new IllegalArgumentException("Resource not found: " + resFilePath);
+            }
+            byte[] buf = new byte[65536];
+            int chunkSize;
+            while ((chunkSize = in.read(buf)) != -1) {
+                out.write(buf, 0, chunkSize);
+            }
+        }
+    }
+
+    private void pushResource(String resFilePath, String deviceFilePath, boolean decompress)
+            throws Exception {
+        File resFile = File.createTempFile("VirtHostTestResource", "");
+        try {
+            extractResource(resFilePath, resFile);
+            getDevice().pushFile(resFile, deviceFilePath);
+        } finally {
+            resFile.delete();
+        }
+    }
+
+    private void pushResource(String resFilePath, String deviceFilePath) throws Exception {
+        pushResource(resFilePath, deviceFilePath, /* decompress */ false);
+    }
+
+    private boolean deleteResource(String deviceFilePath) throws Exception {
+        return runCommandGetExitCode(String.format("rm %s", deviceFilePath)) == EXIT_SUCCESS;
+    }
+
+    private boolean makeExecutable(String deviceFilePath) throws Exception {
+        return runCommandGetExitCode(String.format("chmod +x %s", deviceFilePath)) == EXIT_SUCCESS;
+    }
+
+    protected boolean isVirtEnabled() throws Exception {
+        return (runCommandGetExitCode("ls /dev/kvm") == EXIT_SUCCESS) &&
+               (runCommandGetExitCode("which crosvm") == EXIT_SUCCESS);
+    }
+
+    private String getKernelResourcePath() {
+        return String.format("/kernel_%s", mAbi.getName());
+    }
+
+    private String getRamdiskResourcePath() {
+        return String.format("/initramfs_%s.cpio", mAbi.getName());
+    }
+
+    private String getHostBinaryResourcePath() {
+        return String.format("/host_binary");
+    }
+
+    protected void prepareGuestVM() throws Exception {
+        if (!isVirtEnabled()) {
+            throw new IllegalStateException("Virtualization is not enabled on the target device");
+        }
+
+        if (!enableRoot()) {
+            throw new IllegalStateException("Cannot enable root on the target device");
+        }
+
+        pushResource(getKernelResourcePath(), KERNEL_DEVICE_PATH);
+        pushResource(getRamdiskResourcePath(), RAMDISK_DEVICE_PATH);
+        pushResource(getHostBinaryResourcePath(), HOST_BINARY_DEVICE_PATH);
+
+        if (!makeExecutable(HOST_BINARY_DEVICE_PATH)) {
+            throw new IllegalStateException("Could not make host binary executable");
+        }
+    }
+
+    protected void finishGuestVM() throws Exception {
+        deleteResource(KERNEL_DEVICE_PATH);
+        deleteResource(RAMDISK_DEVICE_PATH);
+        deleteResource(HOST_BINARY_DEVICE_PATH);
+        disableRoot();
+    }
+
+    protected void runGuestVM(String testName, Integer cid) throws Exception {
+        ArrayList<String> cmd = new ArrayList<>();
+        String params = String.format("%s=%s", CMDLINE_TEST_NAME_PARAM, testName);
+
+        cmd.add("crosvm");
+        cmd.add("run");
+
+        cmd.add("--disable-sandbox");
+
+        if (cid != null) {
+            if (cid > CID_RESERVED) {
+                cmd.add("--cid");
+                cmd.add(cid.toString());
+            } else {
+                throw new IllegalArgumentException("Invalid CID");
+            }
+        }
+
+        cmd.add("--params");
+        cmd.add("\"" + params.replace("\"", "\\\"") + "\"");
+
+        cmd.add("--initrd");
+        cmd.add(RAMDISK_DEVICE_PATH);
+
+        cmd.add(KERNEL_DEVICE_PATH);
+
+        runCommand(String.join(" ", cmd));
+    }
+
+    protected boolean runHostBinary(String testName) throws Exception {
+        ArrayList<String> cmd = new ArrayList<>();
+
+        cmd.add(HOST_BINARY_DEVICE_PATH);
+        cmd.add(testName);
+
+        return runCommandGetExitCode(String.join(" ", cmd)) == EXIT_SUCCESS;
+    }
+
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        // Get the build, this is used to access the APK.
+        mBuildInfo = buildInfo;
+    }
+
+    @Override
+    public void setAbi(IAbi abi) {
+        mAbi = abi;
+    }
+}
diff --git a/system/virt/tests/hostside/java/android/virt/test/VsockTest.java b/system/virt/tests/hostside/java/android/virt/test/VsockTest.java
new file mode 100644
index 0000000..7c44bb1
--- /dev/null
+++ b/system/virt/tests/hostside/java/android/virt/test/VsockTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.virt.test;
+
+import org.junit.Test;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import com.android.tradefed.log.LogUtil.CLog;
+
+public class VsockTest extends VirtHostTestCase {
+    private static final String LOG_TAG = "VsockTest";
+
+    private static final String TEST_NAME = "VsockServer";
+    private static final Long TEST_TIMEOUT = 20L;
+    private static final TimeUnit TEST_TIMEOUT_UNIT = TimeUnit.SECONDS;
+
+    @Test
+    public void testVsockServer() throws Exception {
+        ExecutorService executor = Executors.newFixedThreadPool(2);
+
+        prepareGuestVM();
+        try {
+            Future<?> vmTask = executor.submit(new GuestVmTask());
+            Future<Void> hostTask = executor.submit(new HostBinaryTask());
+            hostTask.get(TEST_TIMEOUT, TEST_TIMEOUT_UNIT);
+        } finally {
+            executor.shutdown();
+            executor.awaitTermination(TEST_TIMEOUT, TEST_TIMEOUT_UNIT);
+            finishGuestVM();
+        }
+    }
+
+    class GuestVmTask implements Callable<Void> {
+        @Override
+        public Void call() throws Exception {
+            runGuestVM(TEST_NAME, /* CID */ 42);
+            return null;
+        }
+    }
+
+    class HostBinaryTask implements Callable<Void> {
+        @Override
+        public Void call() throws Exception {
+            while (true) {
+                Thread.sleep(2000);
+                CLog.w("Executing host binary...");
+                if (runHostBinary(TEST_NAME)) {
+                    return null;
+                }
+            }
+        }
+    }
+}
diff --git a/system/virt/tests/hostside/native/Android.bp b/system/virt/tests/hostside/native/Android.bp
new file mode 100644
index 0000000..cd46f5e
--- /dev/null
+++ b/system/virt/tests/hostside/native/Android.bp
@@ -0,0 +1,40 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary {
+    name: "virt-host-test-guest",
+    srcs: [ "guest/**/*.cc" ],
+    local_include_dirs: [ "./include" ],
+    static_executable: true,
+    installable: false,
+    static_libs: [
+        "libbase",
+        "liblog",
+        "libmodprobe",
+    ],
+    stem: "init",
+}
+
+cc_binary {
+    name: "virt-host-test-host",
+    srcs: [ "host/**/*.cc" ],
+    local_include_dirs: [ "./include" ],
+    static_executable: true,
+    installable: false,
+    static_libs: [
+        "libbase",
+        "liblog",
+    ],
+    stem: "host_binary",
+}
diff --git a/system/virt/tests/hostside/native/guest/main.cc b/system/virt/tests/hostside/native/guest/main.cc
new file mode 100644
index 0000000..ec6d062
--- /dev/null
+++ b/system/virt/tests/hostside/native/guest/main.cc
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+
+#include <cstdlib>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <modprobe/modprobe.h>
+#include <private/android_filesystem_config.h>
+
+#include "virt_test.h"
+
+static constexpr const char LOG_TAG[] = "guest: ";
+
+static constexpr const char CMDLINE_TEST_NAME_PARAM[] = "virt_test_name";
+
+#define MODULE_BASE_DIR "/lib/modules"
+
+std::string GetModuleLoadList(bool recovery, const std::string& dir_path) {
+    auto module_load_file = "modules.load";
+    if (recovery) {
+        struct stat fileStat;
+        std::string recovery_load_path = dir_path + "/modules.load.recovery";
+        if (!stat(recovery_load_path.c_str(), &fileStat)) {
+            module_load_file = "modules.load.recovery";
+        }
+    }
+
+    return module_load_file;
+}
+
+int LoadKernelModules(bool recovery, bool want_console) {
+    struct utsname uts;
+    if (uname(&uts)) {
+        std::cerr << "Failed to get kernel version." << std::endl;
+        return EXIT_FAILURE;
+    }
+    int major, minor;
+    if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
+        std::cerr << "Failed to parse kernel version " << uts.release << std::endl;
+        return EXIT_FAILURE;
+    }
+
+    std::unique_ptr<DIR, decltype(&closedir)> base_dir(opendir(MODULE_BASE_DIR), closedir);
+    if (!base_dir) {
+        std::cerr << "Unable to open /lib/modules, skipping module loading." << std::endl;
+        return EXIT_FAILURE;
+    }
+    dirent* entry;
+    std::vector<std::string> module_dirs;
+    while ((entry = readdir(base_dir.get()))) {
+        if (entry->d_type != DT_DIR) {
+            continue;
+        }
+        int dir_major, dir_minor;
+        if (sscanf(entry->d_name, "%d.%d", &dir_major, &dir_minor) != 2 || dir_major != major ||
+            dir_minor != minor) {
+            continue;
+        }
+        module_dirs.emplace_back(entry->d_name);
+    }
+
+    // Sort the directories so they are iterated over during module loading
+    // in a consistent order. Alphabetical sorting is fine here because the
+    // kernel version at the beginning of the directory name must match the
+    // current kernel version, so the sort only applies to a label that
+    // follows the kernel version, for example /lib/modules/5.4 vs.
+    // /lib/modules/5.4-gki.
+    std::sort(module_dirs.begin(), module_dirs.end());
+
+    for (const auto& module_dir : module_dirs) {
+        std::string dir_path = MODULE_BASE_DIR "/";
+        dir_path.append(module_dir);
+        Modprobe m({dir_path}, GetModuleLoadList(recovery, dir_path));
+        bool retval = m.LoadListedModules(!want_console);
+        int modules_loaded = m.GetModuleCount();
+        if (modules_loaded > 0) {
+            return retval ? EXIT_SUCCESS : EXIT_FAILURE;
+        }
+    }
+
+    Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(recovery, MODULE_BASE_DIR));
+    bool retval = m.LoadListedModules(!want_console);
+    int modules_loaded = m.GetModuleCount();
+    if (modules_loaded > 0) {
+        return retval ? EXIT_SUCCESS : EXIT_FAILURE;
+    }
+    return EXIT_SUCCESS;
+}
+
+int ReadCommandLineTestName(std::string *out) {
+    std::ifstream ifs("/proc/cmdline");
+    std::string cmdline;
+    std::string test_name;
+    char delim = ' ';
+    size_t last = 0;
+
+    if (!ifs) {
+        std::cerr << "ERROR: Could not read command line params" << std::endl;
+        return EXIT_FAILURE;
+    }
+
+    if (ifs.eof()) {
+        std::cerr << "ERROR: Command line params empty" << std::endl;
+        return EXIT_FAILURE;
+    }
+
+    std::getline(ifs, cmdline, '\n');
+
+    while (last < cmdline.length()) {
+        ssize_t next = cmdline.find(delim, last);
+        if (next < 0) {
+            next = cmdline.length();
+        }
+
+        std::string param = cmdline.substr(last, next - last);
+        last = next + 1;
+
+        ssize_t equals = param.find('=');
+        if (equals < 0) {
+            continue;
+        }
+
+        std::string name = param.substr(0, equals);
+        std::string val = param.substr(equals + 1);
+        if (name == CMDLINE_TEST_NAME_PARAM) {
+            out->assign(val);
+            return EXIT_SUCCESS;
+        }
+    }
+
+    std::cerr << "ERROR: Command line params do not contain test name" << std::endl;
+    return EXIT_FAILURE;
+}
+
+#define CHECKCALL(x)                                                    \
+    do {                                                                \
+        if ((x) != 0) {                                                 \
+            std::cerr << LOG_TAG << "ERROR: " << #x << std::endl;       \
+            return EXIT_FAILURE;                                        \
+        }                                                               \
+    } while(0)
+
+int main() {
+    std::string test_name;
+
+    CHECKCALL(clearenv());
+
+    std::cerr << LOG_TAG << "Loading kernel modules..." << std::endl;
+    CHECKCALL(LoadKernelModules(false, false));
+
+    std::cerr << LOG_TAG << "Mounting filesystems..." << std::endl;
+    CHECKCALL(mkdir("/proc", 0777));
+    CHECKCALL(mount("proc", "/proc", "proc", 0, nullptr));
+
+    std::cerr << LOG_TAG << "Reading command line params..." << std::endl;
+    CHECKCALL(ReadCommandLineTestName(&test_name));
+
+#define VIRT_TEST(FN)                                                       \
+    if (test_name == #FN) {                                                 \
+        std::cerr << LOG_TAG << "Invoking " << #FN << "..." << std::endl;   \
+        CHECKCALL(FN());                                                    \
+        return EXIT_FAILURE;                                                \
+    }
+#include "virt_test_list.h"
+#undef VIRT_TEST
+
+    std::cerr << LOG_TAG << "Exiting VM..." << std::endl;
+    return EXIT_FAILURE;
+}
diff --git a/system/virt/tests/hostside/native/guest/vsock_server.cc b/system/virt/tests/hostside/native/guest/vsock_server.cc
new file mode 100644
index 0000000..3c32dcf
--- /dev/null
+++ b/system/virt/tests/hostside/native/guest/vsock_server.cc
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <linux/vm_sockets.h>
+
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+
+static constexpr const char LOG_TAG[] = "VsockTest: ";
+static constexpr int VSOCK_TEST_PORT = 4455;
+static constexpr const char VSOCK_TEST_MSG[] = "VsockTestMessage\n";
+
+int VsockServer() {
+    int server_fd, client_fd, ret;
+    struct sockaddr_vm server_sa = (struct sockaddr_vm) {
+        .svm_family = AF_VSOCK,
+        .svm_port = VSOCK_TEST_PORT,
+        .svm_cid = VMADDR_CID_ANY,
+    };
+    struct sockaddr_vm client_sa;
+    socklen_t socklen = sizeof(client_sa);
+    size_t sent = 0, len = strlen(VSOCK_TEST_MSG) + 1;
+
+    server_fd = socket(AF_VSOCK, SOCK_STREAM, 0);
+    if (server_fd < 0) {
+        std::cerr << "ERROR: socket: " << strerror(errno) << std::endl;
+        return -1;
+    }
+
+    ret = bind(server_fd, (struct sockaddr*)&server_sa, sizeof(server_sa));
+    if (ret != 0) {
+        std::cerr << "ERROR: bind: " << strerror(errno) << std::endl;
+        close(server_fd);
+        return -1;
+    }
+
+    std::cerr << LOG_TAG << "Waiting for a vsock connection..." << std::endl;
+
+    ret = listen(server_fd, 1);
+    if (ret != 0) {
+        std::cerr << "ERROR: listen: " << strerror(errno) << std::endl;
+        close(server_fd);
+        return -1;
+    }
+
+    client_fd = accept(server_fd, (struct sockaddr*)&client_sa, &socklen);
+    if (client_fd < 0) {
+        std::cerr << "ERROR: accept: " << strerror(errno) << std::endl;
+        close(server_fd);
+        return -1;
+    }
+
+    std::cerr << LOG_TAG << "Sending message to host..." << std::endl;
+
+    while (sent < len) {
+        ssize_t nbytes = write(client_fd, VSOCK_TEST_MSG + sent, len - sent);
+        if (nbytes > 0) {
+            sent += nbytes;
+        } else if (nbytes < 0 && errno == EAGAIN) {
+            continue;
+        } else {
+            close(client_fd);
+            return EXIT_FAILURE;
+        }
+    }
+
+    close(client_fd);
+    return EXIT_SUCCESS;
+}
diff --git a/system/virt/tests/hostside/native/host/main.cc b/system/virt/tests/hostside/native/host/main.cc
new file mode 100644
index 0000000..27ab132
--- /dev/null
+++ b/system/virt/tests/hostside/native/host/main.cc
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdlib>
+#include <iostream>
+#include <string>
+
+#include "virt_test.h"
+
+static constexpr const char LOG_TAG[] = "host: ";
+
+int main(int argc, const char *argv[]) {
+    std::string test_name;
+
+    if (argc != 2) {
+        std::cerr << LOG_TAG << "Usage: " << argv[0] << " <test-name>" << std::endl;
+        return EXIT_FAILURE;
+    }
+    test_name = std::string(argv[1]);
+
+#define VIRT_TEST(FN)                                                       \
+    if (test_name == #FN) {                                                 \
+        std::cerr << LOG_TAG << "Invoking " << #FN << "..." << std::endl;   \
+        return FN();                                                        \
+    }
+#include "virt_test_list.h"
+#undef VIRT_TEST
+
+    std::cerr << LOG_TAG << "Unknown test function: " << test_name << std::endl;
+    return EXIT_FAILURE;
+}
diff --git a/system/virt/tests/hostside/native/host/vsock_server.cc b/system/virt/tests/hostside/native/host/vsock_server.cc
new file mode 100644
index 0000000..c649139
--- /dev/null
+++ b/system/virt/tests/hostside/native/host/vsock_server.cc
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <linux/vm_sockets.h>
+
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+
+static constexpr const char LOG_TAG[] = "VsockTest: ";
+static constexpr int VSOCK_TEST_PORT = 4455;
+static constexpr int VSOCK_TEST_CID = 42;
+static constexpr const char VSOCK_TEST_MSG[] = "VsockTestMessage\n";
+
+int VsockServer() {
+    int fd, ret;
+    struct sockaddr_vm sa = (struct sockaddr_vm) {
+        .svm_family = AF_VSOCK,
+        .svm_port = VSOCK_TEST_PORT,
+        .svm_cid = VSOCK_TEST_CID,
+    };
+    char buf[1024];
+    size_t buf_used = 0;
+
+    fd = socket(AF_VSOCK, SOCK_STREAM, 0);
+    if (fd < 0) {
+        std::cerr << "ERROR: socket: " << strerror(errno) << std::endl;
+        return EXIT_FAILURE;
+    }
+
+    std::cerr << LOG_TAG << "Establishing a vsock connection..." << std::endl;
+
+    ret = connect(fd, (struct sockaddr*)&sa, sizeof(sa));
+    if (ret != 0) {
+        std::cerr << "ERROR: connect: " << strerror(errno) << std::endl;
+        close(fd);
+        return EXIT_FAILURE;
+    }
+
+    std::cerr << LOG_TAG << "Reading message from guest..." << std::endl;
+
+    while (true) {
+        ssize_t n = read(fd, buf + buf_used, sizeof(buf) - buf_used);
+
+        if (n == 0) {
+            break;
+        } else if (n < 0) {
+            close(fd);
+            return EXIT_FAILURE;
+        }
+
+        buf_used += n;
+    }
+
+    close(fd);
+
+    std::cerr << LOG_TAG << "Message from guest: " << buf << std::endl;
+    if ((buf_used == sizeof(VSOCK_TEST_MSG)) &&
+        memcmp(VSOCK_TEST_MSG, buf, sizeof(VSOCK_TEST_MSG)) == 0) {
+        std::cerr << LOG_TAG << "Message match" << std::endl;
+        return EXIT_SUCCESS;
+    } else {
+        std::cerr << LOG_TAG << "Message mismatch" << std::endl;
+        return EXIT_FAILURE;
+    }
+}
diff --git a/system/virt/tests/hostside/native/include/virt_test.h b/system/virt/tests/hostside/native/include/virt_test.h
new file mode 100644
index 0000000..9add2af
--- /dev/null
+++ b/system/virt/tests/hostside/native/include/virt_test.h
@@ -0,0 +1,3 @@
+#define VIRT_TEST(FN) extern int FN();
+#include "virt_test_list.h"
+#undef VIRT_TEST
diff --git a/system/virt/tests/hostside/native/include/virt_test_list.h b/system/virt/tests/hostside/native/include/virt_test_list.h
new file mode 100644
index 0000000..51fc8e1
--- /dev/null
+++ b/system/virt/tests/hostside/native/include/virt_test_list.h
@@ -0,0 +1 @@
+VIRT_TEST(VsockServer)
\ No newline at end of file
diff --git a/system/virt/tests/hostside/scripts/build_initramfs.sh b/system/virt/tests/hostside/scripts/build_initramfs.sh
new file mode 100755
index 0000000..112c19d
--- /dev/null
+++ b/system/virt/tests/hostside/scripts/build_initramfs.sh
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+##
+## Copyright (C) 2020 The Android Open Source Project
+##
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+##
+##      http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+##
+
+set -euo pipefail
+
+ROOT_DIR="${GEN_DIR}/root"
+MODULES_DIR="${ROOT_DIR}/lib/modules"
+
+mkdir -p "${ROOT_DIR}" "${MODULES_DIR}"
+for FILE in $*
+do
+	BASENAME=$(basename "${FILE}")
+	if [ "${BASENAME: -3}" == ".ko" ]
+	then
+		cp "${FILE}" "${MODULES_DIR}/${BASENAME}"
+	else
+		cp "${FILE}" "${ROOT_DIR}/${BASENAME}"
+	fi
+done
+"${MKBOOTFS}" "${ROOT_DIR}" > "${OUT}"
diff --git a/system/virt/tests/hostside/scripts/concat_initramfs.sh b/system/virt/tests/hostside/scripts/concat_initramfs.sh
new file mode 100755
index 0000000..3200b61
--- /dev/null
+++ b/system/virt/tests/hostside/scripts/concat_initramfs.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+##
+## Copyright (C) 2020 The Android Open Source Project
+##
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+##
+##      http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+##
+
+set -euo pipefail
+
+rm -f "${OUT}"
+while [ "$#" -gt 0 ]
+do
+	# Try to decompress with LZ4, otherwise just print.
+	("${LZ4}" -d -c "$1" || cat "$1") >> "${OUT}" 2>/dev/null
+	shift
+done