diff --git a/devlib/bin/scripts/shutils.in b/devlib/bin/scripts/shutils.in
index e230229..0cca5bb 100755
--- a/devlib/bin/scripts/shutils.in
+++ b/devlib/bin/scripts/shutils.in
@@ -255,6 +255,28 @@ sched_get_kernel_attributes() {
 # Misc
 ################################################################################
 
+read_tree_values() {
+    BASEPATH=$1
+    MAXDEPTH=$2
+
+    if [ ! -e $BASEPATH ]; then
+        echo "ERROR: $BASEPATH does not exist"
+        exit 1
+    fi
+
+    PATHS=$($BUSYBOX find $BASEPATH -follow -maxdepth $MAXDEPTH)
+    i=0
+    for path in $PATHS; do
+        i=$(expr $i + 1)
+        if [ $i -gt 1 ]; then
+            break;
+        fi
+    done
+    if [ $i -gt 1 ]; then
+        $BUSYBOX grep -s '' $PATHS
+    fi
+}
+
 read_tree_tgz_b64() {
     BASEPATH=$1
     MAXDEPTH=$2
@@ -353,6 +375,9 @@ ftrace_get_function_stats)
 hotplug_online_all)
 	hotplug_online_all
     ;;
+read_tree_values)
+	read_tree_values $*
+    ;;
 read_tree_tgz_b64)
 	read_tree_tgz_b64 $*
     ;;
diff --git a/devlib/target.py b/devlib/target.py
index a902917..18fb59c 100644
--- a/devlib/target.py
+++ b/devlib/target.py
@@ -695,7 +695,7 @@ class Target(object):
         timeout = duration + 10
         self.execute('sleep {}'.format(duration), timeout=timeout)
 
-    def read_tree_values_flat(self, path, depth=1, check_exit_code=True,
+    def read_tree_tar_flat(self, path, depth=1, check_exit_code=True,
                               decode_unicode=True, strip_null_chars=True):
         command = 'read_tree_tgz_b64 {} {} {}'.format(quote(path), depth,
                                                   quote(self.working_directory))
@@ -732,8 +732,23 @@ class Target(object):
 
         return result
 
+    def read_tree_values_flat(self, path, depth=1, check_exit_code=True):
+        command = 'read_tree_values {} {}'.format(quote(path), depth)
+        output = self._execute_util(command, as_root=self.is_rooted,
+                                    check_exit_code=check_exit_code)
+
+        accumulator = defaultdict(list)
+        for entry in output.strip().split('\n'):
+            if ':' not in entry:
+                continue
+            path, value = entry.strip().split(':', 1)
+            accumulator[path].append(value)
+
+        result = {k: '\n'.join(v).strip() for k, v in accumulator.items()}
+        return result
+
     def read_tree_values(self, path, depth=1, dictcls=dict,
-                         check_exit_code=True, decode_unicode=True,
+                         check_exit_code=True, tar=False, decode_unicode=True,
                          strip_null_chars=True):
         """
         Reads the content of all files under a given tree
@@ -742,14 +757,20 @@ class Target(object):
         :depth: maximum tree depth to read
         :dictcls: type of the dict used to store the results
         :check_exit_code: raise an exception if the shutil command fails
-        :decode_unicode: decode the content of files as utf-8
+        :tar: fetch the entire tree using tar rather than just the value (more
+              robust but slower in some use-cases)
+        :decode_unicode: decode the content of tar-ed files as utf-8
         :strip_null_chars: remove '\x00' chars from the content of utf-8
                            decoded files
 
         :returns: a tree-like dict with the content of files as leafs
         """
-        value_map = self.read_tree_values_flat(path, depth, check_exit_code,
-                                               decode_unicode, strip_null_chars)
+        if not tar:
+            value_map = self.read_tree_values_flat(path, depth, check_exit_code)
+        else:
+            value_map = self.read_tree_tar_flat(path, depth, check_exit_code,
+                                                decode_unicode,
+                                                strip_null_chars)
         return _build_path_tree(value_map, path, self.path.sep, dictcls)
 
     # internal methods