Add kernel version detection to emulator
Different Linux kernels require different handling by the emulator.
Modified emulator to scan compressed/uncompressed kernel images for
a 'Linux version ' string. Manually tested on all prebuilt kernel
images and added basic unit tests.
Change-Id: Icecfb048e9184fd983799d0975215448feba8e08
diff --git a/Makefile.common b/Makefile.common
index c3f6820..5e21ccd 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -150,6 +150,7 @@
android/utils/stralloc.c \
android/utils/system.c \
android/utils/tempfile.c \
+ android/utils/uncompress.cpp \
android/utils/vector.c \
android/utils/win32_cmdline_quote.c \
diff --git a/Makefile.qemu-launcher b/Makefile.qemu-launcher
index 7be48d7..9c26567 100644
--- a/Makefile.qemu-launcher
+++ b/Makefile.qemu-launcher
@@ -25,7 +25,8 @@
LOCAL_SRC_FILES := $(qemu_launcher_SOURCES)
LOCAL_CFLAGS := $(qemu_launcher_CFLAGS)
LOCAL_STATIC_LIBRARIES := \
- emulator-common
+ emulator-common \
+ emulator-zlib
LOCAL_LDLIBS := $(qemu_launcher_LDLIBS)
$(call gen-hw-config-defs)
$(call end-emulator-program)
@@ -34,7 +35,8 @@
LOCAL_SRC_FILES := $(qemu_launcher_SOURCES)
LOCAL_CFLAGS := $(qemu_launcher_CFLAGS)
LOCAL_STATIC_LIBRARIES := \
- emulator64-common
+ emulator64-common \
+ emulator64-zlib
LOCAL_LDLIBS := $(qemu_launcher_LDLIBS)
$(call gen-hw-config-defs)
$(call end-emulator-program)
diff --git a/android/kernel/kernel_utils.cpp b/android/kernel/kernel_utils.cpp
old mode 100644
new mode 100755
index 50b7094..cbc4f4e
--- a/android/kernel/kernel_utils.cpp
+++ b/android/kernel/kernel_utils.cpp
@@ -9,14 +9,16 @@
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
-#include "android/kernel/kernel_utils.h"
-
+#include "android/base/containers/PodVector.h"
+#include "android/base/Limits.h"
#include "android/base/Log.h"
-#include "android/base/files/ScopedStdioFile.h"
-#include "android/base/String.h"
+#include "android/kernel/kernel_utils.h"
#include "android/kernel/kernel_utils_testing.h"
+#include "android/utils/file_data.h"
#include "android/utils/path.h"
+#include "android/utils/uncompress.h"
+#include <algorithm>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -30,144 +32,179 @@
#define KERNEL_ERROR LOG_IF(ERROR, DEBUG_KERNEL)
#define KERNEL_PERROR PLOG_IF(ERROR, DEBUG_KERNEL)
-using android::base::String;
-
-namespace {
-
-#ifndef _WIN32
-// Helper class to perform launch a command through popen() and call
-// pclose() on destruction.
-class ScopedPopenFile {
-public:
- ScopedPopenFile(const char* command) {
- mFile = ::popen(command, "r");
- }
-
- FILE* get() const { return mFile; }
-
- ~ScopedPopenFile() {
- if (mFile) {
- ::pclose(mFile);
- }
- }
-
-private:
- FILE* mFile;
-};
-#endif // !_WIN32
-
-bool getFileDescription(void* opaque, const char* filePath, String* text) {
- if (!filePath) {
- KERNEL_ERROR << "NULL path parameter";
- return false;
- }
-
- if (!path_exists(filePath)) {
- KERNEL_ERROR << "Kernel file doesn't exist: " << filePath;
- return false;
- }
-
-#ifdef _WIN32
- // TODO(digit): Better/portable detection based on libmagic or something.
- KERNEL_ERROR << "Can't detect kernel version on Windows!";
- return false;
-#else
- // NOTE: Use /usr/bin/file instead of 'file' because the latter can
- // be broken in certain environments (e.g. some versions of MacPorts).
- String command("/usr/bin/file ");
- command += filePath;
-
- ScopedPopenFile file(command.c_str());
- if (!file.get()) {
- KERNEL_PERROR << "Could not launch command: " << command.c_str();
- return false;
- }
-
- String result;
- const size_t kReserveSize = 256U;
- result.resize(kReserveSize);
-
- int ret = ::fread(&result[0], 1, kReserveSize, file.get());
- if (ret < static_cast<int>(kReserveSize) && ferror(file.get())) {
- KERNEL_ERROR << "Could not read file command output!?";
- return false;
- }
- result.resize(ret);
- text->assign(result);
- return true;
-#endif
-}
-
-android::kernel::GetFileDescriptionFunction* sGetFileDescription =
- getFileDescription;
-
-void* sGetFileDescriptionOpaque = NULL;
-
-} // namespace
+using android::base::PodVector;
namespace android {
namespace kernel {
-void setFileDescriptionFunction(GetFileDescriptionFunction* file_func,
- void* file_opaque) {
- sGetFileDescription = file_func ? file_func : &getFileDescription;
- sGetFileDescriptionOpaque = file_func ? file_opaque : NULL;
-}
+static const char kVersionStringPrefix[] = "Linux version ";
+static const size_t kVersionStringPrefixLen = sizeof(kVersionStringPrefix) - 1U;
} // namespace kernel
} // namespace android
-bool android_pathProbeKernelType(const char* kernelPath, KernelType* ktype) {
- String description;
+#ifndef __APPLE__
+size_t strlcpy(char* dst, const char * src, size_t size)
+{
+ size_t srcLen = strlen(src);
+ if (size > 0) {
+ size_t copyLen = std::min(srcLen, size-1);
+ memcpy(dst, src, copyLen);
+ dst[copyLen] = 0;
+ }
+ return srcLen;
+}
+#endif
- if (!sGetFileDescription(sGetFileDescriptionOpaque,
- kernelPath,
- &description)) {
- return false;
+#ifdef _WIN32
+// TODO: (vharron) move somewhere more generally useful?
+// Returns a pointer to the first occurrence of |needle| in |haystack|, or a
+// NULL pointer if |needle| is not part of |haystack|.
+const void* memmem(const void* haystack, size_t haystackLen,
+ const void* needle, size_t needleLen) {
+ if (needleLen == 0 ) {
+ return haystack;
}
- const char* bzImage = ::strstr(description.c_str(), "bzImage");
- if (!bzImage) {
- KERNEL_ERROR << "Not a compressed Linux kernel image!";
- return false;
- }
- const char* version = ::strstr(bzImage, "version ");
- if (!version) {
- KERNEL_ERROR << "Could not determine version!";
- return false;
- }
- version += ::strlen("version ");
- KERNEL_LOG << "Found kernel version " << version;
- char* end;
- unsigned long major = ::strtoul(version, &end, 10);
- if (end == version || *end != '.') {
- KERNEL_ERROR << "Could not find kernel major version!";
+ if (haystackLen < needleLen) {
+ return NULL;
+ }
+
+ const char* haystackPos = (const char*)haystack;
+ const char* haystackEnd = haystackPos + (haystackLen - needleLen);
+ for (; haystackPos < haystackEnd; haystackPos++) {
+ if (0==memcmp(haystackPos, needle, needleLen)) {
+ return haystackPos;
+ }
+ }
+ return NULL;
+}
+#endif
+
+
+bool android_parseLinuxVersionString(const char* versionString,
+ KernelVersion* kernelVersion) {
+ if (strncmp(versionString, android::kernel::kVersionStringPrefix,
+ android::kernel::kVersionStringPrefixLen)) {
+ KERNEL_ERROR << "unsupported kernel version string:" << versionString;
return false;
}
- KERNEL_LOG << "Kernel major version: " << major;
- if (major > 3) {
- *ktype = KERNEL_TYPE_3_10_OR_ABOVE;
- } else if (major < 3) {
- *ktype = KERNEL_TYPE_LEGACY;
- } else /* major == 3 */ {
- version = end + 1;
- unsigned long minor = ::strtoul(version, &end, 10);
- if (end == version) {
- KERNEL_ERROR << "Could not find kernel minor version!";
+ // skip past the prefix to the version number
+ versionString += android::kernel::kVersionStringPrefixLen;
+
+ uint32_t temp = 0;
+ for (int i = 0; i < 3; i++) {
+ // skip '.' delimeters
+ while (i > 0 && *versionString == '.') {
+ versionString++;
+ }
+
+ char* end;
+ unsigned long number = ::strtoul(versionString, &end, 10);
+ if (end == versionString || number > 0xff) {
+ KERNEL_ERROR << "unsupported kernel version string:"
+ << versionString;
return false;
}
- KERNEL_LOG << "Kernel minor version: " << minor;
-
- *ktype = (minor >= 10)
- ? KERNEL_TYPE_3_10_OR_ABOVE : KERNEL_TYPE_LEGACY;
+ temp <<= 8;
+ temp |= number;
+ versionString = end;
}
+ *kernelVersion = (KernelVersion)temp;
+
+ KERNEL_LOG << android::base::LogString("Kernel version hex 0x%06x", temp);
return true;
}
-const char* android_kernelSerialDevicePrefix(KernelType ktype) {
- switch (ktype) {
- case KERNEL_TYPE_LEGACY: return "ttyS";
- case KERNEL_TYPE_3_10_OR_ABOVE: return "ttyGF";
- default: return "";
+const char* android_kernelSerialDevicePrefix(KernelVersion kernelVersion) {
+ if (kernelVersion >= KERNEL_VERSION_3_10_0) {
+ return "ttyGF";
}
+ return "ttyS";
+}
+
+bool android_imageProbeKernelVersionString(const uint8_t* kernelFileData,
+ size_t kernelFileSize,
+ char* dst/*[dstLen]*/,
+ size_t dstLen) {
+ PodVector<uint8_t> uncompressed;
+
+ const uint8_t* uncompressedKernel = NULL;
+ size_t uncompressedKernelLen = 0;
+
+ const char kElfHeader[] = { 0x7f, 'E', 'L', 'F' };
+
+ if (kernelFileSize < sizeof(kElfHeader)) {
+ KERNEL_ERROR << "Kernel image too short";
+ return false;
+ }
+
+ if (0 == memcmp(kElfHeader, kernelFileData, sizeof(kElfHeader))) {
+ // this is an uncompressed ELF file (probably mips)
+ uncompressedKernel = kernelFileData;
+ uncompressedKernelLen = kernelFileSize;
+ }
+ else {
+ // handle compressed kernels here
+ const uint8_t kGZipMagic[4] = { 0x1f, 0x8b, 0x08, 0x00 };
+ const uint8_t* compressedKernel = (const uint8_t*)memmem(kernelFileData,
+ kernelFileSize,
+ kGZipMagic,
+ sizeof(kGZipMagic));
+ if (!compressedKernel) {
+ KERNEL_ERROR << "Could not find gzip header in kernel file!";
+ return false;
+ }
+
+ size_t compressedKernelLen = kernelFileSize -
+ (compressedKernel - kernelFileData);
+
+ // inflate ratios for all prebuilt kernels on 2014-07-14 is 1.9:1 ~
+ // 3.43:1 not sure how big the uncompressed size is, so make an
+ // absurdly large buffer
+ uncompressedKernelLen = compressedKernelLen * 10;
+ uncompressed.resize(uncompressedKernelLen);
+ uncompressedKernel = uncompressed.begin();
+
+ bool zOk = uncompress_gzipStream(uncompressed.begin(),
+ &uncompressedKernelLen,
+ compressedKernel,
+ compressedKernelLen);
+ if (!zOk) {
+ KERNEL_ERROR << "Kernel decompression error";
+ // it may have been partially decompressed, so we're going to
+ // try to find the version string anyway
+ }
+ }
+
+ // okay, now we have a pointer to an uncompressed kernel, let's find the
+ // version string
+ const char* versionStringStart = (const char*)memmem(
+ uncompressedKernel,
+ uncompressedKernelLen,
+ android::kernel::kVersionStringPrefix,
+ android::kernel::kVersionStringPrefixLen);
+ if (!versionStringStart) {
+ KERNEL_ERROR << "Could not find 'Linux version ' in kernel!";
+ return false;
+ }
+
+ strlcpy(dst, versionStringStart, dstLen);
+
+ return true;
+}
+
+bool android_pathProbeKernelVersionString(const char* kernelPath,
+ char* dst/*[dstLen]*/,
+ size_t dstLen) {
+ FileData kernelFileData;
+ if (fileData_initFromFile(&kernelFileData, kernelPath) < 0) {
+ KERNEL_ERROR << "Could not open kernel file!";
+ return false;
+ }
+
+ return android_imageProbeKernelVersionString(kernelFileData.data,
+ kernelFileData.size,
+ dst,
+ dstLen);
}
diff --git a/android/kernel/kernel_utils.h b/android/kernel/kernel_utils.h
old mode 100644
new mode 100755
index e10eb34..2e6fb45
--- a/android/kernel/kernel_utils.h
+++ b/android/kernel/kernel_utils.h
@@ -13,6 +13,8 @@
#define ANDROID_KERNEL_KERNEL_UTILS_H
#include "android/utils/compiler.h"
+
+#include <stddef.h>
#include <stdint.h>
ANDROID_BEGIN_HEADER
@@ -20,24 +22,36 @@
// An enum used to list of the types of Linux kernel images we need to
// handle. Unfortunately, this affects how we setup the kernel command-line
// when launching the system.
-//
-// KERNEL_TYPE_LEGACY is any Linux kernel image before 3.10
-// KERNEL_TYPE_3_10_OR_ABOVE is anything at 3.10 or above.
typedef enum {
- KERNEL_TYPE_LEGACY = 0,
- KERNEL_TYPE_3_10_OR_ABOVE = 1,
-} KernelType;
+ KERNEL_VERSION_2_6_29 = 0x02061d,
+ KERNEL_VERSION_3_4_0 = 0x030400,
+ KERNEL_VERSION_3_4_67 = 0x030443,
+ KERNEL_VERSION_3_10_0 = 0x030a00,
+} KernelVersion;
-// Probe the kernel image at |kernelPath| and returns the corresponding
-// KernelType value. On success, returns true and sets |*ktype| appropriately.
+// Converts a string at |versionString| in the format "Linux version MM.mm.rr..."
+// into a hex value 0x00MMmmrr. On success, returns true and sets |*kernelVersion|
+// appropriately
+// On failure, returns false and doesn't touch |*kernelVersion|.
+bool android_parseLinuxVersionString(const char* versionString,
+ KernelVersion* kernelVersion);
+
+// Probe the kernel image at |kernelPath| and copy the corresponding
+// 'Linux version ' string into the |dst| buffer. On success, returns true
+// and copies up to |dstLen-1| characters into dst. dst will always be NUL
+// terminated if |dstLen| >= 1
+//
// On failure (e.g. if the file doesn't exist or cannot be probed), return
-// false and doesn't touch |*ktype|.
-bool android_pathProbeKernelType(const char* kernelPath, KernelType *ktype);
+// false and doesn't touch |dst| buffer.
+bool android_pathProbeKernelVersionString(const char* kernelPath,
+ char* dst,
+ size_t dstLen);
-// Return the serial device name prefix matching a given kernel type |ktype|.
-// I.e. this should be "/dev/ttyS" for KERNEL_TYPE_LEGACY, and
-// "/dev/ttyGF" for KERNEL_TYPE_3_10_OR_ABOVE.
-const char* android_kernelSerialDevicePrefix(KernelType ktype);
+// Return the serial device name prefix matching a given kernel type
+// |kernelVersion|. I.e. this should be "/dev/ttyS" for before 3.10.0 and
+// "/dev/ttyGF" for 3.10.0 and later.
+const char* android_kernelSerialDevicePrefix(KernelVersion kernelVersion);
+
ANDROID_END_HEADER
diff --git a/android/kernel/kernel_utils_testing.h b/android/kernel/kernel_utils_testing.h
old mode 100644
new mode 100755
index f2d64ec..51b476a
--- a/android/kernel/kernel_utils_testing.h
+++ b/android/kernel/kernel_utils_testing.h
@@ -12,36 +12,17 @@
#ifndef ANDROID_KERNEL_KERNEL_UTILS_TESTING_H
#define ANDROID_KERNEL_KERNEL_UTILS_TESTING_H
-namespace android {
-
-namespace base {
-class String;
-} // namespace base
-
-namespace kernel {
-
-// Type of a function used to retrieve the textual description of a given
-// file at |filePath|. On success, return true and sets |*text| to the
-// description text, as if running through the 'file' command on Unix.
-// |opaque| is a client-provided value set by calling
-// setFileDescriptionFunction() below.
-typedef bool (GetFileDescriptionFunction)(void* opaque,
- const char* filePath,
- android::base::String* text);
-
-// Change the implementation of the function that extracts textual
-// descriptions from a given file. |file_func| is a pointer to the
-// new function, and |file_opaque| is the value that will be passed
-// as its first parameter. Note that if |file_func| is NULL, the
-// default implementation will be selected instead.
+// Probe the kernel image at |kernelPath| and copy the corresponding
+// 'Linux version ' string into the |dst| buffer. On success, returns true
+// and copies up to |dstLen-1| characters into dst. dst will always be NUL
+// terminated if |dstLen| >= 1
//
-// Only use this during unit-testing to force different description
-// values on arbitrary file content.
-void setFileDescriptionFunction(GetFileDescriptionFunction* file_func,
- void* file_opaque);
+// On failure (e.g. if the file doesn't exist or cannot be probed), return
+// false and doesn't touch |dst| buffer.
+bool android_imageProbeKernelVersionString(const uint8_t* kernelFileData,
+ size_t kernelFileSize,
+ char* dst,
+ size_t dstLen);
-} // namespace kernel
-
-} // namespace android
#endif // ANDROID_KERNEL_KERNEL_UTILS_TESTING_H
diff --git a/android/kernel/kernel_utils_unittest.cpp b/android/kernel/kernel_utils_unittest.cpp
old mode 100644
new mode 100755
index a5afa2a..40ad06c
--- a/android/kernel/kernel_utils_unittest.cpp
+++ b/android/kernel/kernel_utils_unittest.cpp
@@ -11,121 +11,141 @@
#include "android/kernel/kernel_utils.h"
-#include "android/base/String.h"
#include "android/kernel/kernel_utils_testing.h"
#include <gtest/gtest.h>
-using android::base::String;
-
namespace android {
namespace kernel {
-namespace {
-
-class ScopedDescriptionFunc {
-public:
- explicit ScopedDescriptionFunc(GetFileDescriptionFunction* file_func) {
- setFileDescriptionFunction(file_func, NULL);
- }
-
- ScopedDescriptionFunc(GetFileDescriptionFunction* file_func,
- void* file_opaque) {
- setFileDescriptionFunction(file_func, file_opaque);
- }
-
- ~ScopedDescriptionFunc() {
- setFileDescriptionFunction(NULL, NULL);
- }
-};
-
-} // namespace
-
TEST(KernelUtils, GetKernelSerialDevicePrefix) {
EXPECT_STREQ("ttyS",
- android_kernelSerialDevicePrefix(KERNEL_TYPE_LEGACY));
+ android_kernelSerialDevicePrefix(KERNEL_VERSION_2_6_29));
+ EXPECT_STREQ("ttyS",
+ android_kernelSerialDevicePrefix(KERNEL_VERSION_3_4_0));
+ EXPECT_STREQ("ttyS",
+ android_kernelSerialDevicePrefix(KERNEL_VERSION_3_4_67));
EXPECT_STREQ("ttyGF",
- android_kernelSerialDevicePrefix(KERNEL_TYPE_3_10_OR_ABOVE));
+ android_kernelSerialDevicePrefix(KERNEL_VERSION_3_10_0));
}
-static bool failFunc(void* opaque, const char* path, String* text) {
- return false;
-}
+TEST(KernelUtils, ProbeKernelVersionString) {
+ // you can regenerate these tables using
+ // android/kernel/testing/print_mock_kernel_data.sh
-TEST(KernelUtils, ProbeKernelTypeWithNoKernelFile) {
- ScopedDescriptionFunc func(&failFunc);
+ const char kMockKernelVersion[] =
+ "Linux version 3.10.0+ (vharron@tifa.mtv.corp.google.com) "
+ "(gcc version 4.7 (GCC) ) #1 PREEMPT Sat Jan 5 2:45:24 PDT 2008\n";
- KernelType ktype;
- EXPECT_FALSE(android_pathProbeKernelType("/tmp/kernel", &ktype));
-}
-
-struct Expectation {
- const char* description;
- bool result;
- KernelType ktype;
-
- static bool getDescriptionFunc(void* opaque,
- const char* path,
- String* text) {
- const Expectation* expectation = static_cast<const Expectation*>(opaque);
- if (!expectation->result)
- return false;
- text->assign(expectation->description);
- return true;
- }
-};
-
-#ifdef _WIN32
-#define DISABLED_ON_WIN32(x) DISABLED_ ## x
-#else
-#define DISABLED_ON_WIN32(x) x
-#endif
-
-TEST(KernelUtils, DISABLED_ON_WIN32(PathProbeKernelType)) {
- static const Expectation kData[] = {
- { NULL, false, KERNEL_TYPE_LEGACY },
- // Missing bzImage.
- { "Linux kernel x86 boot executable raw image, version 3.10.0+",
- false, KERNEL_TYPE_LEGACY },
- // Missing version
- { "Linux kernel x86 boot executable bzImage, 3.10.0+",
- false, KERNEL_TYPE_LEGACY },
- // Legacy 2.6.29 kernel
- { "Linux kernel x86 boot executable bzImage, version 2.6.29 (foo...)",
- true,
- KERNEL_TYPE_LEGACY },
- // Legacy 3.4
- { "Linux kernel x86 boot executable bzImage, version 3.4.1 (foo...)",
- true,
- KERNEL_TYPE_LEGACY },
- // 3.10
- { "Linux kernel x86 boot executable bzImage, version 3.10.0+",
- true,
- KERNEL_TYPE_3_10_OR_ABOVE },
- // 3.40
- { "Linux kernel x86 boot executable bzImage, version 3.40.0",
- true,
- KERNEL_TYPE_3_10_OR_ABOVE },
- // 4.0
- { "Linux kernel x86 boot executable bzImage, version 4.0.9",
- true,
- KERNEL_TYPE_3_10_OR_ABOVE },
+ // a mock uncompressed kernel
+ // ELF header, followed by an unspecified number of bytes
+ // followed by a 'Linux version ' string
+ static const unsigned char kMockKernelElf[] = {
+ 0x7f, 0x45, 0x4c, 0x46, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x76, 0x65, 0x72, 0x73,
+ 0x69, 0x6f, 0x6e, 0x20, 0x33, 0x2e, 0x31, 0x30, 0x2e, 0x30, 0x2b, 0x20,
+ 0x28, 0x76, 0x68, 0x61, 0x72, 0x72, 0x6f, 0x6e, 0x40, 0x74, 0x69, 0x66,
+ 0x61, 0x2e, 0x6d, 0x74, 0x76, 0x2e, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x67,
+ 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x29, 0x20, 0x28,
+ 0x67, 0x63, 0x63, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20,
+ 0x34, 0x2e, 0x37, 0x20, 0x28, 0x47, 0x43, 0x43, 0x29, 0x20, 0x29, 0x20,
+ 0x23, 0x31, 0x20, 0x50, 0x52, 0x45, 0x45, 0x4d, 0x50, 0x54, 0x20, 0x53,
+ 0x61, 0x74, 0x20, 0x4a, 0x61, 0x6e, 0x20, 0x35, 0x20, 0x32, 0x3a, 0x34,
+ 0x35, 0x3a, 0x32, 0x34, 0x20, 0x50, 0x44, 0x54, 0x20, 0x32, 0x30, 0x30,
+ 0x38, 0x0a, 0x00, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39
};
- const size_t kDataSize = sizeof(kData) / sizeof(kData[0]);
- static const char kKernelPath[] = "/tmp/kernel";
- for (size_t n = 0; n < kDataSize; ++n) {
- KernelType kernelType;
- const Expectation& expectation = kData[n];
- ScopedDescriptionFunc func(&Expectation::getDescriptionFunc,
- (void*)&expectation);
- EXPECT_EQ(expectation.result,
- android_pathProbeKernelType(kKernelPath, &kernelType))
- << "For [" << expectation.description << "]";
- if (expectation.result) {
- EXPECT_EQ(expectation.ktype, kernelType) << "For ["
- << expectation.description << "]";
- }
- }
+
+ // a mock uncompressed kernel without version string
+ // ELF header, followed by an unspecified number of bytes
+ static const unsigned char kMockKernelElfNoString[] = {
+ 0x7f, 0x45, 0x4c, 0x46, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31,
+ 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39
+ };
+
+ // a mock compressed kernel
+ // an unspecified number of bytes, followed by a gzip header
+ // gzip stream starts 10 bytes after gzip header, uncompressed gzip stream
+ // begins with an ELF header as above
+ static const unsigned char kMockKernelGzip[] = {
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x1f, 0x8b,
+ 0x08, 0x00, 0x24, 0xc7, 0xc6, 0x53, 0x00, 0x03, 0xab, 0x77, 0xf5, 0x71,
+ 0x33, 0x30, 0x34, 0x32, 0x36, 0x31, 0x35, 0x33, 0xb7, 0xb0, 0xf4, 0xc9,
+ 0xcc, 0x2b, 0xad, 0x50, 0x28, 0x4b, 0x2d, 0x2a, 0xce, 0xcc, 0xcf, 0x53,
+ 0x30, 0xd6, 0x33, 0x34, 0xd0, 0x33, 0xd0, 0x56, 0xd0, 0x28, 0xcb, 0x48,
+ 0x2c, 0x2a, 0xca, 0xcf, 0x73, 0x28, 0xc9, 0x4c, 0x4b, 0xd4, 0xcb, 0x2d,
+ 0x29, 0xd3, 0x4b, 0xce, 0x2f, 0x2a, 0xd0, 0x4b, 0xcf, 0xcf, 0x4f, 0xcf,
+ 0x49, 0x05, 0xb2, 0x73, 0x35, 0x15, 0x34, 0xd2, 0x93, 0x93, 0xe1, 0xfa,
+ 0x4c, 0xf4, 0xcc, 0x15, 0x34, 0xdc, 0x9d, 0x9d, 0x35, 0x15, 0x34, 0x15,
+ 0x94, 0x0d, 0x15, 0x02, 0x82, 0x5c, 0x5d, 0x7d, 0x03, 0x42, 0x14, 0x82,
+ 0x13, 0x4b, 0x14, 0xbc, 0x12, 0xf3, 0x14, 0x4c, 0x15, 0x8c, 0xac, 0x4c,
+ 0x4c, 0xad, 0x8c, 0x4c, 0x14, 0x02, 0x5c, 0x42, 0x14, 0x8c, 0x0c, 0x0c,
+ 0x2c, 0xb8, 0x18, 0xe0, 0x2e, 0x00, 0x00, 0xd3, 0x6e, 0x68, 0xa6, 0x90,
+ 0x00, 0x00, 0x00
+ };
+
+ char kernelVersionString[256];
+
+ kernelVersionString[0] = 0;
+ EXPECT_EQ(true, android_imageProbeKernelVersionString(
+ kMockKernelElf,
+ sizeof(kMockKernelElf),
+ kernelVersionString,
+ sizeof(kernelVersionString)));
+ EXPECT_STREQ(kMockKernelVersion, kernelVersionString);
+
+ kernelVersionString[0] = 0;
+ EXPECT_EQ(true, android_imageProbeKernelVersionString(
+ kMockKernelGzip,
+ sizeof(kMockKernelGzip),
+ kernelVersionString,
+ sizeof(kernelVersionString)));
+ EXPECT_STREQ(kMockKernelVersion, kernelVersionString);
+
+ kernelVersionString[0] = 127;
+ EXPECT_EQ(false, android_imageProbeKernelVersionString(
+ 0,
+ 0,
+ kernelVersionString,
+ sizeof(kernelVersionString)));
+ EXPECT_EQ(127, kernelVersionString[0]);
+
+ kernelVersionString[0] = 127;
+ EXPECT_EQ(false, android_imageProbeKernelVersionString(
+ kMockKernelElfNoString,
+ sizeof(kMockKernelElfNoString),
+ kernelVersionString,
+ sizeof(kernelVersionString)));
+ EXPECT_EQ(127, kernelVersionString[0]);
+}
+
+void ParseKernelVersionString(const char* versionString,
+ KernelVersion expectedVersion) {
+ KernelVersion actualVersion;
+ EXPECT_EQ(true, android_parseLinuxVersionString(versionString,
+ &actualVersion));
+ EXPECT_EQ(expectedVersion, actualVersion);
+}
+
+TEST(KernelUtils, ParseKernelVersionString) {
+ ParseKernelVersionString("Linux version 2.6.29-gea477bb (kroo...\n",
+ KERNEL_VERSION_2_6_29);
+
+ ParseKernelVersionString("Linux version 2.6.29 (vcht...\n",
+ KERNEL_VERSION_2_6_29);
+
+ ParseKernelVersionString("Linux version 3.4.0-66985-gb04946b (digi...\n",
+ KERNEL_VERSION_3_4_0);
+
+ ParseKernelVersionString("Linux version 3.4.67+ (ghac...\n",
+ KERNEL_VERSION_3_4_67 );
+
+ ParseKernelVersionString("Linux version 3.4.67-01413-g9ac497f (ghac...\n",
+ KERNEL_VERSION_3_4_67);
+
+ ParseKernelVersionString("Linux version 3.10.0+ (ghac...\n",
+ KERNEL_VERSION_3_10_0);
}
} // namespace kernel
diff --git a/android/kernel/testing/print_mock_kernel_data.sh b/android/kernel/testing/print_mock_kernel_data.sh
new file mode 100755
index 0000000..1a1a5cb
--- /dev/null
+++ b/android/kernel/testing/print_mock_kernel_data.sh
@@ -0,0 +1,31 @@
+#!/bin/bash -e
+
+LINUX_VERSION="Linux version 3.10.0+ (vharron@tifa.mtv.corp.google.com) (gcc version 4.7 (GCC) ) #1 PREEMPT Sat Jan 5 2:45:24 PDT 2008\xA\x0"
+TMP_FILE=/tmp/print_mock_kernel_tmp
+
+printf "// a mock uncompressed kernel\n"
+printf "// ELF header, followed by an unspecified number of bytes\n"
+printf "// followed by a 'Linux version ' string\n"
+printf "static const unsigned char kMockKernelElf[] = {\n"
+printf '\x7f'ELF0123456789"${LINUX_VERSION}"0123456789 | xxd -i
+printf "};\n\n"
+
+printf "// a mock uncompressed kernel without version string\n"
+printf "// ELF header, followed by an unspecified number of bytes\n"
+printf "static const unsigned char kMockKernelElfNoString[] = {\n"
+printf '\x7f'ELF0123456789012345678901234567890123456789 | xxd -i
+printf "};\n\n"
+
+printf "// a mock compressed kernel\n"
+printf "// an unspecified number of bytes, followed by a gzip header\n"
+printf "// gzip stream starts 10 bytes after gzip header, uncompressed gzip stream\n"
+printf "// begins with an ELF header as above\n"
+printf "static const unsigned char kMockKernelGzip[] = {\n"
+printf "0123456789" > $TMP_FILE
+printf '\x7f'ELF0123456789"${LINUX_VERSION}"0123456789 | gzip >> $TMP_FILE
+cat $TMP_FILE | xxd -i
+printf "};\n\n"
+
+rm $TMP_FILE
+
+
diff --git a/android/main.c b/android/main.c
old mode 100644
new mode 100755
index b1a3053..a5b479a
--- a/android/main.c
+++ b/android/main.c
@@ -453,17 +453,26 @@
}
}
- KernelType kernelType = KERNEL_TYPE_LEGACY;
- if (!android_pathProbeKernelType(hw->kernel_path, &kernelType)) {
- D("WARNING: Could not determine kernel device naming scheme. Assuming legacy\n"
- "If this AVD doesn't boot, and uses a recent kernel (3.10 or above) try setting\n"
- "'kernel.newDeviceNaming' to 'yes' in its configuration.\n");
+ char versionString[256];
+ if (!android_pathProbeKernelVersionString(hw->kernel_path,
+ versionString,
+ sizeof(versionString))) {
+ derror("Can't find 'Linux version ' string in kernel image file: %s",
+ hw->kernel_path);
+ exit(2);
+ }
+
+ KernelVersion kernelVersion = 0;
+ if (!android_parseLinuxVersionString(versionString, &kernelVersion)) {
+ derror("Can't parse 'Linux version ' string in kernel image file: '%s'",
+ versionString);
+ exit(2);
}
// Auto-detect kernel device naming scheme if needed.
if (androidHwConfig_getKernelDeviceNaming(hw) < 0) {
const char* newDeviceNaming = "no";
- if (kernelType == KERNEL_TYPE_3_10_OR_ABOVE) {
+ if (kernelVersion >= KERNEL_VERSION_3_10_0) {
D("Auto-detect: Kernel image requires new device naming scheme.");
newDeviceNaming = "yes";
} else {
diff --git a/android/qemu-launcher/emulator-qemu.cpp b/android/qemu-launcher/emulator-qemu.cpp
index 899cc9d..280bd58 100644
--- a/android/qemu-launcher/emulator-qemu.cpp
+++ b/android/qemu-launcher/emulator-qemu.cpp
@@ -299,17 +299,26 @@
hw->kernel_path = kernelFile;
}
- KernelType kernelType = KERNEL_TYPE_LEGACY;
- if (!android_pathProbeKernelType(hw->kernel_path, &kernelType)) {
- D("WARNING: Could not determine kernel device naming scheme. Assuming legacy\n"
- "If this AVD doesn't boot, and uses a recent kernel (3.10 or above) try setting\n"
- "'kernel.newDeviceNaming' to 'yes' in its configuration.\n");
+ char versionString[256];
+ if (!android_pathProbeKernelVersionString(hw->kernel_path,
+ versionString,
+ sizeof(versionString))) {
+ derror("Can't find 'Linux version ' string in kernel image file: %s",
+ hw->kernel_path);
+ exit(2);
+ }
+
+ KernelVersion kernelVersion;
+ if (!android_parseLinuxVersionString(versionString, &kernelVersion)) {
+ derror("Can't parse 'Linux version ' string in kernel image file: '%s'",
+ versionString);
+ exit(2);
}
// Auto-detect kernel device naming scheme if needed.
if (androidHwConfig_getKernelDeviceNaming(hw) < 0) {
const char* newDeviceNaming = "no";
- if (kernelType == KERNEL_TYPE_3_10_OR_ABOVE) {
+ if (kernelVersion >= KERNEL_VERSION_3_10_0) {
D("Auto-detect: Kernel image requires new device naming scheme.");
newDeviceNaming = "yes";
} else {
diff --git a/android/utils/uncompress.cpp b/android/utils/uncompress.cpp
new file mode 100755
index 0000000..3a3a939
--- /dev/null
+++ b/android/utils/uncompress.cpp
@@ -0,0 +1,43 @@
+// Copyright 2014 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+#include "android/utils/uncompress.h"
+#include "zlib.h"
+
+bool uncompress_gzipStream(uint8_t* dst, size_t* dstLen, const uint8_t* src,
+ size_t srcLen) {
+ z_stream stream;
+ stream.next_in = (Bytef*)src;
+ stream.avail_in = srcLen;
+ stream.next_out = dst;
+ stream.avail_out = *dstLen;
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+
+ // magic number from gz_read
+ const int GZIP_WINDOW_BITS = 15 + 16;
+
+ int result = inflateInit2(&stream, GZIP_WINDOW_BITS);
+ if (result != Z_OK) {
+ return result;
+ }
+
+ result = inflate(&stream, Z_FINISH);
+ *dstLen = stream.total_out;
+ if (result == Z_STREAM_END) {
+ result = inflateEnd(&stream);
+ }
+ else {
+ // preserve error code
+ inflateEnd(&stream);
+ }
+ return result == Z_OK;
+}
diff --git a/android/utils/uncompress.h b/android/utils/uncompress.h
new file mode 100755
index 0000000..8ba8968
--- /dev/null
+++ b/android/utils/uncompress.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2009 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+*/
+#ifndef ANDROID_UTILS_UNCOMPRESS_H
+#define ANDROID_UTILS_UNCOMPRESS_H
+
+#include "android/utils/compiler.h"
+
+#include <stdint.h>
+#include <stddef.h>
+
+ANDROID_BEGIN_HEADER
+
+// uncompress a gzip file in memory into memory in one function call
+// the dstLen must be large enough to hold all the decompressed data
+//
+// src - pointer to the beginning of the gzip file data
+// srcLen - total number of bytes in the gzip file
+// dst - pointer that receives the decompressed bytes
+// dstLen - number of bytes available for decompressed data
+//
+// return values
+// true - all data decompressed correctly
+// false - dst buffer too small or corrupt zstream or out of memory
+// |dstLen| hold the number of bytes written to dst
+bool uncompress_gzipStream(uint8_t* dst, size_t* dstLen, const uint8_t* src,
+ size_t srcLen);
+
+ANDROID_END_HEADER
+
+#endif /* ANDROID_UTILS_UNCOMPRESS_H */