Factor out some bits
diff --git a/configure.ac b/configure.ac
index 120c6d9..6fb59a3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -10,6 +10,7 @@
 dnl Check for progs
 AC_PROG_CPP
 AC_PROG_CC
+AC_PROG_RANLIB
 AC_CHECK_PROGS([PYTHON3], [python3])
 AC_PROG_SED
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 47c6f18..e7a6747 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,10 @@
 bin_PROGRAMS = etnaviv_cl_test
 
+EXTRA_LIBRARIES = shared.a
+
+shared_a_SOURCES = drm_setup.c
+shared_a_CFLAGS = $(LIBDRM_CFLAGS) $(LIBDRM_ETNAVIV_CFLAGS)
+
 etnaviv_cl_test_SOURCES = etnaviv_cl_test.c
-etnaviv_cl_test_LDADD = $(LIBDRM_LIBS) $(LIBDRM_ETNAVIV_LIBS)
+etnaviv_cl_test_LDADD = shared.a $(LIBDRM_LIBS) $(LIBDRM_ETNAVIV_LIBS)
 etnaviv_cl_test_CFLAGS = $(LIBDRM_CFLAGS) $(LIBDRM_ETNAVIV_CFLAGS)
diff --git a/src/cmdstream.h b/src/cmdstream.h
new file mode 100644
index 0000000..c700611
--- /dev/null
+++ b/src/cmdstream.h
@@ -0,0 +1,39 @@
+#ifndef H_CMDSTREAM
+#define H_CMDSTREAM
+
+#include <etnaviv_drmif.h>
+
+#include "cmdstream.xml.h"
+
+static inline void etna_emit_load_state(struct etna_cmd_stream *stream,
+        const uint16_t offset, const uint16_t count)
+{
+    uint32_t v;
+
+    v =     (VIV_FE_LOAD_STATE_HEADER_OP_LOAD_STATE | VIV_FE_LOAD_STATE_HEADER_OFFSET(offset) |
+            (VIV_FE_LOAD_STATE_HEADER_COUNT(count) & VIV_FE_LOAD_STATE_HEADER_COUNT__MASK));
+
+    etna_cmd_stream_emit(stream, v);
+}
+
+static inline void etna_set_state(struct etna_cmd_stream *stream, uint32_t address, uint32_t value)
+{
+    etna_cmd_stream_reserve(stream, 2);
+    etna_emit_load_state(stream, address >> 2, 1);
+    etna_cmd_stream_emit(stream, value);
+}
+
+static inline void etna_set_state_from_bo(struct etna_cmd_stream *stream,
+        uint32_t address, struct etna_bo *bo)
+{
+    etna_cmd_stream_reserve(stream, 2);
+    etna_emit_load_state(stream, address >> 2, 1);
+
+    etna_cmd_stream_reloc(stream, &(struct etna_reloc){
+        .bo = bo,
+        .flags = ETNA_RELOC_WRITE,
+        .offset = 0,
+    });
+}
+
+#endif
diff --git a/src/drm_setup.c b/src/drm_setup.c
new file mode 100644
index 0000000..711799d
--- /dev/null
+++ b/src/drm_setup.c
@@ -0,0 +1,87 @@
+#include "drm_setup.h"
+#include "memutil.h"
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+struct drm_test_info *drm_test_setup(int argc, char **argv)
+{
+    struct drm_test_info *info;
+    drmVersionPtr version;
+
+    if (argc < 2) {
+        printf("Usage: %s /dev/dri/...\n", argv[0]);
+        return NULL;
+    }
+    info = CALLOC_STRUCT(drm_test_info);
+    info->fd = open(argv[1], O_RDWR);
+    if (info->fd < 0) {
+        fprintf(stdout, "Unable to open %s\n", argv[1]);
+        goto out;
+    }
+
+    version = drmGetVersion(info->fd);
+    if (version) {
+        printf("Version: %d.%d.%d\n", version->version_major,
+               version->version_minor, version->version_patchlevel);
+        printf("  Name: %s\n", version->name);
+        printf("  Date: %s\n", version->date);
+        printf("  Description: %s\n", version->desc);
+        drmFreeVersion(version);
+    }
+
+    info->dev = etna_device_new(info->fd);
+    if (!info->dev) {
+        fprintf(stdout, "Unable to create device\n");
+        goto out;
+    }
+
+    /* TODO: we assume that core 1 is a 3D+CL capable one.
+     * This is pretty much only true for GC2000.
+     * If the tests don't work on your hardware check this carefully.
+     */
+    info->gpu = etna_gpu_new(info->dev, 1);
+    if (!info->gpu) {
+        fprintf(stdout, "Unable to create gpu\n");
+        goto out_device;
+    }
+
+    info->pipe = etna_pipe_new(info->gpu, ETNA_PIPE_3D);
+    if (!info->pipe) {
+        fprintf(stdout, "Unable to create pipe\n");
+        goto out_gpu;
+    }
+
+    info->stream = etna_cmd_stream_new(info->pipe, 0x3000, NULL, NULL);
+    if (!info->stream) {
+        fprintf(stdout, "Unable to create command stream\n");
+        goto out_pipe;
+    }
+    return info;
+
+out_pipe:
+    etna_pipe_del(info->pipe);
+
+out_gpu:
+    etna_gpu_del(info->gpu);
+
+out_device:
+    etna_device_del(info->dev);
+
+out:
+    close(info->fd);
+    free(info);
+    return NULL;
+}
+
+void drm_test_teardown(struct drm_test_info *info)
+{
+    etna_cmd_stream_del(info->stream);
+    etna_pipe_del(info->pipe);
+    etna_gpu_del(info->gpu);
+    etna_device_del(info->dev);
+    close(info->fd);
+    free(info);
+}
+
diff --git a/src/drm_setup.h b/src/drm_setup.h
new file mode 100644
index 0000000..5861167
--- /dev/null
+++ b/src/drm_setup.h
@@ -0,0 +1,18 @@
+#ifndef H_DRM_SETUP
+#define H_DRM_SETUP
+
+#include <etnaviv_drmif.h>
+
+struct drm_test_info
+{
+    int fd;
+    struct etna_device *dev;
+    struct etna_gpu *gpu;
+    struct etna_pipe *pipe;
+    struct etna_cmd_stream *stream;
+};
+
+struct drm_test_info *drm_test_setup(int argc, char **argv);
+void drm_test_teardown(struct drm_test_info *info);
+
+#endif
diff --git a/src/etnaviv_cl_test.c b/src/etnaviv_cl_test.c
index 4f2b0bf..a9cb377 100644
--- a/src/etnaviv_cl_test.c
+++ b/src/etnaviv_cl_test.c
@@ -33,47 +33,15 @@
 #include <string.h>
 #include <unistd.h>
 
-#include <xf86drm.h>
-#include <etnaviv_drmif.h>
+#include "drm_setup.h"
+#include "cmdstream.h"
 
 #include "state.xml.h"
 #include "state_3d.xml.h"
 #include "common.xml.h"
-#include "cmdstream.xml.h"
 
 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
 
-static inline void etna_emit_load_state(struct etna_cmd_stream *stream,
-        const uint16_t offset, const uint16_t count)
-{
-    uint32_t v;
-
-    v =     (VIV_FE_LOAD_STATE_HEADER_OP_LOAD_STATE | VIV_FE_LOAD_STATE_HEADER_OFFSET(offset) |
-            (VIV_FE_LOAD_STATE_HEADER_COUNT(count) & VIV_FE_LOAD_STATE_HEADER_COUNT__MASK));
-
-    etna_cmd_stream_emit(stream, v);
-}
-
-static inline void etna_set_state(struct etna_cmd_stream *stream, uint32_t address, uint32_t value)
-{
-    etna_cmd_stream_reserve(stream, 2);
-    etna_emit_load_state(stream, address >> 2, 1);
-    etna_cmd_stream_emit(stream, value);
-}
-
-static inline void etna_set_state_from_bo(struct etna_cmd_stream *stream,
-        uint32_t address, struct etna_bo *bo)
-{
-    etna_cmd_stream_reserve(stream, 2);
-    etna_emit_load_state(stream, address >> 2, 1);
-
-    etna_cmd_stream_reloc(stream, &(struct etna_reloc){
-        .bo = bo,
-        .flags = ETNA_RELOC_WRITE,
-        .offset = 0,
-    });
-}
-
 static void gen_cmd_stream(struct etna_cmd_stream *stream, struct etna_bo *bmp)
 {
     etna_set_state(stream, VIVS_PA_SYSTEM_MODE, VIVS_PA_SYSTEM_MODE_UNK0 | VIVS_PA_SYSTEM_MODE_UNK4);
@@ -217,72 +185,24 @@
 
 int main(int argc, char *argv[])
 {
-    const size_t out_size = 65536;
-
-    struct etna_device *dev;
-    struct etna_gpu *gpu;
-    struct etna_pipe *pipe;
+    struct drm_test_info *info;
     struct etna_bo *bmp;
-    struct etna_cmd_stream *stream;
-
-    drmVersionPtr version;
-    int fd, ret = 0;
-
-        if (argc < 2) {
-            printf("Usage: %s /dev/dri/...\n", argv[0]);
-            return 1;
-        }
-
-    fd = open(argv[1], O_RDWR);
-    if (fd < 0)
+    static const size_t out_size = 65536;
+    if ((info = drm_test_setup(argc, argv)) == NULL) {
         return 1;
-
-    version = drmGetVersion(fd);
-    if (version) {
-        printf("Version: %d.%d.%d\n", version->version_major,
-               version->version_minor, version->version_patchlevel);
-        printf("  Name: %s\n", version->name);
-        printf("  Date: %s\n", version->date);
-        printf("  Description: %s\n", version->desc);
-        drmFreeVersion(version);
     }
 
-    dev = etna_device_new(fd);
-    if (!dev) {
-        ret = 2;
-        goto out;
-    }
-
-    /* TODO: we assume that core 1 is a 3D+CL capable one */
-    gpu = etna_gpu_new(dev, 1);
-    if (!gpu) {
-        ret = 3;
-        goto out_device;
-    }
-
-    pipe = etna_pipe_new(gpu, ETNA_PIPE_3D);
-    if (!pipe) {
-        ret = 4;
-        goto out_gpu;
-    }
-
-    bmp = etna_bo_new(dev, out_size, DRM_ETNA_GEM_CACHE_UNCACHED);
+    bmp = etna_bo_new(info->dev, out_size, DRM_ETNA_GEM_CACHE_UNCACHED);
     if (!bmp) {
-        ret = 5;
-        goto out_pipe;
+        fprintf(stderr, "Unable to allocate buffer\n");
+        goto out;
     }
     memset(etna_bo_map(bmp), 0, out_size);
 
-    stream = etna_cmd_stream_new(pipe, 0x3000, NULL, NULL);
-    if (!stream) {
-        ret = 6;
-        goto out_bo;
-    }
-
     /* generate command sequence */
-    gen_cmd_stream(stream, bmp);
+    gen_cmd_stream(info->stream, bmp);
 
-    etna_cmd_stream_finish(stream);
+    etna_cmd_stream_finish(info->stream);
 
     const unsigned char *data = etna_bo_map(bmp);
     for(int i=0; i<0x100; ++i) {
@@ -291,22 +211,9 @@
     printf("\n");
     printf("%s\n", data);
 
-    etna_cmd_stream_del(stream);
-
-out_bo:
-    etna_bo_del(bmp);
-
-out_pipe:
-    etna_pipe_del(pipe);
-
-out_gpu:
-    etna_gpu_del(gpu);
-
-out_device:
-    etna_device_del(dev);
-
+    drm_test_teardown(info);
+    return 0;
 out:
-    close(fd);
-
-    return ret;
+    drm_test_teardown(info);
+    return 1;
 }
diff --git a/src/memutil.h b/src/memutil.h
new file mode 100644
index 0000000..1858291
--- /dev/null
+++ b/src/memutil.h
@@ -0,0 +1,8 @@
+#ifndef H_MEMUTIL
+#define H_MEMUTIL
+
+#include <stdlib.h>
+#define CALLOC_STRUCT(T)   (struct T *) calloc(1, sizeof(struct T))
+
+#endif
+