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 */