diff options
148 files changed, 8316 insertions, 3676 deletions
diff --git a/adb/.clang-format b/adb/.clang-format index 0395c8ec3..673753525 100644 --- a/adb/.clang-format +++ b/adb/.clang-format @@ -2,6 +2,7 @@ BasedOnStyle: Google AllowShortBlocksOnASingleLine: false AllowShortFunctionsOnASingleLine: false +ColumnLimit: 100 CommentPragmas: NOLINT:.* DerivePointerAlignment: false IndentWidth: 4 diff --git a/adb/Android.mk b/adb/Android.mk index 903d1e15a..5ddc93796 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -17,10 +17,20 @@ ADB_COMMON_CFLAGS := \ -Wvla \ -DADB_REVISION='"$(adb_version)"' \ +ADB_COMMON_linux_CFLAGS := \ + -std=c++14 \ + -Wexit-time-destructors \ + +ADB_COMMON_darwin_CFLAGS := \ + -std=c++14 \ + -Wexit-time-destructors \ + # Define windows.h and tchar.h Unicode preprocessor symbols so that # CreateFile(), _tfopen(), etc. map to versions that take wchar_t*, breaking the # build if you accidentally pass char*. Fix by calling like: -# CreateFileW(widen(utf8).c_str()). +# std::wstring path_wide; +# if (!android::base::UTF8ToWide(path_utf8, &path_wide)) { /* error handling */ } +# CreateFileW(path_wide.c_str()); ADB_COMMON_windows_CFLAGS := \ -DUNICODE=1 -D_UNICODE=1 \ @@ -55,7 +65,10 @@ LIBADB_CFLAGS := \ -fvisibility=hidden \ LIBADB_linux_CFLAGS := \ - -std=c++14 \ + $(ADB_COMMON_linux_CFLAGS) \ + +LIBADB_darwin_CFLAGS := \ + $(ADB_COMMON_darwin_CFLAGS) \ LIBADB_windows_CFLAGS := \ $(ADB_COMMON_windows_CFLAGS) \ @@ -110,6 +123,7 @@ LOCAL_MODULE_HOST_OS := darwin linux windows LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=1 LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS) LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS) +LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS) LOCAL_SRC_FILES := \ $(LIBADB_SRC_FILES) \ adb_auth_host.cpp \ @@ -155,6 +169,7 @@ LOCAL_MODULE_HOST_OS := darwin linux windows LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS) LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS) LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS) +LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS) LOCAL_SRC_FILES := \ $(LIBADB_TEST_SRCS) \ services.cpp \ @@ -189,6 +204,7 @@ LOCAL_MODULE := adb_device_tracker_test LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS) LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS) LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS) +LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS) LOCAL_SRC_FILES := test_track_devices.cpp LOCAL_SANITIZE := $(adb_host_sanitize) LOCAL_SHARED_LIBRARIES := libbase @@ -204,7 +220,6 @@ include $(CLEAR_VARS) LOCAL_LDLIBS_linux := -lrt -ldl -lpthread LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon -LOCAL_CFLAGS_darwin := -Wno-sizeof-pointer-memaccess -Wno-unused-parameter # Use wmain instead of main LOCAL_LDFLAGS_windows := -municode @@ -230,6 +245,13 @@ LOCAL_CFLAGS += \ LOCAL_CFLAGS_windows := \ $(ADB_COMMON_windows_CFLAGS) +LOCAL_CFLAGS_linux := \ + $(ADB_COMMON_linux_CFLAGS) \ + +LOCAL_CFLAGS_darwin := \ + $(ADB_COMMON_darwin_CFLAGS) \ + -Wno-sizeof-pointer-memaccess -Wno-unused-parameter \ + LOCAL_MODULE := adb LOCAL_MODULE_TAGS := debug LOCAL_MODULE_HOST_OS := darwin linux windows @@ -273,6 +295,7 @@ LOCAL_SRC_FILES := \ LOCAL_CFLAGS := \ $(ADB_COMMON_CFLAGS) \ + $(ADB_COMMON_linux_CFLAGS) \ -DADB_HOST=0 \ -D_GNU_SOURCE \ -Wno-deprecated-declarations \ @@ -50,7 +50,7 @@ constexpr size_t MAX_PAYLOAD = MAX_PAYLOAD_V2; std::string adb_version(); // Increment this when we want to force users to start a new adb server. -#define ADB_SERVER_VERSION 34 +#define ADB_SERVER_VERSION 35 class atransport; struct usb_handle; diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp index 53095193c..e11bff000 100644 --- a/adb/adb_auth_host.cpp +++ b/adb/adb_auth_host.cpp @@ -311,7 +311,9 @@ static int get_user_keyfilepath(char *filename, size_t len) SystemErrorCodeToString(hr).c_str()); return -1; } - home_str = narrow(path); + if (!android::base::WideToUTF8(path, &home_str)) { + return -1; + } home = home_str.c_str(); } format = "%s\\%s"; diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp index 9586f7c75..cf99df7ac 100644 --- a/adb/adb_trace.cpp +++ b/adb/adb_trace.cpp @@ -163,7 +163,7 @@ void adb_trace_init(char** argv) { } #endif - android::base::InitLogging(argv, AdbLogger); + android::base::InitLogging(argv, &AdbLogger); setup_trace_mask(); VLOG(ADB) << adb_version(); diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp index f7b2e4e29..42f1c7de7 100644 --- a/adb/adb_utils.cpp +++ b/adb/adb_utils.cpp @@ -33,6 +33,7 @@ #include "adb_trace.h" #include "sysdeps.h" +ADB_MUTEX_DEFINE(basename_lock); ADB_MUTEX_DEFINE(dirname_lock); bool getcwd(std::string* s) { @@ -69,13 +70,31 @@ std::string escape_arg(const std::string& s) { } std::string adb_basename(const std::string& path) { - size_t base = path.find_last_of(OS_PATH_SEPARATORS); - return (base != std::string::npos) ? path.substr(base + 1) : path; + // Copy path because basename may modify the string passed in. + std::string result(path); + + // Use lock because basename() may write to a process global and return a + // pointer to that. Note that this locking strategy only works if all other + // callers to dirname in the process also grab this same lock. + adb_mutex_lock(&basename_lock); + + // Note that if std::string uses copy-on-write strings, &str[0] will cause + // the copy to be made, so there is no chance of us accidentally writing to + // the storage for 'path'. + char* name = basename(&result[0]); + + // In case dirname returned a pointer to a process global, copy that string + // before leaving the lock. + result.assign(name); + + adb_mutex_unlock(&basename_lock); + + return result; } std::string adb_dirname(const std::string& path) { // Copy path because dirname may modify the string passed in. - std::string parent_storage(path); + std::string result(path); // Use lock because dirname() may write to a process global and return a // pointer to that. Note that this locking strategy only works if all other @@ -85,11 +104,11 @@ std::string adb_dirname(const std::string& path) { // Note that if std::string uses copy-on-write strings, &str[0] will cause // the copy to be made, so there is no chance of us accidentally writing to // the storage for 'path'. - char* parent = dirname(&parent_storage[0]); + char* parent = dirname(&result[0]); // In case dirname returned a pointer to a process global, copy that string // before leaving the lock. - const std::string result(parent); + result.assign(parent); adb_mutex_unlock(&dirname_lock); @@ -115,9 +134,7 @@ bool mkdirs(const std::string& path) { // - Recursive, so it uses stack space relative to number of directory // components. - const std::string parent(adb_dirname(path)); - - if (directory_exists(parent)) { + if (directory_exists(path)) { return true; } @@ -125,19 +142,21 @@ bool mkdirs(const std::string& path) { // This can happen on Windows when walking up the directory hierarchy and not // finding anything that already exists (unlike POSIX that will eventually // find . or /). + const std::string parent(adb_dirname(path)); + if (parent == path) { errno = ENOENT; return false; } - // Recursively make parent directories of 'parent'. + // Recursively make parent directories of 'path'. if (!mkdirs(parent)) { return false; } - // Now that the parent directory hierarchy of 'parent' has been ensured, + // Now that the parent directory hierarchy of 'path' has been ensured, // create parent itself. - if (adb_mkdir(parent, 0775) == -1) { + if (adb_mkdir(path, 0775) == -1) { // Can't just check for errno == EEXIST because it might be a file that // exists. const int saved_errno = errno; @@ -163,11 +182,8 @@ std::string dump_hex(const void* data, size_t byte_count) { line.push_back(' '); for (size_t i = 0; i < byte_count; ++i) { - int c = p[i]; - if (c < 32 || c > 127) { - c = '.'; - } - line.push_back(c); + int ch = p[i]; + line.push_back(isprint(ch) ? ch : '.'); } return line; diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp index 4a2787a52..93c20cb00 100644 --- a/adb/adb_utils_test.cpp +++ b/adb/adb_utils_test.cpp @@ -102,6 +102,13 @@ TEST(adb_utils, escape_arg) { TEST(adb_utils, adb_basename) { EXPECT_EQ("sh", adb_basename("/system/bin/sh")); EXPECT_EQ("sh", adb_basename("sh")); + EXPECT_EQ("sh", adb_basename("/system/bin/sh/")); +} + +TEST(adb_utils, adb_dirname) { + EXPECT_EQ("/system/bin", adb_dirname("/system/bin/sh")); + EXPECT_EQ(".", adb_dirname("sh")); + EXPECT_EQ("/system/bin", adb_dirname("/system/bin/sh/")); } TEST(adb_utils, parse_host_and_port) { diff --git a/adb/client/main.cpp b/adb/client/main.cpp index a225a5366..8d01af3bf 100644 --- a/adb/client/main.cpp +++ b/adb/client/main.cpp @@ -58,7 +58,12 @@ static std::string GetLogFilePath() { SystemErrorCodeToString(GetLastError()).c_str()); } - return narrow(temp_path) + log_name; + std::string temp_path_utf8; + if (!android::base::WideToUTF8(temp_path, &temp_path_utf8)) { + fatal_errno("cannot convert temporary file path from UTF-16 to UTF-8"); + } + + return temp_path_utf8 + log_name; } #else static const char kNullFileName[] = "/dev/null"; diff --git a/adb/commandline.cpp b/adb/commandline.cpp index ffde6199e..5113eb8a7 100644 --- a/adb/commandline.cpp +++ b/adb/commandline.cpp @@ -41,6 +41,7 @@ #if !defined(_WIN32) #include <signal.h> +#include <sys/ioctl.h> #include <termios.h> #include <unistd.h> #endif @@ -58,8 +59,10 @@ static int install_app(TransportType t, const char* serial, int argc, const char** argv); static int install_multiple_app(TransportType t, const char* serial, int argc, const char** argv); static int uninstall_app(TransportType t, const char* serial, int argc, const char** argv); +static int install_app_legacy(TransportType t, const char* serial, int argc, const char** argv); +static int uninstall_app_legacy(TransportType t, const char* serial, int argc, const char** argv); -static std::string gProductOutPath; +static auto& gProductOutPath = *new std::string(); extern int gListenAll; static std::string product_file(const char *extra) { @@ -109,11 +112,12 @@ static void help() { " (-a preserves file timestamp and mode)\n" " adb sync [ <directory> ] - copy host->device only if changed\n" " (-l means list but don't copy)\n" - " adb shell [-Ttx] - run remote shell interactively\n" - " adb shell [-Ttx] <command> - run remote shell command\n" - " (-T disables PTY allocation)\n" - " (-t forces PTY allocation)\n" - " (-x disables remote exit codes and stdout/stderr separation)\n" + " adb shell [-e escape] [-Tt] [-x] [command]\n" + " - run remote shell command (interactive shell if no command given)\n" + " (-e: choose escape character, or \"none\"; default '~')\n" + " (-T: disable PTY allocation)\n" + " (-t: force PTY allocation)\n" + " (-x: disable remote exit codes and stdout/stderr separation)\n" " adb emu <command> - run emulator console command\n" " adb logcat [ <filter-spec> ] - View device log\n" " adb forward --list - list all forward socket connections.\n" @@ -247,17 +251,17 @@ static int usage() { #if defined(_WIN32) // Implemented in sysdeps_win32.cpp. -void stdin_raw_init(int fd); -void stdin_raw_restore(int fd); +void stdin_raw_init(); +void stdin_raw_restore(); #else static termios g_saved_terminal_state; -static void stdin_raw_init(int fd) { - if (tcgetattr(fd, &g_saved_terminal_state)) return; +static void stdin_raw_init() { + if (tcgetattr(STDIN_FILENO, &g_saved_terminal_state)) return; termios tio; - if (tcgetattr(fd, &tio)) return; + if (tcgetattr(STDIN_FILENO, &tio)) return; cfmakeraw(&tio); @@ -265,11 +269,11 @@ static void stdin_raw_init(int fd) { tio.c_cc[VTIME] = 0; tio.c_cc[VMIN] = 1; - tcsetattr(fd, TCSAFLUSH, &tio); + tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio); } -static void stdin_raw_restore(int fd) { - tcsetattr(fd, TCSAFLUSH, &g_saved_terminal_state); +static void stdin_raw_restore() { + tcsetattr(STDIN_FILENO, TCSAFLUSH, &g_saved_terminal_state); } #endif @@ -358,7 +362,7 @@ static void copy_to_file(int inFd, int outFd) { D("copy_to_file(%d -> %d)", inFd, outFd); if (inFd == STDIN_FILENO) { - stdin_raw_init(STDIN_FILENO); + stdin_raw_init(); #ifdef _WIN32 old_stdin_mode = _setmode(STDIN_FILENO, _O_BINARY); if (old_stdin_mode == -1) { @@ -400,7 +404,7 @@ static void copy_to_file(int inFd, int outFd) { } if (inFd == STDIN_FILENO) { - stdin_raw_restore(STDIN_FILENO); + stdin_raw_restore(); #ifdef _WIN32 if (_setmode(STDIN_FILENO, old_stdin_mode) == -1) { fatal_errno("could not restore stdin mode"); @@ -420,47 +424,110 @@ static void copy_to_file(int inFd, int outFd) { free(buf); } -namespace { +static std::string format_host_command(const char* command, + TransportType type, const char* serial) { + if (serial) { + return android::base::StringPrintf("host-serial:%s:%s", serial, command); + } + + const char* prefix = "host"; + if (type == kTransportUsb) { + prefix = "host-usb"; + } else if (type == kTransportLocal) { + prefix = "host-local"; + } + return android::base::StringPrintf("%s:%s", prefix, command); +} + +// Returns the FeatureSet for the indicated transport. +static FeatureSet GetFeatureSet(TransportType transport_type, const char* serial) { + std::string result, error; + if (adb_query(format_host_command("features", transport_type, serial), &result, &error)) { + return StringToFeatureSet(result); + } + return FeatureSet(); +} + +static void send_window_size_change(int fd, std::unique_ptr<ShellProtocol>& shell) { +#if !defined(_WIN32) + // Old devices can't handle window size changes. + if (shell == nullptr) return; + + winsize ws; + if (ioctl(fd, TIOCGWINSZ, &ws) == -1) return; + + // Send the new window size as human-readable ASCII for debugging convenience. + size_t l = snprintf(shell->data(), shell->data_capacity(), "%dx%d,%dx%d", + ws.ws_row, ws.ws_col, ws.ws_xpixel, ws.ws_ypixel); + shell->Write(ShellProtocol::kIdWindowSizeChange, l + 1); +#endif +} // Used to pass multiple values to the stdin read thread. struct StdinReadArgs { int stdin_fd, write_fd; bool raw_stdin; std::unique_ptr<ShellProtocol> protocol; + char escape_char; }; -} // namespace - // Loops to read from stdin and push the data to the given FD. // The argument should be a pointer to a StdinReadArgs object. This function // will take ownership of the object and delete it when finished. -static void* stdin_read_thread(void* x) { +static void* stdin_read_thread_loop(void* x) { std::unique_ptr<StdinReadArgs> args(reinterpret_cast<StdinReadArgs*>(x)); - int state = 0; - - adb_thread_setname("stdin reader"); -#ifndef _WIN32 - // Mask SIGTTIN in case we're in a backgrounded process +#if !defined(_WIN32) + // Mask SIGTTIN in case we're in a backgrounded process. sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGTTIN); pthread_sigmask(SIG_BLOCK, &sigset, nullptr); #endif - char raw_buffer[1024]; +#if !defined(_WIN32) + // Unblock SIGWINCH for this thread, so our read(2) below will be + // interrupted if the window size changes. + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGWINCH); + pthread_sigmask(SIG_UNBLOCK, &mask, nullptr); +#endif + + // Set up the initial window size. + send_window_size_change(args->stdin_fd, args->protocol); + + char raw_buffer[BUFSIZ]; char* buffer_ptr = raw_buffer; size_t buffer_size = sizeof(raw_buffer); - if (args->protocol) { + if (args->protocol != nullptr) { buffer_ptr = args->protocol->data(); buffer_size = args->protocol->data_capacity(); } + // If we need to parse escape sequences, make life easy. + if (args->raw_stdin && args->escape_char != '\0') { + buffer_size = 1; + } + + enum EscapeState { kMidFlow, kStartOfLine, kInEscape }; + EscapeState state = kStartOfLine; + while (true) { // Use unix_read() rather than adb_read() for stdin. - D("stdin_read_thread(): pre unix_read(fdi=%d,...)", args->stdin_fd); + D("stdin_read_thread_loop(): pre unix_read(fdi=%d,...)", args->stdin_fd); +#if !defined(_WIN32) +#undef read + int r = read(args->stdin_fd, buffer_ptr, buffer_size); + if (r == -1 && errno == EINTR) { + send_window_size_change(args->stdin_fd, args->protocol); + continue; + } +#define read ___xxx_read +#else int r = unix_read(args->stdin_fd, buffer_ptr, buffer_size); - D("stdin_read_thread(): post unix_read(fdi=%d,...)", args->stdin_fd); +#endif + D("stdin_read_thread_loop(): post unix_read(fdi=%d,...)", args->stdin_fd); if (r <= 0) { // Only devices using the shell protocol know to close subprocess // stdin. For older devices we want to just leave the connection @@ -471,36 +538,36 @@ static void* stdin_read_thread(void* x) { } break; } - // If we made stdin raw, check input for the "~." escape sequence. In + // If we made stdin raw, check input for escape sequences. In // this situation signals like Ctrl+C are sent remotely rather than // interpreted locally so this provides an emergency out if the remote // process starts ignoring the signal. SSH also does this, see the // "escape characters" section on the ssh man page for more info. - if (args->raw_stdin) { - for (int n = 0; n < r; n++){ - switch(buffer_ptr[n]) { - case '\n': - state = 1; - break; - case '\r': - state = 1; - break; - case '~': - if(state == 1) { - state++; - } else { - state = 0; - } - break; - case '.': - if(state == 2) { - stdin_raw_restore(args->stdin_fd); - fprintf(stderr,"\n* disconnect *\n"); + if (args->raw_stdin && args->escape_char != '\0') { + char ch = buffer_ptr[0]; + if (ch == args->escape_char) { + if (state == kStartOfLine) { + state = kInEscape; + // Swallow the escape character. + continue; + } else { + state = kMidFlow; + } + } else { + if (state == kInEscape) { + if (ch == '.') { + fprintf(stderr,"\r\n[ disconnected ]\r\n"); + stdin_raw_restore(); exit(0); + } else { + // We swallowed an escape character that wasn't part of + // a valid escape sequence; time to cough it up. + buffer_ptr[0] = args->escape_char; + buffer_ptr[1] = ch; + ++r; } - default: - state = 0; } + state = (ch == '\n' || ch == '\r') ? kStartOfLine : kMidFlow; } } if (args->protocol) { @@ -545,6 +612,7 @@ static std::string ShellServiceString(bool use_shell_protocol, // On success returns the remote exit code if |use_shell_protocol| is true, // 0 otherwise. On failure returns 1. static int RemoteShell(bool use_shell_protocol, const std::string& type_arg, + char escape_char, const std::string& command) { std::string service_string = ShellServiceString(use_shell_protocol, type_arg, command); @@ -570,57 +638,135 @@ static int RemoteShell(bool use_shell_protocol, const std::string& type_arg, args->stdin_fd = STDIN_FILENO; args->write_fd = fd; args->raw_stdin = raw_stdin; + args->escape_char = escape_char; if (use_shell_protocol) { args->protocol.reset(new ShellProtocol(args->write_fd)); } - if (raw_stdin) { - stdin_raw_init(STDIN_FILENO); - } + if (raw_stdin) stdin_raw_init(); - int exit_code = 0; - if (!adb_thread_create(stdin_read_thread, args)) { +#if !defined(_WIN32) + // Ensure our process is notified if the local window size changes. + // We use sigaction(2) to ensure that the SA_RESTART flag is not set, + // because the whole reason we're sending signals is to unblock the read(2)! + // That also means we don't need to do anything in the signal handler: + // the side effect of delivering the signal is all we need. + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = [](int) {}; + sa.sa_flags = 0; + sigaction(SIGWINCH, &sa, nullptr); + + // Now block SIGWINCH in this thread (the main thread) and all threads spawned + // from it. The stdin read thread will unblock this signal to ensure that it's + // the thread that receives the signal. + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGWINCH); + pthread_sigmask(SIG_BLOCK, &mask, nullptr); +#endif + + // TODO: combine read_and_dump with stdin_read_thread to make life simpler? + int exit_code = 1; + if (!adb_thread_create(stdin_read_thread_loop, args)) { PLOG(ERROR) << "error starting stdin read thread"; - exit_code = 1; delete args; } else { exit_code = read_and_dump(fd, use_shell_protocol); } - if (raw_stdin) { - stdin_raw_restore(STDIN_FILENO); - } + // TODO: properly exit stdin_read_thread_loop and close |fd|. - // TODO(dpursell): properly exit stdin_read_thread and close |fd|. + // TODO: we should probably install signal handlers for this. + // TODO: can we use atexit? even on Windows? + if (raw_stdin) stdin_raw_restore(); return exit_code; } +static int adb_shell(int argc, const char** argv, + TransportType transport_type, const char* serial) { + FeatureSet features = GetFeatureSet(transport_type, serial); -static std::string format_host_command(const char* command, TransportType type, const char* serial) { - if (serial) { - return android::base::StringPrintf("host-serial:%s:%s", serial, command); + bool use_shell_protocol = CanUseFeature(features, kFeatureShell2); + if (!use_shell_protocol) { + D("shell protocol not supported, using raw data transfer"); + } else { + D("using shell protocol"); } - const char* prefix = "host"; - if (type == kTransportUsb) { - prefix = "host-usb"; - } else if (type == kTransportLocal) { - prefix = "host-local"; + // Parse shell-specific command-line options. + // argv[0] is always "shell". + --argc; + ++argv; + int t_arg_count = 0; + char escape_char = '~'; + while (argc) { + if (!strcmp(argv[0], "-e")) { + if (argc < 2 || !(strlen(argv[1]) == 1 || strcmp(argv[1], "none") == 0)) { + fprintf(stderr, "error: -e requires a single-character argument or 'none'\n"); + return 1; + } + escape_char = (strcmp(argv[1], "none") == 0) ? 0 : argv[1][0]; + argc -= 2; + argv += 2; + } else if (!strcmp(argv[0], "-T") || !strcmp(argv[0], "-t")) { + if (!CanUseFeature(features, kFeatureShell2)) { + fprintf(stderr, "error: target doesn't support PTY args -Tt\n"); + return 1; + } + // Like ssh, -t arguments are cumulative so that multiple -t's + // are needed to force a PTY. + if (argv[0][1] == 't') { + ++t_arg_count; + } else { + t_arg_count = -1; + } + --argc; + ++argv; + } else if (!strcmp(argv[0], "-x")) { + use_shell_protocol = false; + --argc; + ++argv; + } else { + break; + } } - return android::base::StringPrintf("%s:%s", prefix, command); -} -// Returns the FeatureSet for the indicated transport. -static FeatureSet GetFeatureSet(TransportType transport_type, - const char* serial) { - std::string result, error; + std::string shell_type_arg; + if (CanUseFeature(features, kFeatureShell2)) { + if (t_arg_count < 0) { + shell_type_arg = kShellServiceArgRaw; + } else if (t_arg_count == 0) { + // If stdin isn't a TTY, default to a raw shell; this lets + // things like `adb shell < my_script.sh` work as expected. + // Otherwise leave |shell_type_arg| blank which uses PTY for + // interactive shells and raw for non-interactive. + if (!unix_isatty(STDIN_FILENO)) { + shell_type_arg = kShellServiceArgRaw; + } + } else if (t_arg_count == 1) { + // A single -t arg isn't enough to override implicit -T. + if (!unix_isatty(STDIN_FILENO)) { + fprintf(stderr, + "Remote PTY will not be allocated because stdin is not a terminal.\n" + "Use multiple -t options to force remote PTY allocation.\n"); + shell_type_arg = kShellServiceArgRaw; + } else { + shell_type_arg = kShellServiceArgPty; + } + } else { + shell_type_arg = kShellServiceArgPty; + } + } - if (adb_query(format_host_command("features", transport_type, serial), - &result, &error)) { - return StringToFeatureSet(result); + std::string command; + if (argc) { + // We don't escape here, just like ssh(1). http://b/20564385. + command = android::base::Join(std::vector<const char*>(argv, argv + argc), ' '); } - return FeatureSet(); + + return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command); } static int adb_download_buffer(const char *service, const char *fn, const void* data, unsigned sz, @@ -928,14 +1074,14 @@ static int logcat(TransportType transport, const char* serial, int argc, const c } static int backup(int argc, const char** argv) { - const char* filename = "./backup.ab"; + const char* filename = "backup.ab"; /* find, extract, and use any -f argument */ for (int i = 1; i < argc; i++) { if (!strcmp("-f", argv[i])) { if (i == argc-1) { - fprintf(stderr, "adb: -f passed with no filename\n"); - return usage(); + fprintf(stderr, "adb: backup -f passed with no filename.\n"); + return EXIT_FAILURE; } filename = argv[i+1]; for (int j = i+2; j <= argc; ) { @@ -946,15 +1092,18 @@ static int backup(int argc, const char** argv) { } } - /* bare "adb backup" or "adb backup -f filename" are not valid invocations */ - if (argc < 2) return usage(); + // Bare "adb backup" or "adb backup -f filename" are not valid invocations --- + // a list of packages is required. + if (argc < 2) { + fprintf(stderr, "adb: backup either needs a list of packages or -all/-shared.\n"); + return EXIT_FAILURE; + } adb_unlink(filename); - mkdirs(filename); int outFd = adb_creat(filename, 0640); if (outFd < 0) { - fprintf(stderr, "adb: unable to open file %s\n", filename); - return -1; + fprintf(stderr, "adb: backup unable to create file '%s': %s\n", filename, strerror(errno)); + return EXIT_FAILURE; } std::string cmd = "backup:"; @@ -970,15 +1119,17 @@ static int backup(int argc, const char** argv) { if (fd < 0) { fprintf(stderr, "adb: unable to connect for backup: %s\n", error.c_str()); adb_close(outFd); - return -1; + return EXIT_FAILURE; } - printf("Now unlock your device and confirm the backup operation.\n"); + printf("Now unlock your device and confirm the backup operation...\n"); + fflush(stdout); + copy_to_file(fd, outFd); adb_close(fd); adb_close(outFd); - return 0; + return EXIT_SUCCESS; } static int restore(int argc, const char** argv) { @@ -1343,94 +1494,8 @@ int adb_commandline(int argc, const char **argv) { else if (!strcmp(argv[0], "emu")) { return adb_send_emulator_command(argc, argv, serial); } - else if (!strcmp(argv[0], "shell") || !strcmp(argv[0], "hell")) { - char h = (argv[0][0] == 'h'); - - FeatureSet features = GetFeatureSet(transport_type, serial); - - bool use_shell_protocol = CanUseFeature(features, kFeatureShell2); - if (!use_shell_protocol) { - D("shell protocol not supported, using raw data transfer"); - } else { - D("using shell protocol"); - } - - // Parse shell-specific command-line options. - // argv[0] is always "shell". - --argc; - ++argv; - int t_arg_count = 0; - while (argc) { - if (!strcmp(argv[0], "-T") || !strcmp(argv[0], "-t")) { - if (!CanUseFeature(features, kFeatureShell2)) { - fprintf(stderr, "error: target doesn't support PTY args -Tt\n"); - return 1; - } - // Like ssh, -t arguments are cumulative so that multiple -t's - // are needed to force a PTY. - if (argv[0][1] == 't') { - ++t_arg_count; - } else { - t_arg_count = -1; - } - --argc; - ++argv; - } else if (!strcmp(argv[0], "-x")) { - use_shell_protocol = false; - --argc; - ++argv; - } else { - break; - } - } - - std::string shell_type_arg; - if (CanUseFeature(features, kFeatureShell2)) { - if (t_arg_count < 0) { - shell_type_arg = kShellServiceArgRaw; - } else if (t_arg_count == 0) { - // If stdin isn't a TTY, default to a raw shell; this lets - // things like `adb shell < my_script.sh` work as expected. - // Otherwise leave |shell_type_arg| blank which uses PTY for - // interactive shells and raw for non-interactive. - if (!unix_isatty(STDIN_FILENO)) { - shell_type_arg = kShellServiceArgRaw; - } - } else if (t_arg_count == 1) { - // A single -t arg isn't enough to override implicit -T. - if (!unix_isatty(STDIN_FILENO)) { - fprintf(stderr, - "Remote PTY will not be allocated because stdin is not a terminal.\n" - "Use multiple -t options to force remote PTY allocation.\n"); - shell_type_arg = kShellServiceArgRaw; - } else { - shell_type_arg = kShellServiceArgPty; - } - } else { - shell_type_arg = kShellServiceArgPty; - } - } - - std::string command; - if (argc) { - // We don't escape here, just like ssh(1). http://b/20564385. - command = android::base::Join( - std::vector<const char*>(argv, argv + argc), ' '); - } - - if (h) { - printf("\x1b[41;33m"); - fflush(stdout); - } - - r = RemoteShell(use_shell_protocol, shell_type_arg, command); - - if (h) { - printf("\x1b[0m"); - fflush(stdout); - } - - return r; + else if (!strcmp(argv[0], "shell")) { + return adb_shell(argc, argv, transport_type, serial); } else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) { int exec_in = !strcmp(argv[0], "exec-in"); @@ -1585,7 +1650,11 @@ int adb_commandline(int argc, const char **argv) { } else if (!strcmp(argv[0], "install")) { if (argc < 2) return usage(); - return install_app(transport_type, serial, argc, argv); + FeatureSet features = GetFeatureSet(transport_type, serial); + if (CanUseFeature(features, kFeatureCmd)) { + return install_app(transport_type, serial, argc, argv); + } + return install_app_legacy(transport_type, serial, argc, argv); } else if (!strcmp(argv[0], "install-multiple")) { if (argc < 2) return usage(); @@ -1593,7 +1662,11 @@ int adb_commandline(int argc, const char **argv) { } else if (!strcmp(argv[0], "uninstall")) { if (argc < 2) return usage(); - return uninstall_app(transport_type, serial, argc, argv); + FeatureSet features = GetFeatureSet(transport_type, serial); + if (CanUseFeature(features, kFeatureCmd)) { + return uninstall_app(transport_type, serial, argc, argv); + } + return uninstall_app_legacy(transport_type, serial, argc, argv); } else if (!strcmp(argv[0], "sync")) { std::string src; @@ -1701,86 +1774,83 @@ int adb_commandline(int argc, const char **argv) { return 1; } -static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) { - std::string cmd = "pm"; - +static int uninstall_app(TransportType transport, const char* serial, int argc, const char** argv) { + // 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device + std::string cmd = "cmd package"; while (argc-- > 0) { + // deny the '-k' option until the remaining data/cache can be removed with adb/UI + if (strcmp(*argv, "-k") == 0) { + printf( + "The -k option uninstalls the application while retaining the data/cache.\n" + "At the moment, there is no way to remove the remaining data.\n" + "You will have to reinstall the application with the same signature, and fully uninstall it.\n" + "If you truly wish to continue, execute 'adb shell cmd package uninstall -k'.\n"); + return EXIT_FAILURE; + } cmd += " " + escape_arg(*argv++); } - // TODO(dpursell): add command-line arguments to install/uninstall to - // manually disable shell protocol if needed. return send_shell_command(transport, serial, cmd, false); } -static int uninstall_app(TransportType transport, const char* serial, int argc, const char** argv) { - /* if the user choose the -k option, we refuse to do it until devices are - out with the option to uninstall the remaining data somehow (adb/ui) */ - if (argc == 3 && strcmp(argv[1], "-k") == 0) - { - printf( - "The -k option uninstalls the application while retaining the data/cache.\n" - "At the moment, there is no way to remove the remaining data.\n" - "You will have to reinstall the application with the same signature, and fully uninstall it.\n" - "If you truly wish to continue, execute 'adb shell pm uninstall -k %s'\n", argv[2]); - return -1; +static int install_app(TransportType transport, const char* serial, int argc, const char** argv) { + // The last argument must be the APK file + const char* file = argv[argc - 1]; + const char* dot = strrchr(file, '.'); + bool found_apk = false; + struct stat sb; + if (dot && !strcasecmp(dot, ".apk")) { + if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) { + fprintf(stderr, "Invalid APK file: %s\n", file); + return EXIT_FAILURE; + } + found_apk = true; } - /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */ - return pm_command(transport, serial, argc, argv); -} + if (!found_apk) { + fprintf(stderr, "Missing APK file\n"); + return EXIT_FAILURE; + } -static int delete_file(TransportType transport, const char* serial, const std::string& filename) { - std::string cmd = "rm -f " + escape_arg(filename); - return send_shell_command(transport, serial, cmd, false); -} + int localFd = adb_open(file, O_RDONLY); + if (localFd < 0) { + fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno)); + return 1; + } -static int install_app(TransportType transport, const char* serial, int argc, const char** argv) { - static const char *const DATA_DEST = "/data/local/tmp/%s"; - static const char *const SD_DEST = "/sdcard/tmp/%s"; - const char* where = DATA_DEST; - int i; - struct stat sb; + std::string error; + std::string cmd = "exec:cmd package"; - for (i = 1; i < argc; i++) { - if (!strcmp(argv[i], "-s")) { - where = SD_DEST; - } + // don't copy the APK name, but, copy the rest of the arguments as-is + while (argc-- > 1) { + cmd += " " + escape_arg(std::string(*argv++)); } - // Find last APK argument. - // All other arguments passed through verbatim. - int last_apk = -1; - for (i = argc - 1; i >= 0; i--) { - const char* file = argv[i]; - const char* dot = strrchr(file, '.'); - if (dot && !strcasecmp(dot, ".apk")) { - if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) { - fprintf(stderr, "Invalid APK file: %s\n", file); - return -1; - } + // add size parameter [required for streaming installs] + // do last to override any user specified value + cmd += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size)); - last_apk = i; - break; - } + int remoteFd = adb_connect(cmd, &error); + if (remoteFd < 0) { + fprintf(stderr, "Connect error for write: %s\n", error.c_str()); + adb_close(localFd); + return 1; } - if (last_apk == -1) { - fprintf(stderr, "Missing APK file\n"); - return -1; - } + char buf[BUFSIZ]; + copy_to_file(localFd, remoteFd); + read_status_line(remoteFd, buf, sizeof(buf)); - int result = -1; - std::vector<const char*> apk_file = {argv[last_apk]}; - std::string apk_dest = android::base::StringPrintf( - where, adb_basename(argv[last_apk]).c_str()); - if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk; - argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */ - result = pm_command(transport, serial, argc, argv); + adb_close(localFd); + adb_close(remoteFd); -cleanup_apk: - delete_file(transport, serial, apk_dest); - return result; + if (strncmp("Success", buf, 7)) { + fprintf(stderr, "Failed to write %s\n", file); + fputs(buf, stderr); + return 1; + } + fputs(buf, stderr); + return 0; } static int install_multiple_app(TransportType transport, const char* serial, int argc, @@ -1799,7 +1869,7 @@ static int install_multiple_app(TransportType transport, const char* serial, int if (dot && !strcasecmp(dot, ".apk")) { if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) { fprintf(stderr, "Invalid APK file: %s\n", file); - return -1; + return EXIT_FAILURE; } total_size += sb.st_size; @@ -1824,7 +1894,7 @@ static int install_multiple_app(TransportType transport, const char* serial, int int fd = adb_connect(cmd, &error); if (fd < 0) { fprintf(stderr, "Connect error for create: %s\n", error.c_str()); - return -1; + return EXIT_FAILURE; } char buf[BUFSIZ]; read_status_line(fd, buf, sizeof(buf)); @@ -1842,7 +1912,7 @@ static int install_multiple_app(TransportType transport, const char* serial, int if (session_id < 0) { fprintf(stderr, "Failed to create session\n"); fputs(buf, stderr); - return -1; + return EXIT_FAILURE; } // Valid session, now stream the APKs @@ -1897,7 +1967,7 @@ finalize_session: fd = adb_connect(service, &error); if (fd < 0) { fprintf(stderr, "Connect error for finalize: %s\n", error.c_str()); - return -1; + return EXIT_FAILURE; } read_status_line(fd, buf, sizeof(buf)); adb_close(fd); @@ -1908,6 +1978,88 @@ finalize_session: } else { fprintf(stderr, "Failed to finalize session\n"); fputs(buf, stderr); - return -1; + return EXIT_FAILURE; } } + +static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) { + std::string cmd = "pm"; + + while (argc-- > 0) { + cmd += " " + escape_arg(*argv++); + } + + return send_shell_command(transport, serial, cmd, false); +} + +static int uninstall_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) { + /* if the user choose the -k option, we refuse to do it until devices are + out with the option to uninstall the remaining data somehow (adb/ui) */ + int i; + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-k")) { + printf( + "The -k option uninstalls the application while retaining the data/cache.\n" + "At the moment, there is no way to remove the remaining data.\n" + "You will have to reinstall the application with the same signature, and fully uninstall it.\n" + "If you truly wish to continue, execute 'adb shell pm uninstall -k'\n."); + return EXIT_FAILURE; + } + } + + /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */ + return pm_command(transport, serial, argc, argv); +} + +static int delete_file(TransportType transport, const char* serial, const std::string& filename) { + std::string cmd = "rm -f " + escape_arg(filename); + return send_shell_command(transport, serial, cmd, false); +} + +static int install_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) { + static const char *const DATA_DEST = "/data/local/tmp/%s"; + static const char *const SD_DEST = "/sdcard/tmp/%s"; + const char* where = DATA_DEST; + int i; + struct stat sb; + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-s")) { + where = SD_DEST; + } + } + + // Find last APK argument. + // All other arguments passed through verbatim. + int last_apk = -1; + for (i = argc - 1; i >= 0; i--) { + const char* file = argv[i]; + const char* dot = strrchr(file, '.'); + if (dot && !strcasecmp(dot, ".apk")) { + if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) { + fprintf(stderr, "Invalid APK file: %s\n", file); + return EXIT_FAILURE; + } + + last_apk = i; + break; + } + } + + if (last_apk == -1) { + fprintf(stderr, "Missing APK file\n"); + return EXIT_FAILURE; + } + + int result = -1; + std::vector<const char*> apk_file = {argv[last_apk]}; + std::string apk_dest = android::base::StringPrintf( + where, adb_basename(argv[last_apk]).c_str()); + if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk; + argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */ + result = pm_command(transport, serial, argc, argv); + +cleanup_apk: + delete_file(transport, serial, apk_dest); + return result; +} diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp index 8c3ca63c8..f4e054e3c 100644 --- a/adb/daemon/main.cpp +++ b/adb/daemon/main.cpp @@ -142,11 +142,13 @@ int adbd_main(int server_port) { // AID_SDCARD_R to allow reading from the SD card // AID_SDCARD_RW to allow writing to the SD card // AID_NET_BW_STATS to read out qtaguid statistics + // AID_READPROC for reading /proc entries across UID boundaries gid_t groups[] = {AID_ADB, AID_LOG, AID_INPUT, AID_INET, AID_NET_BT, AID_NET_BT_ADMIN, - AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS}; + AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS, + AID_READPROC }; if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) != 0) { - PLOG(FATAL) << "Could not set supplental groups"; + PLOG(FATAL) << "Could not set supplemental groups"; } /* don't listen on a port (default 5037) if running in secure mode */ @@ -166,7 +168,7 @@ int adbd_main(int server_port) { } else { if (root_seclabel != nullptr) { if (setcon(root_seclabel) < 0) { - LOG(FATAL) << "Could not set selinux context"; + LOG(FATAL) << "Could not set SELinux context"; } } std::string error; diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp index 4458c85ec..46547b91f 100644 --- a/adb/fdevent.cpp +++ b/adb/fdevent.cpp @@ -54,7 +54,7 @@ int SHELL_EXIT_NOTIFY_FD = -1; struct PollNode { fdevent* fde; - pollfd pollfd; + ::pollfd pollfd; PollNode(fdevent* fde) : fde(fde) { memset(&pollfd, 0, sizeof(pollfd)); @@ -70,8 +70,8 @@ struct PollNode { // All operations to fdevent should happen only in the main thread. // That's why we don't need a lock for fdevent. -static std::unordered_map<int, PollNode> g_poll_node_map; -static std::list<fdevent*> g_pending_list; +static auto& g_poll_node_map = *new std::unordered_map<int, PollNode>(); +static auto& g_pending_list = *new std::list<fdevent*>(); static bool main_thread_valid; static pthread_t main_thread; diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp index 7a605808f..a2d2a66bf 100644 --- a/adb/file_sync_client.cpp +++ b/adb/file_sync_client.cpp @@ -28,6 +28,7 @@ #include <unistd.h> #include <utime.h> +#include <functional> #include <memory> #include <vector> @@ -208,6 +209,17 @@ class SyncConnection { line_printer_.Print(s, LinePrinter::FULL); } + void Warning(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) { + std::string s = "adb: warning: "; + + va_list ap; + va_start(ap, fmt); + android::base::StringAppendV(&s, fmt, ap); + va_end(ap); + + line_printer_.Print(s, LinePrinter::FULL); + } + uint64_t total_bytes; // TODO: add a char[max] buffer here, to replace syncsendbuf... @@ -230,9 +242,10 @@ class SyncConnection { } }; -typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char* name, void* cookie); +typedef void (sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char* name); -static bool sync_ls(SyncConnection& sc, const char* path, sync_ls_cb func, void* cookie) { +static bool sync_ls(SyncConnection& sc, const char* path, + std::function<sync_ls_cb> func) { if (!sc.SendRequest(ID_LIST, path)) return false; while (true) { @@ -249,7 +262,7 @@ static bool sync_ls(SyncConnection& sc, const char* path, sync_ls_cb func, void* if (!ReadFdExactly(sc.fd, buf, len)) return false; buf[len] = 0; - func(msg.dent.mode, msg.dent.size, msg.dent.time, buf, cookie); + func(msg.dent.mode, msg.dent.size, msg.dent.time, buf); } } @@ -390,7 +403,12 @@ static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath) if (!sc.SendRequest(ID_RECV, rpath)) return false; adb_unlink(lpath); - mkdirs(lpath); + if (!mkdirs(adb_dirname(lpath))) { + sc.Error("failed to create parent directory '%s': %s", + adb_dirname(lpath).c_str(), strerror(errno)); + return false; + } + int lfd = adb_creat(lpath, 0644); if (lfd < 0) { sc.Error("cannot create '%s': %s", lpath, strerror(errno)); @@ -448,180 +466,179 @@ static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath) return true; } -static void do_sync_ls_cb(unsigned mode, unsigned size, unsigned time, - const char* name, void* /*cookie*/) { - printf("%08x %08x %08x %s\n", mode, size, time, name); -} - bool do_sync_ls(const char* path) { SyncConnection sc; if (!sc.IsValid()) return false; - return sync_ls(sc, path, do_sync_ls_cb, 0); + return sync_ls(sc, path, [](unsigned mode, unsigned size, unsigned time, + const char* name) { + printf("%08x %08x %08x %s\n", mode, size, time, name); + }); } struct copyinfo { - copyinfo *next; - const char *src; - const char *dst; + std::string lpath; + std::string rpath; unsigned int time; unsigned int mode; uint64_t size; - int flag; + bool skip; }; -static copyinfo* mkcopyinfo(const char* spath, const char* dpath, const char* name, int isdir) { - int slen = strlen(spath); - int dlen = strlen(dpath); - int nlen = strlen(name); - int ssize = slen + nlen + 2; - int dsize = dlen + nlen + 2; - - copyinfo *ci = reinterpret_cast<copyinfo*>(malloc(sizeof(copyinfo) + ssize + dsize)); - if (ci == 0) { - fprintf(stderr, "out of memory\n"); - abort(); - } - - ci->next = 0; - ci->time = 0; - ci->mode = 0; - ci->size = 0; - ci->flag = 0; - ci->src = (const char*)(ci + 1); - ci->dst = ci->src + ssize; - snprintf((char*) ci->src, ssize, isdir ? "%s%s/" : "%s%s", spath, name); - snprintf((char*) ci->dst, dsize, isdir ? "%s%s/" : "%s%s", dpath, name); - - return ci; +static void ensure_trailing_separator(std::string& lpath, std::string& rpath) { + if (!adb_is_separator(lpath.back())) { + lpath.push_back(OS_PATH_SEPARATOR); + } + if (rpath.back() != '/') { + rpath.push_back('/'); + } +} + +static copyinfo mkcopyinfo(std::string lpath, std::string rpath, + const std::string& name, unsigned int mode) { + copyinfo result; + result.lpath = std::move(lpath); + result.rpath = std::move(rpath); + ensure_trailing_separator(result.lpath, result.rpath); + result.lpath.append(name); + result.rpath.append(name); + + if (S_ISDIR(mode)) { + ensure_trailing_separator(result.lpath, result.rpath); + } + + result.time = 0; + result.mode = mode; + result.size = 0; + result.skip = false; + return result; } static bool IsDotOrDotDot(const char* name) { return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); } -static int local_build_list(SyncConnection& sc, - copyinfo** filelist, const char* lpath, const char* rpath) { - copyinfo *dirlist = 0; - copyinfo *ci, *next; - - std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(lpath), closedir); +static bool local_build_list(SyncConnection& sc, std::vector<copyinfo>* filelist, + const std::string& lpath, + const std::string& rpath) { + std::vector<copyinfo> dirlist; + std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(lpath.c_str()), closedir); if (!dir) { - sc.Error("cannot open '%s': %s", lpath, strerror(errno)); - return -1; + sc.Error("cannot open '%s': %s", lpath.c_str(), strerror(errno)); + return false; } + bool empty_dir = true; dirent* de; while ((de = readdir(dir.get()))) { - if (IsDotOrDotDot(de->d_name)) continue; - - char stat_path[PATH_MAX]; - if (strlen(lpath) + strlen(de->d_name) + 1 > sizeof(stat_path)) { - sc.Error("skipping long path '%s%s'", lpath, de->d_name); + if (IsDotOrDotDot(de->d_name)) { continue; } - strcpy(stat_path, lpath); - strcat(stat_path, de->d_name); + + empty_dir = false; + std::string stat_path = lpath + de->d_name; struct stat st; - if (!lstat(stat_path, &st)) { - if (S_ISDIR(st.st_mode)) { - ci = mkcopyinfo(lpath, rpath, de->d_name, 1); - ci->next = dirlist; - dirlist = ci; + if (lstat(stat_path.c_str(), &st) == -1) { + sc.Error("cannot lstat '%s': %s", stat_path.c_str(), + strerror(errno)); + continue; + } + + copyinfo ci = mkcopyinfo(lpath, rpath, de->d_name, st.st_mode); + if (S_ISDIR(st.st_mode)) { + dirlist.push_back(ci); + } else { + if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) { + sc.Error("skipping special file '%s'", lpath.c_str()); } else { - ci = mkcopyinfo(lpath, rpath, de->d_name, 0); - if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) { - sc.Error("skipping special file '%s'", ci->src); - free(ci); - } else { - ci->time = st.st_mtime; - ci->mode = st.st_mode; - ci->size = st.st_size; - ci->next = *filelist; - *filelist = ci; - } + ci.time = st.st_mtime; + ci.size = st.st_size; + filelist->push_back(ci); } - } else { - sc.Error("cannot lstat '%s': %s",stat_path , strerror(errno)); } } // Close this directory and recurse. dir.reset(); - for (ci = dirlist; ci != 0; ci = next) { - next = ci->next; - local_build_list(sc, filelist, ci->src, ci->dst); - free(ci); + + // Add the current directory to the list if it was empty, to ensure that + // it gets created. + if (empty_dir) { + // TODO(b/25566053): Make pushing empty directories work. + // TODO(b/25457350): We don't preserve permissions on directories. + sc.Warning("skipping empty directory '%s'", lpath.c_str()); + copyinfo ci = mkcopyinfo(adb_dirname(lpath), adb_dirname(rpath), + adb_basename(lpath), S_IFDIR); + ci.skip = true; + filelist->push_back(ci); + return true; + } + + for (const copyinfo& ci : dirlist) { + local_build_list(sc, filelist, ci.lpath, ci.rpath); } - return 0; + return true; } -static bool copy_local_dir_remote(SyncConnection& sc, const char* lpath, const char* rpath, - bool check_timestamps, bool list_only) { - copyinfo *filelist = 0; - copyinfo *ci, *next; +static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath, + std::string rpath, bool check_timestamps, + bool list_only) { + // Make sure that both directory paths end in a slash. + // Both paths are known to be nonempty, so we don't need to check. + ensure_trailing_separator(lpath, rpath); + + // Recursively build the list of files to copy. + std::vector<copyinfo> filelist; int pushed = 0; int skipped = 0; - - if ((lpath[0] == 0) || (rpath[0] == 0)) return false; - if (lpath[strlen(lpath) - 1] != '/') { - int tmplen = strlen(lpath)+2; - char *tmp = reinterpret_cast<char*>(malloc(tmplen)); - if(tmp == 0) return false; - snprintf(tmp, tmplen, "%s/",lpath); - lpath = tmp; - } - if (rpath[strlen(rpath) - 1] != '/') { - int tmplen = strlen(rpath)+2; - char *tmp = reinterpret_cast<char*>(malloc(tmplen)); - if(tmp == 0) return false; - snprintf(tmp, tmplen, "%s/",rpath); - rpath = tmp; - } - - if (local_build_list(sc, &filelist, lpath, rpath)) { + if (!local_build_list(sc, &filelist, lpath, rpath)) { return false; } if (check_timestamps) { - for (ci = filelist; ci != 0; ci = ci->next) { - if (!sc.SendRequest(ID_STAT, ci->dst)) return false; + for (const copyinfo& ci : filelist) { + if (!sc.SendRequest(ID_STAT, ci.rpath.c_str())) { + return false; + } } - for(ci = filelist; ci != 0; ci = ci->next) { + for (copyinfo& ci : filelist) { unsigned int timestamp, mode, size; - if (!sync_finish_stat(sc, ×tamp, &mode, &size)) return false; - if (size == ci->size) { + if (!sync_finish_stat(sc, ×tamp, &mode, &size)) { + return false; + } + if (size == ci.size) { /* for links, we cannot update the atime/mtime */ - if ((S_ISREG(ci->mode & mode) && timestamp == ci->time) || - (S_ISLNK(ci->mode & mode) && timestamp >= ci->time)) { - ci->flag = 1; + if ((S_ISREG(ci.mode & mode) && timestamp == ci.time) || + (S_ISLNK(ci.mode & mode) && timestamp >= ci.time)) { + ci.skip = true; } } } } - for (ci = filelist; ci != 0; ci = next) { - next = ci->next; - if (ci->flag == 0) { + + for (const copyinfo& ci : filelist) { + if (!ci.skip) { if (list_only) { - fprintf(stderr, "would push: %s -> %s\n", ci->src, ci->dst); + sc.Error("would push: %s -> %s", ci.lpath.c_str(), + ci.rpath.c_str()); } else { - if (!sync_send(sc, ci->src, ci->dst, ci->time, ci->mode)) { - return false; + if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, + ci.mode)) { + return false; } } pushed++; } else { skipped++; } - free(ci); } - sc.Printf("%s: %d file%s pushed. %d file%s skipped.%s\n", rpath, pushed, - (pushed == 1) ? "" : "s", skipped, (skipped == 1) ? "" : "s", - sc.TransferRate().c_str()); + sc.Printf("%s: %d file%s pushed. %d file%s skipped.%s\n", rpath.c_str(), + pushed, (pushed == 1) ? "" : "s", skipped, + (skipped == 1) ? "" : "s", sc.TransferRate().c_str()); return true; } @@ -630,9 +647,10 @@ bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) { if (!sc.IsValid()) return false; bool success = true; - unsigned mode; - if (!sync_stat(sc, dst, nullptr, &mode, nullptr)) return false; - bool dst_isdir = mode != 0 && S_ISDIR(mode); + unsigned dst_mode; + if (!sync_stat(sc, dst, nullptr, &dst_mode, nullptr)) return false; + bool dst_exists = (dst_mode != 0); + bool dst_isdir = S_ISDIR(dst_mode); if (!dst_isdir) { if (srcs.size() > 1) { @@ -640,7 +658,10 @@ bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) { return false; } else { size_t dst_len = strlen(dst); - if (dst[dst_len - 1] == '/') { + + // A path that ends with a slash doesn't have to be a directory if + // it doesn't exist yet. + if (dst[dst_len - 1] == '/' && dst_exists) { sc.Error("failed to access '%s': Not a directory", dst); return false; } @@ -650,19 +671,37 @@ bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) { for (const char* src_path : srcs) { const char* dst_path = dst; struct stat st; - if (stat(src_path, &st)) { + if (stat(src_path, &st) == -1) { sc.Error("cannot stat '%s': %s", src_path, strerror(errno)); success = false; continue; } if (S_ISDIR(st.st_mode)) { - success &= copy_local_dir_remote(sc, src_path, dst, false, false); + std::string dst_dir = dst; + + // If the destination path existed originally, the source directory + // should be copied as a child of the destination. + if (dst_exists) { + if (!dst_isdir) { + sc.Error("target '%s' is not a directory", dst); + return false; + } + // dst is a POSIX path, so we don't want to use the sysdeps + // helpers here. + if (dst_dir.back() != '/') { + dst_dir.push_back('/'); + } + dst_dir.append(adb_basename(src_path)); + } + + success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), + false, false); continue; } std::string path_holder; - if (mode != 0 && S_ISDIR(mode)) { + if (dst_isdir) { // If we're copying a local file to a remote directory, // we really want to copy to remote_dir + "/" + local_filename. path_holder = android::base::StringPrintf( @@ -676,124 +715,119 @@ bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) { return success; } -struct sync_ls_build_list_cb_args { - SyncConnection* sc; - copyinfo** filelist; - copyinfo** dirlist; - const char* rpath; - const char* lpath; -}; +static bool remote_build_list(SyncConnection& sc, + std::vector<copyinfo>* filelist, + const std::string& rpath, + const std::string& lpath) { + std::vector<copyinfo> dirlist; + bool empty_dir = true; -static void sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time, - const char* name, void* cookie) -{ - sync_ls_build_list_cb_args* args = static_cast<sync_ls_build_list_cb_args*>(cookie); - copyinfo *ci; + // Put the files/dirs in rpath on the lists. + auto callback = [&](unsigned mode, unsigned size, unsigned time, + const char* name) { + if (IsDotOrDotDot(name)) { + return; + } + + // We found a child that isn't '.' or '..'. + empty_dir = false; + + copyinfo ci = mkcopyinfo(lpath, rpath, name, mode); + if (S_ISDIR(mode)) { + dirlist.push_back(ci); + } else if (S_ISREG(mode) || S_ISLNK(mode)) { + ci.time = time; + ci.size = size; + filelist->push_back(ci); + } else { + sc.Warning("skipping special file '%s'\n", name); + } + }; - if (S_ISDIR(mode)) { - copyinfo **dirlist = args->dirlist; - - // Don't try recursing down "." or "..". - if (IsDotOrDotDot(name)) return; - - ci = mkcopyinfo(args->rpath, args->lpath, name, 1); - ci->next = *dirlist; - *dirlist = ci; - } else if (S_ISREG(mode) || S_ISLNK(mode)) { - copyinfo **filelist = args->filelist; - - ci = mkcopyinfo(args->rpath, args->lpath, name, 0); - ci->time = time; - ci->mode = mode; - ci->size = size; - ci->next = *filelist; - *filelist = ci; - } else { - args->sc->Printf("skipping special file '%s'\n", name); + if (!sync_ls(sc, rpath.c_str(), callback)) { + return false; } -} - -static bool remote_build_list(SyncConnection& sc, copyinfo **filelist, - const char *rpath, const char *lpath) { - copyinfo* dirlist = nullptr; - sync_ls_build_list_cb_args args; - args.sc = ≻ - args.filelist = filelist; - args.dirlist = &dirlist; - args.rpath = rpath; - args.lpath = lpath; - - // Put the files/dirs in rpath on the lists. - if (!sync_ls(sc, rpath, sync_ls_build_list_cb, &args)) { - return false; + // Add the current directory to the list if it was empty, to ensure that + // it gets created. + if (empty_dir) { + filelist->push_back(mkcopyinfo(adb_dirname(lpath), adb_dirname(rpath), + adb_basename(rpath), S_IFDIR)); + return true; } // Recurse into each directory we found. - while (dirlist != NULL) { - copyinfo* next = dirlist->next; - if (!remote_build_list(sc, filelist, dirlist->src, dirlist->dst)) { + while (!dirlist.empty()) { + copyinfo current = dirlist.back(); + dirlist.pop_back(); + if (!remote_build_list(sc, filelist, current.rpath, current.lpath)) { return false; } - free(dirlist); - dirlist = next; } return true; } -static int set_time_and_mode(const char *lpath, time_t time, unsigned int mode) -{ +static int set_time_and_mode(const std::string& lpath, time_t time, + unsigned int mode) { struct utimbuf times = { time, time }; - int r1 = utime(lpath, ×); + int r1 = utime(lpath.c_str(), ×); /* use umask for permissions */ - mode_t mask=umask(0000); + mode_t mask = umask(0000); umask(mask); - int r2 = chmod(lpath, mode & ~mask); + int r2 = chmod(lpath.c_str(), mode & ~mask); - return r1 ? : r2; + return r1 ? r1 : r2; } -static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const char* lpath, - bool copy_attrs) { +static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath, + std::string lpath, bool copy_attrs) { // Make sure that both directory paths end in a slash. - std::string rpath_clean(rpath); - std::string lpath_clean(lpath); - if (rpath_clean.empty() || lpath_clean.empty()) return false; - if (rpath_clean.back() != '/') rpath_clean.push_back('/'); - if (lpath_clean.back() != '/') lpath_clean.push_back('/'); + // Both paths are known to be nonempty, so we don't need to check. + ensure_trailing_separator(lpath, rpath); // Recursively build the list of files to copy. sc.Print("pull: building file list..."); - copyinfo* filelist = nullptr; - if (!remote_build_list(sc, &filelist, rpath_clean.c_str(), lpath_clean.c_str())) return false; + std::vector<copyinfo> filelist; + if (!remote_build_list(sc, &filelist, rpath.c_str(), lpath.c_str())) { + return false; + } int pulled = 0; int skipped = 0; - copyinfo* ci = filelist; - while (ci) { - copyinfo* next = ci->next; - if (ci->flag == 0) { - sc.Printf("pull: %s -> %s", ci->src, ci->dst); - if (!sync_recv(sc, ci->src, ci->dst)) { + for (const copyinfo &ci : filelist) { + if (!ci.skip) { + sc.Printf("pull: %s -> %s", ci.rpath.c_str(), ci.lpath.c_str()); + + if (S_ISDIR(ci.mode)) { + // Entry is for an empty directory, create it and continue. + // TODO(b/25457350): We don't preserve permissions on directories. + if (!mkdirs(ci.lpath)) { + sc.Error("failed to create directory '%s': %s", + ci.lpath.c_str(), strerror(errno)); + return false; + } + pulled++; + continue; + } + + if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str())) { return false; } - if (copy_attrs && set_time_and_mode(ci->dst, ci->time, ci->mode)) { + if (copy_attrs && set_time_and_mode(ci.lpath, ci.time, ci.mode)) { return false; } pulled++; } else { skipped++; } - free(ci); - ci = next; } - sc.Printf("%s: %d file%s pulled. %d file%s skipped.%s\n", rpath, pulled, - (pulled == 1) ? "" : "s", skipped, (skipped == 1) ? "" : "s", - sc.TransferRate().c_str()); + sc.Printf("%s: %d file%s pulled. %d file%s skipped.%s\n", rpath.c_str(), + pulled, (pulled == 1) ? "" : "s", skipped, + (skipped == 1) ? "" : "s", sc.TransferRate().c_str()); return true; } @@ -803,25 +837,38 @@ bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, if (!sc.IsValid()) return false; bool success = true; - unsigned mode, time; struct stat st; - if (stat(dst, &st)) { - // If we're only pulling one file, the destination path might point to + bool dst_exists = true; + + if (stat(dst, &st) == -1) { + dst_exists = false; + + // If we're only pulling one path, the destination path might point to // a path that doesn't exist yet. - if (srcs.size() != 1 || errno != ENOENT) { - sc.Error("cannot stat '%s': %s", dst, strerror(errno)); + if (srcs.size() == 1 && errno == ENOENT) { + // However, its parent must exist. + struct stat parent_st; + if (stat(adb_dirname(dst).c_str(), &parent_st) == -1) { + sc.Error("cannot create file/directory '%s': %s", dst, strerror(errno)); + return false; + } + } else { + sc.Error("failed to access '%s': %s", dst, strerror(errno)); return false; } } - bool dst_isdir = S_ISDIR(st.st_mode); + bool dst_isdir = dst_exists && S_ISDIR(st.st_mode); if (!dst_isdir) { if (srcs.size() > 1) { sc.Error("target '%s' is not a directory", dst); return false; } else { size_t dst_len = strlen(dst); - if (dst[dst_len - 1] == '/') { + + // A path that ends with a slash doesn't have to be a directory if + // it doesn't exist yet. + if (adb_is_separator(dst[dst_len - 1]) && dst_exists) { sc.Error("failed to access '%s': Not a directory", dst); return false; } @@ -830,37 +877,55 @@ bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, for (const char* src_path : srcs) { const char* dst_path = dst; - if (!sync_stat(sc, src_path, &time, &mode, nullptr)) return false; - if (mode == 0) { + unsigned src_mode, src_time; + if (!sync_stat(sc, src_path, &src_time, &src_mode, nullptr)) { + return false; + } + if (src_mode == 0) { sc.Error("remote object '%s' does not exist", src_path); success = false; continue; } - if (S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) { + if (S_ISREG(src_mode)) { std::string path_holder; - struct stat st; - if (stat(dst_path, &st) == 0) { - if (S_ISDIR(st.st_mode)) { - // If we're copying a remote file to a local directory, - // we really want to copy to local_dir + "/" + - // basename(remote). - path_holder = android::base::StringPrintf( - "%s/%s", dst_path, adb_basename(src_path).c_str()); - dst_path = path_holder.c_str(); - } + if (dst_isdir) { + // If we're copying a remote file to a local directory, we + // really want to copy to local_dir + OS_PATH_SEPARATOR + + // basename(remote). + path_holder = android::base::StringPrintf( + "%s%c%s", dst_path, OS_PATH_SEPARATOR, + adb_basename(src_path).c_str()); + dst_path = path_holder.c_str(); } if (!sync_recv(sc, src_path, dst_path)) { success = false; continue; } else { - if (copy_attrs && set_time_and_mode(dst_path, time, mode)) { + if (copy_attrs && + set_time_and_mode(dst_path, src_time, src_mode) != 0) { success = false; continue; } } - } else if (S_ISDIR(mode)) { - success &= copy_remote_dir_local(sc, src_path, dst_path, copy_attrs); + } else if (S_ISDIR(src_mode)) { + std::string dst_dir = dst; + + // If the destination path existed originally, the source directory + // should be copied as a child of the destination. + if (dst_exists) { + if (!dst_isdir) { + sc.Error("target '%s' is not a directory", dst); + return false; + } + if (!adb_is_separator(dst_dir.back())) { + dst_dir.push_back(OS_PATH_SEPARATOR); + } + dst_dir.append(adb_basename(src_path)); + } + + success &= copy_remote_dir_local(sc, src_path, dst_dir.c_str(), + copy_attrs); continue; } else { sc.Error("remote object '%s' not a file or directory", src_path); @@ -877,5 +942,5 @@ bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_ SyncConnection sc; if (!sc.IsValid()) return false; - return copy_local_dir_remote(sc, lpath.c_str(), rpath.c_str(), true, list_only); + return copy_local_dir_remote(sc, lpath, rpath, true, list_only); } diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp index 298ed82eb..7484a7cf3 100644 --- a/adb/file_sync_service.cpp +++ b/adb/file_sync_service.cpp @@ -32,6 +32,7 @@ #include "adb.h" #include "adb_io.h" +#include "adb_utils.h" #include "private/android_filesystem_config.h" #include <base/stringprintf.h> @@ -53,8 +54,6 @@ static bool secure_mkdirs(const std::string& path) { if (path[0] != '/') return false; std::vector<std::string> path_components = android::base::Split(path, "/"); - path_components.pop_back(); // For "/system/bin/sh", only create "/system/bin". - std::string partial_path; for (const auto& path_component : path_components) { if (partial_path.back() != OS_PATH_SEPARATOR) partial_path += OS_PATH_SEPARATOR; @@ -149,7 +148,7 @@ static bool handle_send_file(int s, const char* path, uid_t uid, int fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode); if (fd < 0 && errno == ENOENT) { - if (!secure_mkdirs(path)) { + if (!secure_mkdirs(adb_dirname(path))) { SendSyncFailErrno(s, "secure_mkdirs failed"); goto fail; } @@ -244,7 +243,7 @@ static bool handle_send_link(int s, const std::string& path, std::vector<char>& ret = symlink(&buffer[0], path.c_str()); if (ret && errno == ENOENT) { - if (!secure_mkdirs(path)) { + if (!secure_mkdirs(adb_dirname(path))) { SendSyncFailErrno(s, "secure_mkdirs failed"); return false; } diff --git a/adb/line_printer.cpp b/adb/line_printer.cpp index aa332f7a1..4c57c7eba 100644 --- a/adb/line_printer.cpp +++ b/adb/line_printer.cpp @@ -77,7 +77,7 @@ void LinePrinter::Print(string to_print, LineType type) { CONSOLE_SCREEN_BUFFER_INFO csbi; GetConsoleScreenBufferInfo(console_, &csbi); - // TODO: const std::wstring to_print_wide = widen(to_print); + // TODO: std::wstring to_print_wide; if (!android::base::UTF8ToWide(to_print, &to_print_wide)... // TODO: wstring ElideMiddle. to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X)); // We don't want to have the cursor spamming back and forth, so instead of diff --git a/adb/mutex_list.h b/adb/mutex_list.h index 79c48d8e7..b59c9f200 100644 --- a/adb/mutex_list.h +++ b/adb/mutex_list.h @@ -6,6 +6,7 @@ #ifndef ADB_MUTEX #error ADB_MUTEX not defined when including this file #endif +ADB_MUTEX(basename_lock) ADB_MUTEX(dirname_lock) ADB_MUTEX(socket_list_lock) ADB_MUTEX(transport_lock) diff --git a/adb/remount_service.cpp b/adb/remount_service.cpp index 35ba0566e..8f1c9b015 100644 --- a/adb/remount_service.cpp +++ b/adb/remount_service.cpp @@ -35,8 +35,6 @@ #include "cutils/properties.h" #include "fs_mgr.h" -const std::string kFstab_Prefix = "/fstab."; - // Returns the device used to mount a directory in /proc/mounts. static std::string find_proc_mount(const char* dir) { std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent); @@ -58,7 +56,7 @@ static std::string find_fstab_mount(const char* dir) { char propbuf[PROPERTY_VALUE_MAX]; property_get("ro.hardware", propbuf, ""); - std::string fstab_filename = kFstab_Prefix + propbuf; + std::string fstab_filename = std::string("/fstab.") + propbuf; struct fstab* fstab = fs_mgr_read_fstab(fstab_filename.c_str()); struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, dir); std::string dev = rec ? std::string(rec->blk_device) : ""; diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp index be5921d8e..e3fde2691 100644 --- a/adb/shell_service.cpp +++ b/adb/shell_service.cpp @@ -513,6 +513,18 @@ ScopedFd* Subprocess::PassInput() { if (stdinout_sfd_.valid()) { switch (input_->id()) { + case ShellProtocol::kIdWindowSizeChange: + int rows, cols, x_pixels, y_pixels; + if (sscanf(input_->data(), "%dx%d,%dx%d", + &rows, &cols, &x_pixels, &y_pixels) == 4) { + winsize ws; + ws.ws_row = rows; + ws.ws_col = cols; + ws.ws_xpixel = x_pixels; + ws.ws_ypixel = y_pixels; + ioctl(stdinout_sfd_.fd(), TIOCSWINSZ, &ws); + } + break; case ShellProtocol::kIdStdin: input_bytes_left_ = input_->data_length(); break; @@ -531,8 +543,7 @@ ScopedFd* Subprocess::PassInput() { // non-interactively which is rare and unsupported. // If necessary, the client can manually close the shell // with `exit` or by killing the adb client process. - D("can't close input for PTY FD %d", - stdinout_sfd_.fd()); + D("can't close input for PTY FD %d", stdinout_sfd_.fd()); } break; } diff --git a/adb/shell_service.h b/adb/shell_service.h index 01410a9ba..63c00da15 100644 --- a/adb/shell_service.h +++ b/adb/shell_service.h @@ -54,8 +54,15 @@ class ShellProtocol { kIdStdout = 1, kIdStderr = 2, kIdExit = 3, - kIdCloseStdin = 4, // Close subprocess stdin if possible. - kIdInvalid = 255, // Indicates an invalid or unknown packet. + + // Close subprocess stdin if possible. + kIdCloseStdin = 4, + + // Window size change (an ASCII version of struct winsize). + kIdWindowSizeChange = 5, + + // Indicates an invalid or unknown packet. + kIdInvalid = 255, }; // ShellPackets will probably be too large to allocate on the stack so they diff --git a/adb/sockets.cpp b/adb/sockets.cpp index f8c2f6419..eb0ce85fb 100644 --- a/adb/sockets.cpp +++ b/adb/sockets.cpp @@ -25,6 +25,8 @@ #include <string.h> #include <unistd.h> +#include <algorithm> + #if !ADB_HOST #include "cutils/properties.h" #endif diff --git a/adb/sysdeps.h b/adb/sysdeps.h index 173562745..cba66fc8c 100644 --- a/adb/sysdeps.h +++ b/adb/sysdeps.h @@ -28,6 +28,9 @@ #include <string> +// Include this before open/unlink are defined as macros below. +#include <base/utf8.h> + /* * TEMP_FAILURE_RETRY is defined by some, but not all, versions of * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's @@ -72,7 +75,7 @@ #include <ws2tcpip.h> #include <memory> // unique_ptr -#include <string> // Prototypes for narrow() and widen() use std::(w)string. +#include <string> #include "fdevent.h" @@ -81,6 +84,10 @@ #define OS_PATH_SEPARATOR_STR "\\" #define ENV_PATH_SEPARATOR_STR ";" +static __inline__ bool adb_is_separator(char c) { + return c == '\\' || c == '/'; +} + typedef CRITICAL_SECTION adb_mutex_t; #define ADB_MUTEX_DEFINE(x) adb_mutex_t x @@ -309,7 +316,12 @@ extern char* adb_getcwd(char* buf, int size); #define closedir adb_closedir #define rewinddir rewinddir_utf8_not_yet_implemented #define telldir telldir_utf8_not_yet_implemented -#define seekdir seekdir_utf8_not_yet_implemented +// Some compiler's C++ headers have members named seekdir, so we can't do the +// macro technique and instead cause a link error if seekdir is called. +inline void seekdir(DIR*, long) { + extern int seekdir_utf8_not_yet_implemented; + seekdir_utf8_not_yet_implemented = 1; +} #define utime adb_utime #define chmod adb_chmod @@ -333,18 +345,6 @@ extern char* adb_getcwd(char* buf, int size); char* adb_strerror(int err); #define strerror adb_strerror -// Convert from UTF-8 to UTF-16, typically used to convert char strings into -// wchar_t strings that can be passed to wchar_t-based OS and C Runtime APIs -// on Windows. -extern std::wstring widen(const std::string& utf8); -extern std::wstring widen(const char* utf8); - -// Convert from UTF-16 to UTF-8, typically used to convert strings from OS and -// C Runtime APIs that return wchar_t, to a format for our char-based data -// structures. -extern std::string narrow(const std::wstring& utf16); -extern std::string narrow(const wchar_t* utf16); - // Helper class to convert UTF-16 argv from wmain() to UTF-8 args that can be // passed to main(). class NarrowArgs { @@ -420,6 +420,10 @@ typedef std::unique_ptr<HANDLE, handle_deleter> unique_handle; #define OS_PATH_SEPARATOR_STR "/" #define ENV_PATH_SEPARATOR_STR ":" +static __inline__ bool adb_is_separator(char c) { + return c == '/'; +} + typedef pthread_mutex_t adb_mutex_t; #define ADB_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp index d2e6cdb3f..81dcb415a 100644 --- a/adb/sysdeps_win32.cpp +++ b/adb/sysdeps_win32.cpp @@ -35,6 +35,7 @@ #include <base/logging.h> #include <base/stringprintf.h> #include <base/strings.h> +#include <base/utf8.h> #include "adb.h" @@ -102,7 +103,13 @@ std::string SystemErrorCodeToString(const DWORD error_code) { } // Convert UTF-16 to UTF-8. - std::string msg(narrow(msgbuf)); + std::string msg; + if (!android::base::WideToUTF8(msgbuf, &msg)) { + return android::base::StringPrintf( + "Error (%d) converting from UTF-16 to UTF-8 while retrieving error. (%lu)", errno, + error_code); + } + // Messages returned by the system end with line breaks. msg = android::base::Trim(msg); // There are many Windows error messages compared to POSIX, so include the @@ -143,7 +150,11 @@ void *load_file(const char *fn, unsigned *_sz) char *data; DWORD file_size; - file = CreateFileW( widen(fn).c_str(), + std::wstring fn_wide; + if (!android::base::UTF8ToWide(fn, &fn_wide)) + return NULL; + + file = CreateFileW( fn_wide.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, @@ -433,7 +444,11 @@ int adb_open(const char* path, int options) return -1; } - f->fh_handle = CreateFileW( widen(path).c_str(), desiredAccess, shareMode, + std::wstring path_wide; + if (!android::base::UTF8ToWide(path, &path_wide)) { + return -1; + } + f->fh_handle = CreateFileW( path_wide.c_str(), desiredAccess, shareMode, NULL, OPEN_EXISTING, 0, NULL ); if ( f->fh_handle == INVALID_HANDLE_VALUE ) { @@ -474,7 +489,11 @@ int adb_creat(const char* path, int mode) return -1; } - f->fh_handle = CreateFileW( widen(path).c_str(), GENERIC_WRITE, + std::wstring path_wide; + if (!android::base::UTF8ToWide(path, &path_wide)) { + return -1; + } + f->fh_handle = CreateFileW( path_wide.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); @@ -980,7 +999,7 @@ int network_connect(const std::string& host, int port, int type, int timeout, st #if (NTDDI_VERSION >= NTDDI_WINXPSP2) || (_WIN32_WINNT >= _WIN32_WINNT_WS03) // TODO: When the Android SDK tools increases the Windows system - // requirements >= WinXP SP2, switch to GetAddrInfoW(widen(host).c_str()). + // requirements >= WinXP SP2, switch to android::base::UTF8ToWide() + GetAddrInfoW(). #else // Otherwise, keep using getaddrinfo(), or do runtime API detection // with GetProcAddress("GetAddrInfoW"). @@ -2546,15 +2565,14 @@ int unix_isatty(int fd) { return _get_console_handle(fd) ? 1 : 0; } -// Read an input record from the console; one that should be processed. -static bool _get_interesting_input_record_uncached(const HANDLE console, - INPUT_RECORD* const input_record) { +// Get the next KEY_EVENT_RECORD that should be processed. +static bool _get_key_event_record(const HANDLE console, INPUT_RECORD* const input_record) { for (;;) { DWORD read_count = 0; memset(input_record, 0, sizeof(*input_record)); if (!ReadConsoleInputA(console, input_record, 1, &read_count)) { - D("_get_interesting_input_record_uncached: ReadConsoleInputA() " - "failed: %s\n", SystemErrorCodeToString(GetLastError()).c_str()); + D("_get_key_event_record: ReadConsoleInputA() failed: %s\n", + SystemErrorCodeToString(GetLastError()).c_str()); errno = EIO; return false; } @@ -2580,28 +2598,6 @@ static bool _get_interesting_input_record_uncached(const HANDLE console, } } -// Cached input record (in case _console_read() is passed a buffer that doesn't -// have enough space to fit wRepeatCount number of key sequences). A non-zero -// wRepeatCount indicates that a record is cached. -static INPUT_RECORD _win32_input_record; - -// Get the next KEY_EVENT_RECORD that should be processed. -static KEY_EVENT_RECORD* _get_key_event_record(const HANDLE console) { - // If nothing cached, read directly from the console until we get an - // interesting record. - if (_win32_input_record.Event.KeyEvent.wRepeatCount == 0) { - if (!_get_interesting_input_record_uncached(console, - &_win32_input_record)) { - // There was an error, so make sure wRepeatCount is zero because - // that signifies no cached input record. - _win32_input_record.Event.KeyEvent.wRepeatCount = 0; - return NULL; - } - } - - return &_win32_input_record.Event.KeyEvent; -} - static __inline__ bool _is_shift_pressed(const DWORD control_key_state) { return (control_key_state & SHIFT_PRESSED) != 0; } @@ -2946,16 +2942,34 @@ size_t _escape_prefix(char* const buf, const size_t len) { return len + 1; } -// Writes to buffer buf (of length len), returning number of bytes written or -// -1 on error. Never returns zero because Win32 consoles are never 'closed' -// (as far as I can tell). +// Internal buffer to satisfy future _console_read() calls. +static auto& g_console_input_buffer = *new std::vector<char>(); + +// Writes to buffer buf (of length len), returning number of bytes written or -1 on error. Never +// returns zero on console closure because Win32 consoles are never 'closed' (as far as I can tell). static int _console_read(const HANDLE console, void* buf, size_t len) { for (;;) { - KEY_EVENT_RECORD* const key_event = _get_key_event_record(console); - if (key_event == NULL) { + // Read of zero bytes should not block waiting for something from the console. + if (len == 0) { + return 0; + } + + // Flush as much as possible from input buffer. + if (!g_console_input_buffer.empty()) { + const int bytes_read = std::min(len, g_console_input_buffer.size()); + memcpy(buf, g_console_input_buffer.data(), bytes_read); + const auto begin = g_console_input_buffer.begin(); + g_console_input_buffer.erase(begin, begin + bytes_read); + return bytes_read; + } + + // Read from the actual console. This may block until input. + INPUT_RECORD input_record; + if (!_get_key_event_record(console, &input_record)) { return -1; } + KEY_EVENT_RECORD* const key_event = &input_record.Event.KeyEvent; const WORD vk = key_event->wVirtualKeyCode; const CHAR ch = key_event->uChar.AsciiChar; const DWORD control_key_state = _normalize_altgr_control_key_state( @@ -3133,7 +3147,12 @@ static int _console_read(const HANDLE console, void* buf, size_t len) { break; case 0x32: // 2 + case 0x33: // 3 + case 0x34: // 4 + case 0x35: // 5 case 0x36: // 6 + case 0x37: // 7 + case 0x38: // 8 case VK_OEM_MINUS: // -_ { seqbuflen = _get_control_character(seqbuf, key_event, @@ -3149,25 +3168,6 @@ static int _console_read(const HANDLE console, void* buf, size_t len) { } break; - case 0x33: // 3 - case 0x34: // 4 - case 0x35: // 5 - case 0x37: // 7 - case 0x38: // 8 - { - seqbuflen = _get_control_character(seqbuf, key_event, - control_key_state); - - // If Alt is pressed and it isn't Ctrl-Alt-ShiftUp, then - // prefix with escape. - if (_is_alt_pressed(control_key_state) && - !(_is_ctrl_pressed(control_key_state) && - !_is_shift_pressed(control_key_state))) { - seqbuflen = _escape_prefix(seqbuf, seqbuflen); - } - } - break; - case 0x41: // a case 0x42: // b case 0x43: // c @@ -3296,90 +3296,56 @@ static int _console_read(const HANDLE console, void* buf, size_t len) { // event. D("_console_read: unknown virtual key code: %d, enhanced: %s", vk, _is_enhanced_key(control_key_state) ? "true" : "false"); - key_event->wRepeatCount = 0; continue; } - int bytesRead = 0; - - // put output wRepeatCount times into buf/len - while (key_event->wRepeatCount > 0) { - if (len >= outlen) { - // Write to buf/len - memcpy(buf, out, outlen); - buf = (void*)((char*)buf + outlen); - len -= outlen; - bytesRead += outlen; - - // consume the input - --key_event->wRepeatCount; - } else { - // Not enough space, so just leave it in _win32_input_record - // for a subsequent retrieval. - if (bytesRead == 0) { - // We didn't write anything because there wasn't enough - // space to even write one sequence. This should never - // happen if the caller uses sensible buffer sizes - // (i.e. >= maximum sequence length which is probably a - // few bytes long). - D("_console_read: no buffer space to write one sequence; " - "buffer: %ld, sequence: %ld\n", (long)len, - (long)outlen); - errno = ENOMEM; - return -1; - } else { - // Stop trying to write to buf/len, just return whatever - // we wrote so far. - break; - } - } + // put output wRepeatCount times into g_console_input_buffer + while (key_event->wRepeatCount-- > 0) { + g_console_input_buffer.insert(g_console_input_buffer.end(), out, out + outlen); } - return bytesRead; + // Loop around and try to flush g_console_input_buffer } } static DWORD _old_console_mode; // previous GetConsoleMode() result static HANDLE _console_handle; // when set, console mode should be restored -void stdin_raw_init(const int fd) { - if (STDIN_FILENO == fd) { - const HANDLE in = _get_console_handle(fd, &_old_console_mode); - - // Disable ENABLE_PROCESSED_INPUT so that Ctrl-C is read instead of - // calling the process Ctrl-C routine (configured by - // SetConsoleCtrlHandler()). - // Disable ENABLE_LINE_INPUT so that input is immediately sent. - // Disable ENABLE_ECHO_INPUT to disable local echo. Disabling this - // flag also seems necessary to have proper line-ending processing. - if (!SetConsoleMode(in, _old_console_mode & ~(ENABLE_PROCESSED_INPUT | - ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT))) { - // This really should not fail. - D("stdin_raw_init: SetConsoleMode() failed: %s", - SystemErrorCodeToString(GetLastError()).c_str()); - } - - // Once this is set, it means that stdin has been configured for - // reading from and that the old console mode should be restored later. - _console_handle = in; +void stdin_raw_init() { + const HANDLE in = _get_console_handle(STDIN_FILENO, &_old_console_mode); - // Note that we don't need to configure C Runtime line-ending - // translation because _console_read() does not call the C Runtime to - // read from the console. + // Disable ENABLE_PROCESSED_INPUT so that Ctrl-C is read instead of + // calling the process Ctrl-C routine (configured by + // SetConsoleCtrlHandler()). + // Disable ENABLE_LINE_INPUT so that input is immediately sent. + // Disable ENABLE_ECHO_INPUT to disable local echo. Disabling this + // flag also seems necessary to have proper line-ending processing. + if (!SetConsoleMode(in, _old_console_mode & ~(ENABLE_PROCESSED_INPUT | + ENABLE_LINE_INPUT | + ENABLE_ECHO_INPUT))) { + // This really should not fail. + D("stdin_raw_init: SetConsoleMode() failed: %s", + SystemErrorCodeToString(GetLastError()).c_str()); } + + // Once this is set, it means that stdin has been configured for + // reading from and that the old console mode should be restored later. + _console_handle = in; + + // Note that we don't need to configure C Runtime line-ending + // translation because _console_read() does not call the C Runtime to + // read from the console. } -void stdin_raw_restore(const int fd) { - if (STDIN_FILENO == fd) { - if (_console_handle != NULL) { - const HANDLE in = _console_handle; - _console_handle = NULL; // clear state +void stdin_raw_restore() { + if (_console_handle != NULL) { + const HANDLE in = _console_handle; + _console_handle = NULL; // clear state - if (!SetConsoleMode(in, _old_console_mode)) { - // This really should not fail. - D("stdin_raw_restore: SetConsoleMode() failed: %s", - SystemErrorCodeToString(GetLastError()).c_str()); - } + if (!SetConsoleMode(in, _old_console_mode)) { + // This really should not fail. + D("stdin_raw_restore: SetConsoleMode() failed: %s", + SystemErrorCodeToString(GetLastError()).c_str()); } } } @@ -3457,11 +3423,11 @@ int unix_read(int fd, void* buf, size_t len) { // The Choice // ---------- // -// The code below chooses option 3, the UTF-8 everywhere strategy. It -// introduces narrow() which converts UTF-16 to UTF-8. This is used by the +// The code below chooses option 3, the UTF-8 everywhere strategy. It uses +// android::base::WideToUTF8() which converts UTF-16 to UTF-8. This is used by the // NarrowArgs helper class that is used to convert wmain() args into UTF-8 -// args that are passed to main() at the beginning of program startup. We also -// introduce widen() which converts from UTF-8 to UTF-16. This is used to +// args that are passed to main() at the beginning of program startup. We also use +// android::base::UTF8ToWide() which converts from UTF-8 to UTF-16. This is used to // implement wrappers below that call UTF-16 OS and C Runtime APIs. // // Unicode console output @@ -3491,140 +3457,17 @@ int unix_read(int fd, void* buf, size_t len) { // to UTF-16 and then calls WriteConsoleW(). -// Function prototype because attributes cannot be placed on func definitions. -static void _widen_fatal(const char *fmt, ...) - __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 1, 2))); - -// A version of fatal() that does not call adb_(v)fprintf(), so it can be -// called from those functions. -static void _widen_fatal(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - // If (v)fprintf are macros that point to adb_(v)fprintf, when random adb - // code calls (v)fprintf, it may end up calling adb_(v)fprintf, which then - // calls _widen_fatal(). So then how does _widen_fatal() output a error? - // By directly calling real C Runtime APIs that don't properly output - // Unicode, but will be able to get a comprehendible message out. To do - // this, make sure we don't call (v)fprintf macros by undefining them. -#pragma push_macro("fprintf") -#pragma push_macro("vfprintf") -#undef fprintf -#undef vfprintf - fprintf(stderr, "error: "); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); -#pragma pop_macro("vfprintf") -#pragma pop_macro("fprintf") - va_end(ap); - exit(-1); -} - -// TODO: Consider implementing widen() and narrow() out of std::wstring_convert -// once libcxx is supported on Windows. Or, consider libutils/Unicode.cpp. - -// Convert from UTF-8 to UTF-16. A size of -1 specifies a NULL terminated -// string. Any other size specifies the number of chars to convert, excluding -// any NULL terminator (if you're passing an explicit size, you probably don't -// have a NULL terminated string in the first place). -std::wstring widen(const char* utf8, const int size) { - // Note: Do not call SystemErrorCodeToString() from widen() because - // SystemErrorCodeToString() calls narrow() which may call fatal() which - // calls adb_vfprintf() which calls widen(), potentially causing infinite - // recursion. - const int chars_to_convert = MultiByteToWideChar(CP_UTF8, 0, utf8, size, - NULL, 0); - if (chars_to_convert <= 0) { - // UTF-8 to UTF-16 should be lossless, so we don't expect this to fail. - _widen_fatal("MultiByteToWideChar failed counting: %d, " - "GetLastError: %lu", chars_to_convert, GetLastError()); - } - - std::wstring utf16; - size_t chars_to_allocate = chars_to_convert; - if (size == -1) { - // chars_to_convert includes a NULL terminator, so subtract space - // for that because resize() includes that itself. - --chars_to_allocate; - } - utf16.resize(chars_to_allocate); - - // This uses &string[0] to get write-access to the entire string buffer - // which may be assuming that the chars are all contiguous, but it seems - // to work and saves us the hassle of using a temporary - // std::vector<wchar_t>. - const int result = MultiByteToWideChar(CP_UTF8, 0, utf8, size, &utf16[0], - chars_to_convert); - if (result != chars_to_convert) { - // UTF-8 to UTF-16 should be lossless, so we don't expect this to fail. - _widen_fatal("MultiByteToWideChar failed conversion: %d, " - "GetLastError: %lu", result, GetLastError()); - } - - // If a size was passed in (size != -1), then the string is NULL terminated - // by a NULL char that was written by std::string::resize(). If size == -1, - // then MultiByteToWideChar() read a NULL terminator from the original - // string and converted it to a NULL UTF-16 char in the output. - - return utf16; -} - -// Convert a NULL terminated string from UTF-8 to UTF-16. -std::wstring widen(const char* utf8) { - // Pass -1 to let widen() determine the string length. - return widen(utf8, -1); -} - -// Convert from UTF-8 to UTF-16. -std::wstring widen(const std::string& utf8) { - return widen(utf8.c_str(), utf8.length()); -} - -// Convert from UTF-16 to UTF-8. -std::string narrow(const std::wstring& utf16) { - return narrow(utf16.c_str()); -} - -// Convert from UTF-16 to UTF-8. -std::string narrow(const wchar_t* utf16) { - // Note: Do not call SystemErrorCodeToString() from narrow() because - // SystemErrorCodeToString() calls narrow() and we don't want potential - // infinite recursion. - const int chars_required = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, NULL, - 0, NULL, NULL); - if (chars_required <= 0) { - // UTF-16 to UTF-8 should be lossless, so we don't expect this to fail. - fatal("WideCharToMultiByte failed counting: %d, GetLastError: %lu", - chars_required, GetLastError()); - } - - std::string utf8; - // Subtract space for the NULL terminator because resize() includes - // that itself. Note that this could potentially throw a std::bad_alloc - // exception. - utf8.resize(chars_required - 1); - - // This uses &string[0] to get write-access to the entire string buffer - // which may be assuming that the chars are all contiguous, but it seems - // to work and saves us the hassle of using a temporary - // std::vector<char>. - const int result = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, &utf8[0], - chars_required, NULL, NULL); - if (result != chars_required) { - // UTF-16 to UTF-8 should be lossless, so we don't expect this to fail. - fatal("WideCharToMultiByte failed conversion: %d, GetLastError: %lu", - result, GetLastError()); - } - - return utf8; -} - // Constructor for helper class to convert wmain() UTF-16 args to UTF-8 to // be passed to main(). NarrowArgs::NarrowArgs(const int argc, wchar_t** const argv) { narrow_args = new char*[argc + 1]; for (int i = 0; i < argc; ++i) { - narrow_args[i] = strdup(narrow(argv[i]).c_str()); + std::string arg_narrow; + if (!android::base::WideToUTF8(argv[i], &arg_narrow)) { + fatal_errno("cannot convert argument from UTF-16 to UTF-8"); + } + narrow_args[i] = strdup(arg_narrow.c_str()); } narrow_args[argc] = nullptr; // terminate } @@ -3640,20 +3483,24 @@ NarrowArgs::~NarrowArgs() { } int unix_open(const char* path, int options, ...) { + std::wstring path_wide; + if (!android::base::UTF8ToWide(path, &path_wide)) { + return -1; + } if ((options & O_CREAT) == 0) { - return _wopen(widen(path).c_str(), options); + return _wopen(path_wide.c_str(), options); } else { int mode; va_list args; va_start(args, options); mode = va_arg(args, int); va_end(args); - return _wopen(widen(path).c_str(), options, mode); + return _wopen(path_wide.c_str(), options, mode); } } // Version of stat() that takes a UTF-8 path. -int adb_stat(const char* f, struct adb_stat* s) { +int adb_stat(const char* path, struct adb_stat* s) { #pragma push_macro("wstat") // This definition of wstat seems to be missing from <sys/stat.h>. #if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64) @@ -3666,17 +3513,27 @@ int adb_stat(const char* f, struct adb_stat* s) { // <sys/stat.h> has a function prototype for wstat() that should be available. #endif - return wstat(widen(f).c_str(), s); + std::wstring path_wide; + if (!android::base::UTF8ToWide(path, &path_wide)) { + return -1; + } + + return wstat(path_wide.c_str(), s); #pragma pop_macro("wstat") } // Version of opendir() that takes a UTF-8 path. -DIR* adb_opendir(const char* name) { +DIR* adb_opendir(const char* path) { + std::wstring path_wide; + if (!android::base::UTF8ToWide(path, &path_wide)) { + return nullptr; + } + // Just cast _WDIR* to DIR*. This doesn't work if the caller reads any of // the fields, but right now all the callers treat the structure as // opaque. - return reinterpret_cast<DIR*>(_wopendir(widen(name).c_str())); + return reinterpret_cast<DIR*>(_wopendir(path_wide.c_str())); } // Version of readdir() that returns UTF-8 paths. @@ -3686,8 +3543,12 @@ struct dirent* adb_readdir(DIR* dir) { if (went == nullptr) { return nullptr; } + // Convert from UTF-16 to UTF-8. - const std::string name_utf8(narrow(went->d_name)); + std::string name_utf8; + if (!android::base::WideToUTF8(went->d_name, &name_utf8)) { + return nullptr; + } // Cast the _wdirent* to dirent* and overwrite the d_name field (which has // space for UTF-16 wchar_t's) with UTF-8 char's. @@ -3719,7 +3580,10 @@ int adb_closedir(DIR* dir) { // Version of unlink() that takes a UTF-8 path. int adb_unlink(const char* path) { - const std::wstring wpath(widen(path)); + std::wstring wpath; + if (!android::base::UTF8ToWide(path, &wpath)) { + return -1; + } int rc = _wunlink(wpath.c_str()); @@ -3735,29 +3599,47 @@ int adb_unlink(const char* path) { // Version of mkdir() that takes a UTF-8 path. int adb_mkdir(const std::string& path, int mode) { - return _wmkdir(widen(path.c_str()).c_str()); + std::wstring path_wide; + if (!android::base::UTF8ToWide(path, &path_wide)) { + return -1; + } + + return _wmkdir(path_wide.c_str()); } // Version of utime() that takes a UTF-8 path. int adb_utime(const char* path, struct utimbuf* u) { + std::wstring path_wide; + if (!android::base::UTF8ToWide(path, &path_wide)) { + return -1; + } + static_assert(sizeof(struct utimbuf) == sizeof(struct _utimbuf), "utimbuf and _utimbuf should be the same size because they both " "contain the same types, namely time_t"); - return _wutime(widen(path).c_str(), reinterpret_cast<struct _utimbuf*>(u)); + return _wutime(path_wide.c_str(), reinterpret_cast<struct _utimbuf*>(u)); } // Version of chmod() that takes a UTF-8 path. int adb_chmod(const char* path, int mode) { - return _wchmod(widen(path).c_str(), mode); + std::wstring path_wide; + if (!android::base::UTF8ToWide(path, &path_wide)) { + return -1; + } + + return _wchmod(path_wide.c_str(), mode); } // Internal helper function to write UTF-8 bytes to a console. Returns -1 // on error. static int _console_write_utf8(const char* buf, size_t size, FILE* stream, HANDLE console) { - // Convert from UTF-8 to UTF-16. + std::wstring output; + + // Try to convert from data that might be UTF-8 to UTF-16, ignoring errors. + // Data might not be UTF-8 if the user cat's random data, runs dmesg, etc. // This could throw std::bad_alloc. - const std::wstring output(widen(buf, size)); + (void)android::base::UTF8ToWide(buf, size, &output); // Note that this does not do \n => \r\n translation because that // doesn't seem necessary for the Windows console. For the Windows @@ -3907,8 +3789,18 @@ size_t adb_fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream) { // Version of fopen() that takes a UTF-8 filename and can access a file with // a Unicode filename. -FILE* adb_fopen(const char* f, const char* m) { - return _wfopen(widen(f).c_str(), widen(m).c_str()); +FILE* adb_fopen(const char* path, const char* mode) { + std::wstring path_wide; + if (!android::base::UTF8ToWide(path, &path_wide)) { + return nullptr; + } + + std::wstring mode_wide; + if (!android::base::UTF8ToWide(mode, &mode_wide)) { + return nullptr; + } + + return _wfopen(path_wide.c_str(), mode_wide.c_str()); } // Return a lowercase version of the argument. Uses C Runtime tolower() on @@ -3939,7 +3831,7 @@ extern "C" int wmain(int argc, wchar_t **argv) { // currently updated if putenv, setenv, unsetenv are called. Note that no // thread synchronization is done, but we're called early enough in // single-threaded startup that things work ok. -static std::unordered_map<std::string, char*> g_environ_utf8; +static auto& g_environ_utf8 = *new std::unordered_map<std::string, char*>(); // Make sure that shadow UTF-8 environment variables are setup. static void _ensure_env_setup() { @@ -3968,15 +3860,27 @@ static void _ensure_env_setup() { continue; } + // If we encounter an error converting UTF-16, don't error-out on account of a single env + // var because the program might never even read this particular variable. + std::string name_utf8; + if (!android::base::WideToUTF8(*env, equal - *env, &name_utf8)) { + continue; + } + // Store lowercase name so that we can do case-insensitive searches. - const std::string name_utf8(ToLower(narrow( - std::wstring(*env, equal - *env)))); - char* const value_utf8 = strdup(narrow(equal + 1).c_str()); + name_utf8 = ToLower(name_utf8); + + std::string value_utf8; + if (!android::base::WideToUTF8(equal + 1, &value_utf8)) { + continue; + } + + char* const value_dup = strdup(value_utf8.c_str()); // Don't overwrite a previus env var with the same name. In reality, // the system probably won't let two env vars with the same name exist // in _wenviron. - g_environ_utf8.insert({name_utf8, value_utf8}); + g_environ_utf8.insert({name_utf8, value_dup}); } } @@ -4002,10 +3906,15 @@ char* adb_getcwd(char* buf, int size) { return nullptr; } - const std::string buf_utf8(narrow(wbuf)); + std::string buf_utf8; + const bool narrow_result = android::base::WideToUTF8(wbuf, &buf_utf8); free(wbuf); wbuf = nullptr; + if (!narrow_result) { + return nullptr; + } + // If size was specified, make sure all the chars will fit. if (size != 0) { if (size < static_cast<int>(buf_utf8.length() + 1)) { diff --git a/adb/transport.cpp b/adb/transport.cpp index e9e774fd9..2f18f2011 100644 --- a/adb/transport.cpp +++ b/adb/transport.cpp @@ -26,6 +26,7 @@ #include <string.h> #include <unistd.h> +#include <algorithm> #include <list> #include <base/logging.h> @@ -37,11 +38,14 @@ static void transport_unref(atransport *t); -static std::list<atransport*> transport_list; -static std::list<atransport*> pending_list; +static auto& transport_list = *new std::list<atransport*>(); +static auto& pending_list = *new std::list<atransport*>(); ADB_MUTEX_DEFINE( transport_lock ); +const char* const kFeatureShell2 = "shell_v2"; +const char* const kFeatureCmd = "cmd"; + static std::string dump_packet(const char* name, const char* func, apacket* p) { unsigned command = p->msg.command; int len = p->msg.data_length; @@ -780,7 +784,10 @@ constexpr char kFeatureStringDelimiter = ','; const FeatureSet& supported_features() { // Local static allocation to avoid global non-POD variables. static const FeatureSet* features = new FeatureSet{ - kFeatureShell2 + kFeatureShell2, + // Internal master has 'cmd'. AOSP master doesn't. + // kFeatureCmd + // Increment ADB_SERVER_VERSION whenever the feature list changes to // make sure that the adb client and server features stay in sync // (http://b/24370690). diff --git a/adb/transport.h b/adb/transport.h index f41a8d4e5..d9845b608 100644 --- a/adb/transport.h +++ b/adb/transport.h @@ -38,7 +38,9 @@ bool CanUseFeature(const FeatureSet& feature_set, const std::string& feature); // Do not use any of [:;=,] in feature strings, they have special meaning // in the connection banner. -constexpr char kFeatureShell2[] = "shell_v2"; +extern const char* const kFeatureShell2; +// The 'cmd' command is available +extern const char* const kFeatureCmd; class atransport { public: diff --git a/adb/usb_linux.cpp b/adb/usb_linux.cpp index c633f7fb8..0358b6203 100644 --- a/adb/usb_linux.cpp +++ b/adb/usb_linux.cpp @@ -81,8 +81,8 @@ struct usb_handle { pthread_t reaper_thread = 0; }; -static std::mutex g_usb_handles_mutex; -static std::list<usb_handle*> g_usb_handles; +static auto& g_usb_handles_mutex = *new std::mutex(); +static auto& g_usb_handles = *new std::list<usb_handle*>(); static int is_known_device(const char* dev_name) { std::lock_guard<std::mutex> lock(g_usb_handles_mutex); diff --git a/adb/usb_windows.cpp b/adb/usb_windows.cpp index d811b2446..8d3501e24 100644 --- a/adb/usb_windows.cpp +++ b/adb/usb_windows.cpp @@ -55,7 +55,7 @@ struct usb_handle { ADBAPIHANDLE adb_write_pipe; /// Interface name - char* interface_name; + wchar_t* interface_name; /// Mask for determining when to use zero length packets unsigned zero_mask; @@ -74,11 +74,11 @@ static usb_handle handle_list = { ADB_MUTEX_DEFINE( usb_lock ); /// Checks if there is opened usb handle in handle_list for this device. -int known_device(const char* dev_name); +int known_device(const wchar_t* dev_name); /// Checks if there is opened usb handle in handle_list for this device. /// usb_lock mutex must be held before calling this routine. -int known_device_locked(const char* dev_name); +int known_device_locked(const wchar_t* dev_name); /// Registers opened usb handle (adds it to handle_list). int register_new_device(usb_handle* handle); @@ -118,7 +118,7 @@ void usb_kick(usb_handle* handle); /// Closes opened usb handle int usb_close(usb_handle* handle); -int known_device_locked(const char* dev_name) { +int known_device_locked(const wchar_t* dev_name) { usb_handle* usb; if (NULL != dev_name) { @@ -126,7 +126,7 @@ int known_device_locked(const char* dev_name) { for(usb = handle_list.next; usb != &handle_list; usb = usb->next) { // In Windows names are not case sensetive! if((NULL != usb->interface_name) && - (0 == stricmp(usb->interface_name, dev_name))) { + (0 == wcsicmp(usb->interface_name, dev_name))) { return 1; } } @@ -135,7 +135,7 @@ int known_device_locked(const char* dev_name) { return 0; } -int known_device(const char* dev_name) { +int known_device(const wchar_t* dev_name) { int ret = 0; if (NULL != dev_name) { @@ -316,17 +316,16 @@ usb_handle* do_usb_open(const wchar_t* interface_name) { AdbGetInterfaceName(ret->adb_interface, NULL, &name_len, - true); + false); if (0 == name_len) { D("AdbGetInterfaceName returned name length of zero: %s", SystemErrorCodeToString(GetLastError()).c_str()); goto fail; } - ret->interface_name = (char*)malloc(name_len); + ret->interface_name = (wchar_t*)malloc(name_len * sizeof(ret->interface_name[0])); if (NULL == ret->interface_name) { - D("Could not allocate %lu bytes for interface_name: %s", name_len, - strerror(errno)); + D("Could not allocate %lu characters for interface_name: %s", name_len, strerror(errno)); goto fail; } @@ -334,7 +333,7 @@ usb_handle* do_usb_open(const wchar_t* interface_name) { if (!AdbGetInterfaceName(ret->adb_interface, ret->interface_name, &name_len, - true)) { + false)) { D("AdbGetInterfaceName failed: %s", SystemErrorCodeToString(GetLastError()).c_str()); goto fail; @@ -581,12 +580,10 @@ int recognized_device(usb_handle* handle) { } void find_devices() { - usb_handle* handle = NULL; + usb_handle* handle = NULL; char entry_buffer[2048]; - char interf_name[2048]; AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]); unsigned long entry_buffer_size = sizeof(entry_buffer); - char* copy_name; // Enumerate all present and active interfaces. ADBAPIHANDLE enum_handle = @@ -599,25 +596,21 @@ void find_devices() { } while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) { - // TODO: FIXME - temp hack converting wchar_t into char. - // It would be better to change AdbNextInterface so it will return - // interface name as single char string. - const wchar_t* wchar_name = next_interface->device_name; - for(copy_name = interf_name; - L'\0' != *wchar_name; - wchar_name++, copy_name++) { - *copy_name = (char)(*wchar_name); - } - *copy_name = '\0'; - // Lets see if we already have this device in the list - if (!known_device(interf_name)) { + if (!known_device(next_interface->device_name)) { // This seems to be a new device. Open it! - handle = do_usb_open(next_interface->device_name); - if (NULL != handle) { + handle = do_usb_open(next_interface->device_name); + if (NULL != handle) { // Lets see if this interface (device) belongs to us if (recognized_device(handle)) { - D("adding a new device %s", interf_name); + D("adding a new device %ls", next_interface->device_name); + + // We don't request a wchar_t string from AdbGetSerialNumber() because of a bug in + // adb_winusb_interface.cpp:CopyMemory(buffer, ser_num->bString, bytes_written) where the + // last parameter should be (str_len * sizeof(wchar_t)). The bug reads 2 bytes past the + // end of a stack buffer in the best case, and in the unlikely case of a long serial + // number, it will read 2 bytes past the end of a heap allocation. This doesn't affect the + // resulting string, but we should avoid the bad reads in the first place. char serial_number[512]; unsigned long serial_number_len = sizeof(serial_number); if (AdbGetSerialNumber(handle->adb_interface, @@ -628,7 +621,7 @@ void find_devices() { if (register_new_device(handle)) { register_usb_transport(handle, serial_number, NULL, 1); } else { - D("register_new_device failed for %s", interf_name); + D("register_new_device failed for %ls", next_interface->device_name); usb_cleanup_handle(handle); free(handle); } diff --git a/base/Android.mk b/base/Android.mk index 613636b61..cba70d4c9 100644 --- a/base/Android.mk +++ b/base/Android.mk @@ -23,6 +23,9 @@ libbase_src_files := \ strings.cpp \ test_utils.cpp \ +libbase_windows_src_files := \ + utf8.cpp \ + libbase_test_src_files := \ file_test.cpp \ logging_test.cpp \ @@ -31,19 +34,31 @@ libbase_test_src_files := \ strings_test.cpp \ test_main.cpp \ +libbase_test_windows_src_files := \ + utf8_test.cpp \ + libbase_cppflags := \ -Wall \ -Wextra \ -Werror \ +libbase_linux_cppflags := \ + -Wexit-time-destructors \ + +libbase_darwin_cppflags := \ + -Wexit-time-destructors \ + # Device # ------------------------------------------------------------------------------ include $(CLEAR_VARS) LOCAL_MODULE := libbase LOCAL_CLANG := true LOCAL_SRC_FILES := $(libbase_src_files) +LOCAL_SRC_FILES_darwin := $(libbase_darwin_src_files) +LOCAL_SRC_FILES_linux := $(libbase_linux_src_files) +LOCAL_SRC_FILES_windows := $(libbase_windows_src_files) LOCAL_C_INCLUDES := $(LOCAL_PATH)/include -LOCAL_CPPFLAGS := $(libbase_cppflags) +LOCAL_CPPFLAGS := $(libbase_cppflags) $(libbase_linux_cppflags) LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := libcutils LOCAL_MULTILIB := both @@ -64,8 +79,13 @@ include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := libbase LOCAL_SRC_FILES := $(libbase_src_files) +LOCAL_SRC_FILES_darwin := $(libbase_darwin_src_files) +LOCAL_SRC_FILES_linux := $(libbase_linux_src_files) +LOCAL_SRC_FILES_windows := $(libbase_windows_src_files) LOCAL_C_INCLUDES := $(LOCAL_PATH)/include LOCAL_CPPFLAGS := $(libbase_cppflags) +LOCAL_CPPFLAGS_darwin := $(libbase_darwin_cppflags) +LOCAL_CPPFLAGS_linux := $(libbase_linux_cppflags) LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := libcutils LOCAL_MULTILIB := both @@ -88,6 +108,9 @@ include $(CLEAR_VARS) LOCAL_MODULE := libbase_test LOCAL_CLANG := true LOCAL_SRC_FILES := $(libbase_test_src_files) +LOCAL_SRC_FILES_darwin := $(libbase_test_darwin_src_files) +LOCAL_SRC_FILES_linux := $(libbase_test_linux_src_files) +LOCAL_SRC_FILES_windows := $(libbase_test_windows_src_files) LOCAL_C_INCLUDES := $(LOCAL_PATH) LOCAL_CPPFLAGS := $(libbase_cppflags) LOCAL_SHARED_LIBRARIES := libbase @@ -100,6 +123,9 @@ include $(CLEAR_VARS) LOCAL_MODULE := libbase_test LOCAL_MODULE_HOST_OS := darwin linux windows LOCAL_SRC_FILES := $(libbase_test_src_files) +LOCAL_SRC_FILES_darwin := $(libbase_test_darwin_src_files) +LOCAL_SRC_FILES_linux := $(libbase_test_linux_src_files) +LOCAL_SRC_FILES_windows := $(libbase_test_windows_src_files) LOCAL_C_INCLUDES := $(LOCAL_PATH) LOCAL_CPPFLAGS := $(libbase_cppflags) LOCAL_SHARED_LIBRARIES := libbase diff --git a/base/file.cpp b/base/file.cpp index 3468dcfbf..7b5e7b13d 100644 --- a/base/file.cpp +++ b/base/file.cpp @@ -24,6 +24,7 @@ #include <string> #include "base/macros.h" // For TEMP_FAILURE_RETRY on Darwin. +#include "base/utf8.h" #define LOG_TAG "base.file" #include "cutils/log.h" #include "utils/Compat.h" @@ -35,6 +36,9 @@ namespace android { namespace base { +// Versions of standard library APIs that support UTF-8 strings. +using namespace android::base::utf8; + bool ReadFdToString(int fd, std::string* content) { content->clear(); diff --git a/base/include/base/utf8.h b/base/include/base/utf8.h new file mode 100755 index 000000000..3b0ed0af8 --- /dev/null +++ b/base/include/base/utf8.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef BASE_UTF8_H +#define BASE_UTF8_H + +#ifdef _WIN32 +#include <string> +#else +// Bring in prototypes for standard APIs so that we can import them into the utf8 namespace. +#include <fcntl.h> // open +#include <unistd.h> // unlink +#endif + +namespace android { +namespace base { + +// Only available on Windows because this is only needed on Windows. +#ifdef _WIN32 +// Convert size number of UTF-16 wchar_t's to UTF-8. Returns whether the +// conversion was done successfully. +bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8); + +// Convert a NULL-terminated string of UTF-16 characters to UTF-8. Returns +// whether the conversion was done successfully. +bool WideToUTF8(const wchar_t* utf16, std::string* utf8); + +// Convert a UTF-16 std::wstring (including any embedded NULL characters) to +// UTF-8. Returns whether the conversion was done successfully. +bool WideToUTF8(const std::wstring& utf16, std::string* utf8); + +// Convert size number of UTF-8 char's to UTF-16. Returns whether the conversion +// was done successfully. +bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16); + +// Convert a NULL-terminated string of UTF-8 characters to UTF-16. Returns +// whether the conversion was done successfully. +bool UTF8ToWide(const char* utf8, std::wstring* utf16); + +// Convert a UTF-8 std::string (including any embedded NULL characters) to +// UTF-16. Returns whether the conversion was done successfully. +bool UTF8ToWide(const std::string& utf8, std::wstring* utf16); +#endif + +// The functions in the utf8 namespace take UTF-8 strings. For Windows, these +// are wrappers, for non-Windows these just expose existing APIs. To call these +// functions, use: +// +// // anonymous namespace to avoid conflict with existing open(), unlink(), etc. +// namespace { +// // Import functions into anonymous namespace. +// using namespace android::base::utf8; +// +// void SomeFunction(const char* name) { +// int fd = open(name, ...); // Calls android::base::utf8::open(). +// ... +// unlink(name); // Calls android::base::utf8::unlink(). +// } +// } +namespace utf8 { + +#ifdef _WIN32 +int open(const char* name, int flags, ...); +int unlink(const char* name); +#else +using ::open; +using ::unlink; +#endif + +} // namespace utf8 +} // namespace base +} // namespace android + +#endif // BASE_UTF8_H diff --git a/base/logging.cpp b/base/logging.cpp index 6bfaaec2c..85f8b3fad 100644 --- a/base/logging.cpp +++ b/base/logging.cpp @@ -53,6 +53,33 @@ #include <unistd.h> #endif +// For gettid. +#if defined(__APPLE__) +#include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED +#include <stdint.h> +#include <stdlib.h> +#include <sys/syscall.h> +#include <sys/time.h> +#include <unistd.h> +#elif defined(__linux__) && !defined(__ANDROID__) +#include <syscall.h> +#include <unistd.h> +#elif defined(_WIN32) +#include <windows.h> +#endif + +static pid_t GetThreadId() { +#if defined(__BIONIC__) + return gettid(); +#elif defined(__APPLE__) + return syscall(SYS_thread_selfid); +#elif defined(__linux__) + return syscall(__NR_gettid); +#elif defined(_WIN32) + return GetCurrentThreadId(); +#endif +} + namespace { #ifndef _WIN32 using std::mutex; @@ -127,17 +154,17 @@ class lock_guard { namespace android { namespace base { -static mutex logging_lock; +static auto& logging_lock = *new mutex(); #ifdef __ANDROID__ -static LogFunction gLogger = LogdLogger(); +static auto& gLogger = *new LogFunction(LogdLogger()); #else -static LogFunction gLogger = StderrLogger; +static auto& gLogger = *new LogFunction(StderrLogger); #endif static bool gInitialized = false; static LogSeverity gMinimumLogSeverity = INFO; -static std::unique_ptr<std::string> gProgramInvocationName; +static auto& gProgramInvocationName = *new std::unique_ptr<std::string>(); LogSeverity GetMinimumLogSeverity() { return gMinimumLogSeverity; @@ -158,7 +185,7 @@ void StderrLogger(LogId, LogSeverity severity, const char*, const char* file, "Mismatch in size of log_characters and values in LogSeverity"); char severity_char = log_characters[severity]; fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n", ProgramInvocationName(), - severity_char, getpid(), gettid(), file, line, message); + severity_char, getpid(), GetThreadId(), file, line, message); } @@ -212,8 +239,8 @@ void InitLogging(char* argv[]) { gInitialized = true; // Stash the command line for later use. We can use /proc/self/cmdline on - // Linux to recover this, but we don't have that luxury on the Mac, and there - // are a couple of argv[0] variants that are commonly used. + // Linux to recover this, but we don't have that luxury on the Mac/Windows, + // and there are a couple of argv[0] variants that are commonly used. if (argv != nullptr) { gProgramInvocationName.reset(new std::string(basename(argv[0]))); } @@ -264,11 +291,20 @@ void SetLogger(LogFunction&& logger) { gLogger = std::move(logger); } -// We can't use basename(3) because this code runs on the Mac, which doesn't -// have a non-modifying basename. static const char* GetFileBasename(const char* file) { + // We can't use basename(3) even on Unix because the Mac doesn't + // have a non-modifying basename. const char* last_slash = strrchr(file, '/'); - return (last_slash == nullptr) ? file : last_slash + 1; + if (last_slash != nullptr) { + return last_slash + 1; + } +#if defined(_WIN32) + const char* last_backslash = strrchr(file, '\\'); + if (last_backslash != nullptr) { + return last_backslash + 1; + } +#endif + return file; } // This indirection greatly reduces the stack impact of having lots of diff --git a/base/utf8.cpp b/base/utf8.cpp new file mode 100755 index 000000000..99f0f5444 --- /dev/null +++ b/base/utf8.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2015 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 <windows.h> + +#include "base/utf8.h" + +#include <fcntl.h> + +#include <string> + +#include "base/logging.h" + +namespace android { +namespace base { + +// Helper to set errno based on GetLastError() after WideCharToMultiByte()/MultiByteToWideChar(). +static void SetErrnoFromLastError() { + switch (GetLastError()) { + case ERROR_NO_UNICODE_TRANSLATION: + errno = EILSEQ; + break; + default: + errno = EINVAL; + break; + } +} + +bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8) { + utf8->clear(); + + if (size == 0) { + return true; + } + + // TODO: Consider using std::wstring_convert once libcxx is supported on + // Windows. + + // Only Vista or later has this flag that causes WideCharToMultiByte() to + // return an error on invalid characters. + const DWORD flags = +#if (WINVER >= 0x0600) + WC_ERR_INVALID_CHARS; +#else + 0; +#endif + + const int chars_required = WideCharToMultiByte(CP_UTF8, flags, utf16, size, + NULL, 0, NULL, NULL); + if (chars_required <= 0) { + SetErrnoFromLastError(); + return false; + } + + // This could potentially throw a std::bad_alloc exception. + utf8->resize(chars_required); + + const int result = WideCharToMultiByte(CP_UTF8, flags, utf16, size, + &(*utf8)[0], chars_required, NULL, + NULL); + if (result != chars_required) { + SetErrnoFromLastError(); + CHECK_LE(result, chars_required) << "WideCharToMultiByte wrote " << result + << " chars to buffer of " << chars_required << " chars"; + utf8->clear(); + return false; + } + + return true; +} + +bool WideToUTF8(const wchar_t* utf16, std::string* utf8) { + // Compute string length of NULL-terminated string with wcslen(). + return WideToUTF8(utf16, wcslen(utf16), utf8); +} + +bool WideToUTF8(const std::wstring& utf16, std::string* utf8) { + // Use the stored length of the string which allows embedded NULL characters + // to be converted. + return WideToUTF8(utf16.c_str(), utf16.length(), utf8); +} + +// Internal helper function that takes MultiByteToWideChar() flags. +static bool UTF8ToWideWithFlags(const char* utf8, const size_t size, std::wstring* utf16, + const DWORD flags) { + utf16->clear(); + + if (size == 0) { + return true; + } + + // TODO: Consider using std::wstring_convert once libcxx is supported on + // Windows. + const int chars_required = MultiByteToWideChar(CP_UTF8, flags, utf8, size, + NULL, 0); + if (chars_required <= 0) { + SetErrnoFromLastError(); + return false; + } + + // This could potentially throw a std::bad_alloc exception. + utf16->resize(chars_required); + + const int result = MultiByteToWideChar(CP_UTF8, flags, utf8, size, + &(*utf16)[0], chars_required); + if (result != chars_required) { + SetErrnoFromLastError(); + CHECK_LE(result, chars_required) << "MultiByteToWideChar wrote " << result + << " chars to buffer of " << chars_required << " chars"; + utf16->clear(); + return false; + } + + return true; +} + +bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16) { + // If strictly interpreting as UTF-8 succeeds, return success. + if (UTF8ToWideWithFlags(utf8, size, utf16, MB_ERR_INVALID_CHARS)) { + return true; + } + + const int saved_errno = errno; + + // Fallback to non-strict interpretation, allowing invalid characters and + // converting as best as possible, and return false to signify a problem. + (void)UTF8ToWideWithFlags(utf8, size, utf16, 0); + errno = saved_errno; + return false; +} + +bool UTF8ToWide(const char* utf8, std::wstring* utf16) { + // Compute string length of NULL-terminated string with strlen(). + return UTF8ToWide(utf8, strlen(utf8), utf16); +} + +bool UTF8ToWide(const std::string& utf8, std::wstring* utf16) { + // Use the stored length of the string which allows embedded NULL characters + // to be converted. + return UTF8ToWide(utf8.c_str(), utf8.length(), utf16); +} + +// Versions of standard library APIs that support UTF-8 strings. +namespace utf8 { + +int open(const char* name, int flags, ...) { + std::wstring name_utf16; + if (!UTF8ToWide(name, &name_utf16)) { + return -1; + } + + int mode = 0; + if ((flags & O_CREAT) != 0) { + va_list args; + va_start(args, flags); + mode = va_arg(args, int); + va_end(args); + } + + return _wopen(name_utf16.c_str(), flags, mode); +} + +int unlink(const char* name) { + std::wstring name_utf16; + if (!UTF8ToWide(name, &name_utf16)) { + return -1; + } + + return _wunlink(name_utf16.c_str()); +} + +} // namespace utf8 +} // namespace base +} // namespace android diff --git a/base/utf8_test.cpp b/base/utf8_test.cpp new file mode 100755 index 000000000..13f6431d9 --- /dev/null +++ b/base/utf8_test.cpp @@ -0,0 +1,412 @@ +/* +* Copyright (C) 2015 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 "base/utf8.h" + +#include <gtest/gtest.h> + +#include "base/macros.h" + +namespace android { +namespace base { + +TEST(UTFStringConversionsTest, ConvertInvalidUTF8) { + std::wstring wide; + + errno = 0; + + // Standalone \xa2 is an invalid UTF-8 sequence, so this should return an + // error. Concatenate two C/C++ literal string constants to prevent the + // compiler from giving an error about "\xa2af" containing a "hex escape + // sequence out of range". + EXPECT_FALSE(android::base::UTF8ToWide("before\xa2" "after", &wide)); + + EXPECT_EQ(EILSEQ, errno); + + // Even if an invalid character is encountered, UTF8ToWide() should still do + // its best to convert the rest of the string. sysdeps_win32.cpp: + // _console_write_utf8() depends on this behavior. + // + // Thus, we verify that the valid characters are converted, but we ignore the + // specific replacement character that UTF8ToWide() may replace the invalid + // UTF-8 characters with because we want to allow that to change if the + // implementation changes. + EXPECT_EQ(0, wide.find(L"before")); + const wchar_t after_wide[] = L"after"; + EXPECT_EQ(wide.length() - (arraysize(after_wide) - 1), wide.find(after_wide)); +} + +// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/utf_string_conversions_unittest.cc + +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// The tests below from utf_string_conversions_unittest.cc check for this +// preprocessor symbol, so define it, as it is appropriate for Windows. +#define WCHAR_T_IS_UTF16 +static_assert(sizeof(wchar_t) == 2, "wchar_t is not 2 bytes"); + +// The tests below from utf_string_conversions_unittest.cc call versions of +// UTF8ToWide() and WideToUTF8() that don't return success/failure, so these are +// stub implementations with that signature. These are just for testing and +// should not be moved to base because they assert/expect no errors which is +// probably not a good idea (or at least it is something that should be left +// up to the caller, not a base library). + +static std::wstring UTF8ToWide(const std::string& utf8) { + std::wstring utf16; + EXPECT_TRUE(UTF8ToWide(utf8, &utf16)); + return utf16; +} + +static std::string WideToUTF8(const std::wstring& utf16) { + std::string utf8; + EXPECT_TRUE(WideToUTF8(utf16, &utf8)); + return utf8; +} + +namespace { + +const wchar_t* const kConvertRoundtripCases[] = { + L"Google Video", + // "网页 图片 资讯更多 »" + L"\x7f51\x9875\x0020\x56fe\x7247\x0020\x8d44\x8baf\x66f4\x591a\x0020\x00bb", + // "Παγκόσμιος Ιστός" + L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9" + L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2", + // "Поиск страниц на русском" + L"\x041f\x043e\x0438\x0441\x043a\x0020\x0441\x0442" + L"\x0440\x0430\x043d\x0438\x0446\x0020\x043d\x0430" + L"\x0020\x0440\x0443\x0441\x0441\x043a\x043e\x043c", + // "전체서비스" + L"\xc804\xccb4\xc11c\xbe44\xc2a4", + + // Test characters that take more than 16 bits. This will depend on whether + // wchar_t is 16 or 32 bits. +#if defined(WCHAR_T_IS_UTF16) + L"\xd800\xdf00", + // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E) + L"\xd807\xdd40\xd807\xdd41\xd807\xdd42\xd807\xdd43\xd807\xdd44", +#elif defined(WCHAR_T_IS_UTF32) + L"\x10300", + // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E) + L"\x11d40\x11d41\x11d42\x11d43\x11d44", +#endif +}; + +} // namespace + +TEST(UTFStringConversionsTest, ConvertUTF8AndWide) { + // we round-trip all the wide strings through UTF-8 to make sure everything + // agrees on the conversion. This uses the stream operators to test them + // simultaneously. + for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) { + std::ostringstream utf8; + utf8 << WideToUTF8(kConvertRoundtripCases[i]); + std::wostringstream wide; + wide << UTF8ToWide(utf8.str()); + + EXPECT_EQ(kConvertRoundtripCases[i], wide.str()); + } +} + +TEST(UTFStringConversionsTest, ConvertUTF8AndWideEmptyString) { + // An empty std::wstring should be converted to an empty std::string, + // and vice versa. + std::wstring wempty; + std::string empty; + EXPECT_EQ(empty, WideToUTF8(wempty)); + EXPECT_EQ(wempty, UTF8ToWide(empty)); +} + +TEST(UTFStringConversionsTest, ConvertUTF8ToWide) { + struct UTF8ToWideCase { + const char* utf8; + const wchar_t* wide; + bool success; + } convert_cases[] = { + // Regular UTF-8 input. + {"\xe4\xbd\xa0\xe5\xa5\xbd", L"\x4f60\x597d", true}, + // Non-character is passed through. + {"\xef\xbf\xbfHello", L"\xffffHello", true}, + // Truncated UTF-8 sequence. + {"\xe4\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false}, + // Truncated off the end. + {"\xe5\xa5\xbd\xe4\xa0", L"\x597d\xfffd", false}, + // Non-shortest-form UTF-8. + {"\xf0\x84\xbd\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false}, + // This UTF-8 character decodes to a UTF-16 surrogate, which is illegal. + // Note that for whatever reason, this test fails on Windows XP. + {"\xed\xb0\x80", L"\xfffd", false}, + // Non-BMP characters. The second is a non-character regarded as valid. + // The result will either be in UTF-16 or UTF-32. +#if defined(WCHAR_T_IS_UTF16) + {"A\xF0\x90\x8C\x80z", L"A\xd800\xdf00z", true}, + {"A\xF4\x8F\xBF\xBEz", L"A\xdbff\xdffez", true}, +#elif defined(WCHAR_T_IS_UTF32) + {"A\xF0\x90\x8C\x80z", L"A\x10300z", true}, + {"A\xF4\x8F\xBF\xBEz", L"A\x10fffez", true}, +#endif + }; + + for (size_t i = 0; i < arraysize(convert_cases); i++) { + std::wstring converted; + errno = 0; + const bool success = UTF8ToWide(convert_cases[i].utf8, + strlen(convert_cases[i].utf8), + &converted); + EXPECT_EQ(convert_cases[i].success, success); + // The original test always compared expected and converted, but don't do + // that because our implementation of UTF8ToWide() does not guarantee to + // produce the same output in error situations. + if (success) { + std::wstring expected(convert_cases[i].wide); + EXPECT_EQ(expected, converted); + } else { + EXPECT_EQ(EILSEQ, errno); + } + } + + // Manually test an embedded NULL. + std::wstring converted; + EXPECT_TRUE(UTF8ToWide("\00Z\t", 3, &converted)); + ASSERT_EQ(3U, converted.length()); + EXPECT_EQ(static_cast<wchar_t>(0), converted[0]); + EXPECT_EQ('Z', converted[1]); + EXPECT_EQ('\t', converted[2]); + + // Make sure that conversion replaces, not appends. + EXPECT_TRUE(UTF8ToWide("B", 1, &converted)); + ASSERT_EQ(1U, converted.length()); + EXPECT_EQ('B', converted[0]); +} + +#if defined(WCHAR_T_IS_UTF16) +// This test is only valid when wchar_t == UTF-16. +TEST(UTFStringConversionsTest, ConvertUTF16ToUTF8) { + struct WideToUTF8Case { + const wchar_t* utf16; + const char* utf8; + bool success; + } convert_cases[] = { + // Regular UTF-16 input. + {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true}, + // Test a non-BMP character. + {L"\xd800\xdf00", "\xF0\x90\x8C\x80", true}, + // Non-characters are passed through. + {L"\xffffHello", "\xEF\xBF\xBFHello", true}, + {L"\xdbff\xdffeHello", "\xF4\x8F\xBF\xBEHello", true}, + // The first character is a truncated UTF-16 character. + // Note that for whatever reason, this test fails on Windows XP. + {L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd", +#if (WINVER >= 0x0600) + // Only Vista and later has a new API/flag that correctly returns false. + false +#else + true +#endif + }, + // Truncated at the end. + // Note that for whatever reason, this test fails on Windows XP. + {L"\x597d\xd800", "\xe5\xa5\xbd\xef\xbf\xbd", +#if (WINVER >= 0x0600) + // Only Vista and later has a new API/flag that correctly returns false. + false +#else + true +#endif + }, + }; + + for (size_t i = 0; i < arraysize(convert_cases); i++) { + std::string converted; + errno = 0; + const bool success = WideToUTF8(convert_cases[i].utf16, + wcslen(convert_cases[i].utf16), + &converted); + EXPECT_EQ(convert_cases[i].success, success); + // The original test always compared expected and converted, but don't do + // that because our implementation of WideToUTF8() does not guarantee to + // produce the same output in error situations. + if (success) { + std::string expected(convert_cases[i].utf8); + EXPECT_EQ(expected, converted); + } else { + EXPECT_EQ(EILSEQ, errno); + } + } +} + +#elif defined(WCHAR_T_IS_UTF32) +// This test is only valid when wchar_t == UTF-32. +TEST(UTFStringConversionsTest, ConvertUTF32ToUTF8) { + struct WideToUTF8Case { + const wchar_t* utf32; + const char* utf8; + bool success; + } convert_cases[] = { + // Regular 16-bit input. + {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true}, + // Test a non-BMP character. + {L"A\x10300z", "A\xF0\x90\x8C\x80z", true}, + // Non-characters are passed through. + {L"\xffffHello", "\xEF\xBF\xBFHello", true}, + {L"\x10fffeHello", "\xF4\x8F\xBF\xBEHello", true}, + // Invalid Unicode code points. + {L"\xfffffffHello", "\xEF\xBF\xBDHello", false}, + // The first character is a truncated UTF-16 character. + {L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd", false}, + {L"\xdc01Hello", "\xef\xbf\xbdHello", false}, + }; + + for (size_t i = 0; i < arraysize(convert_cases); i++) { + std::string converted; + EXPECT_EQ(convert_cases[i].success, + WideToUTF8(convert_cases[i].utf32, + wcslen(convert_cases[i].utf32), + &converted)); + std::string expected(convert_cases[i].utf8); + EXPECT_EQ(expected, converted); + } +} +#endif // defined(WCHAR_T_IS_UTF32) + +// The test below uses these types and functions, so just do enough to get the +// test running. +typedef wchar_t char16; +typedef std::wstring string16; + +template<typename T> +static void* WriteInto(T* t, size_t size) { + // std::(w)string::resize() already includes space for a NULL terminator. + t->resize(size - 1); + return &(*t)[0]; +} + +// A stub implementation that calls a helper from above, just to get the test +// below working. This is just for testing and should not be moved to base +// because this ignores errors which is probably not a good idea, plus it takes +// a string16 type which we don't really have. +static std::string UTF16ToUTF8(const string16& utf16) { + return WideToUTF8(utf16); +} + +TEST(UTFStringConversionsTest, ConvertMultiString) { + static char16 multi16[] = { + 'f', 'o', 'o', '\0', + 'b', 'a', 'r', '\0', + 'b', 'a', 'z', '\0', + '\0' + }; + static char multi[] = { + 'f', 'o', 'o', '\0', + 'b', 'a', 'r', '\0', + 'b', 'a', 'z', '\0', + '\0' + }; + string16 multistring16; + memcpy(WriteInto(&multistring16, arraysize(multi16)), multi16, + sizeof(multi16)); + EXPECT_EQ(arraysize(multi16) - 1, multistring16.length()); + std::string expected; + memcpy(WriteInto(&expected, arraysize(multi)), multi, sizeof(multi)); + EXPECT_EQ(arraysize(multi) - 1, expected.length()); + const std::string& converted = UTF16ToUTF8(multistring16); + EXPECT_EQ(arraysize(multi) - 1, converted.length()); + EXPECT_EQ(expected, converted); +} + +// The tests below from sys_string_conversions_unittest.cc call SysWideToUTF8() +// and SysUTF8ToWide(), so these are stub implementations that call the helpers +// above. These are just for testing and should not be moved to base because +// they ignore errors which is probably not a good idea. + +static std::string SysWideToUTF8(const std::wstring& utf16) { + return WideToUTF8(utf16); +} + +static std::wstring SysUTF8ToWide(const std::string& utf8) { + return UTF8ToWide(utf8); +} + +// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/sys_string_conversions_unittest.cc + +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifdef WCHAR_T_IS_UTF32 +static const std::wstring kSysWideOldItalicLetterA = L"\x10300"; +#else +static const std::wstring kSysWideOldItalicLetterA = L"\xd800\xdf00"; +#endif + +TEST(SysStrings, SysWideToUTF8) { + EXPECT_EQ("Hello, world", SysWideToUTF8(L"Hello, world")); + EXPECT_EQ("\xe4\xbd\xa0\xe5\xa5\xbd", SysWideToUTF8(L"\x4f60\x597d")); + + // >16 bits + EXPECT_EQ("\xF0\x90\x8C\x80", SysWideToUTF8(kSysWideOldItalicLetterA)); + + // Error case. When Windows finds a UTF-16 character going off the end of + // a string, it just converts that literal value to UTF-8, even though this + // is invalid. + // + // This is what XP does, but Vista has different behavior, so we don't bother + // verifying it: + // EXPECT_EQ("\xE4\xBD\xA0\xED\xA0\x80zyxw", + // SysWideToUTF8(L"\x4f60\xd800zyxw")); + + // Test embedded NULLs. + std::wstring wide_null(L"a"); + wide_null.push_back(0); + wide_null.push_back('b'); + + std::string expected_null("a"); + expected_null.push_back(0); + expected_null.push_back('b'); + + EXPECT_EQ(expected_null, SysWideToUTF8(wide_null)); +} + +TEST(SysStrings, SysUTF8ToWide) { + EXPECT_EQ(L"Hello, world", SysUTF8ToWide("Hello, world")); + EXPECT_EQ(L"\x4f60\x597d", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5\xbd")); + // >16 bits + EXPECT_EQ(kSysWideOldItalicLetterA, SysUTF8ToWide("\xF0\x90\x8C\x80")); + + // Error case. When Windows finds an invalid UTF-8 character, it just skips + // it. This seems weird because it's inconsistent with the reverse conversion. + // + // This is what XP does, but Vista has different behavior, so we don't bother + // verifying it: + // EXPECT_EQ(L"\x4f60zyxw", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5zyxw")); + + // Test embedded NULLs. + std::string utf8_null("a"); + utf8_null.push_back(0); + utf8_null.push_back('b'); + + std::wstring expected_null(L"a"); + expected_null.push_back(0); + expected_null.push_back('b'); + + EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null)); +} + +} // namespace base +} // namespace android diff --git a/crash_reporter/Android.mk b/crash_reporter/Android.mk index c5ca4e40c..81cb458f5 100644 --- a/crash_reporter/Android.mk +++ b/crash_reporter/Android.mk @@ -102,9 +102,13 @@ LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/$(OSRELEASED_DIRECTORY) include $(BUILD_SYSTEM)/base_rules.mk +# Optionally populate the BRILLO_CRASH_SERVER variable from a product +# configuration file: brillo/crash_server. +LOADED_BRILLO_CRASH_SERVER := $(call cfgtree-get-if-exists,brillo/crash_server) + # If the crash server isn't set, use a blank value. crash_sender # will log it as a configuration error. -$(LOCAL_BUILT_MODULE): BRILLO_CRASH_SERVER ?= "" +$(LOCAL_BUILT_MODULE): BRILLO_CRASH_SERVER ?= "$(LOADED_BRILLO_CRASH_SERVER)" $(LOCAL_BUILT_MODULE): $(hide)mkdir -p $(dir $@) echo $(BRILLO_CRASH_SERVER) > $@ diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc index 56e7bb912..6714f5239 100644 --- a/crash_reporter/user_collector.cc +++ b/crash_reporter/user_collector.cc @@ -90,9 +90,9 @@ void UserCollector::Initialize( directory_failure_ = directory_failure; filter_in_ = filter_in; - gid_t groups[] = { AID_ROOT, AID_SYSTEM, AID_DBUS }; + gid_t groups[] = { AID_ROOT, AID_SYSTEM, AID_DBUS, AID_READPROC }; if (setgroups(arraysize(groups), groups) != 0) { - PLOG(FATAL) << "Unable to set groups to root, system, and dbus"; + PLOG(FATAL) << "Unable to set groups to root, system, dbus, and readproc"; } } diff --git a/debuggerd/debuggerd.rc b/debuggerd/debuggerd.rc index 4338ae92b..e43fe96cf 100644 --- a/debuggerd/debuggerd.rc +++ b/debuggerd/debuggerd.rc @@ -1,2 +1,4 @@ service debuggerd /system/bin/debuggerd class main + group root readproc + writepid /dev/cpuset/system-background/tasks diff --git a/debuggerd/debuggerd64.rc b/debuggerd/debuggerd64.rc index 341a3296d..35b5af35c 100644 --- a/debuggerd/debuggerd64.rc +++ b/debuggerd/debuggerd64.rc @@ -1,2 +1,4 @@ service debuggerd64 /system/bin/debuggerd64 class main + group root readproc + writepid /dev/cpuset/system-background/tasks diff --git a/fastboot/Android.mk b/fastboot/Android.mk index 3f201ec1c..7a7366eee 100644 --- a/fastboot/Android.mk +++ b/fastboot/Android.mk @@ -49,7 +49,7 @@ LOCAL_STATIC_LIBRARIES := \ libutils \ liblog \ libz \ - libbase + libbase \ LOCAL_STATIC_LIBRARIES_darwin := libselinux LOCAL_STATIC_LIBRARIES_linux := libselinux diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp index 44796d7d9..d4be63b73 100644 --- a/fastboot/engine.cpp +++ b/fastboot/engine.cpp @@ -130,6 +130,13 @@ static Action *queue_action(unsigned op, const char *fmt, ...) return a; } +void fb_set_active(const char *slot) +{ + Action *a; + a = queue_action(OP_COMMAND, "set_active:%s", slot); + a->msg = mkmsg("Setting current slot to '%s'", slot); +} + void fb_queue_erase(const char *ptn) { Action *a; diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp index 226f3effe..9f72c8336 100644 --- a/fastboot/fastboot.cpp +++ b/fastboot/fastboot.cpp @@ -42,12 +42,16 @@ #include <sys/time.h> #include <sys/types.h> #include <unistd.h> +#include <functional> #include <base/parseint.h> #include <base/strings.h> #include <sparse/sparse.h> #include <ziparchive/zip_archive.h> +#include <base/strings.h> +#include <base/parseint.h> + #include "bootimg_utils.h" #include "fastboot.h" #include "fs.h" @@ -262,6 +266,11 @@ static void usage() { " partitions.\n" " flashing get_unlock_ability Queries bootloader to see if the\n" " device is unlocked.\n" + " flashing get_unlock_bootloader_nonce Queries the bootloader to get the\n" + " unlock nonce.\n" + " flashing unlock_bootloader <request> Issue unlock bootloader using request.\n" + " flashing lock_bootloader Locks the bootloader to prevent\n" + " bootloader version rollback.\n" " erase <partition> Erase a flash partition.\n" " format[:[<fs type>][:[<size>]] <partition>\n" " Format a flash partition. Can\n" @@ -288,15 +297,34 @@ static void usage() { " -p <product> Specify product name.\n" " -c <cmdline> Override kernel commandline.\n" " -i <vendor id> Specify a custom USB vendor id.\n" - " -b <base_addr> Specify a custom kernel base\n" + " -b, --base <base_addr> Specify a custom kernel base\n" " address (default: 0x10000000).\n" - " -n <page size> Specify the nand page size\n" + " --kernel-offset Specify a custom kernel offset.\n" + " (default: 0x00008000)\n" + " --ramdisk-offset Specify a custom ramdisk offset.\n" + " (default: 0x01000000)\n" + " --tags-offset Specify a custom tags offset.\n" + " (default: 0x00000100)\n" + " -n, --page-size <page size> Specify the nand page size\n" " (default: 2048).\n" " -S <size>[K|M|G] Automatically sparse files greater\n" " than 'size'. 0 to disable.\n" + " --slot <suffix> Specify slot suffix to be used if the\n" + " device supports slots. This will be\n" + " added to all partition names that use\n" + " slots. 'all' can be given to refer\n" + " to all slots. If this is not given,\n" + " slotted partitions will default to\n" + " the current active slot.\n" + " -a, --set-active[=<suffix>] Sets the active slot. If no suffix is\n" + " provided, this will default to the value\n" + " given by --slot. If slots are not\n" + " supported, this does nothing.\n" + " --unbuffered Do not buffer input or output.\n" + " --version Display version.\n" + " -h, --help show this message.\n" ); } - static void* load_bootable_image(const char* kernel, const char* ramdisk, const char* secondstage, int64_t* sz, const char* cmdline) { @@ -571,7 +599,7 @@ static struct sparse_file **load_sparse_files(int fd, int max_size) static int64_t get_target_sparse_limit(usb_handle* usb) { std::string max_download_size; - if (!fb_getvar(usb, "max-download-size", &max_download_size)) { + if (!fb_getvar(usb, "max-download-size", &max_download_size) || max_download_size.empty()) { fprintf(stderr, "target didn't report max-download-size\n"); return 0; } @@ -685,6 +713,80 @@ static void flash_buf(const char *pname, struct fastboot_buffer *buf) } } +static std::vector<std::string> get_suffixes(usb_handle* usb) { + std::vector<std::string> suffixes; + std::string suffix_list; + if (!fb_getvar(usb, "slot-suffixes", &suffix_list)) { + die("Could not get suffixes.\n"); + } + return android::base::Split(suffix_list, ","); +} + +static std::string verify_slot(usb_handle* usb, const char *slot) { + if (strcmp(slot, "all") == 0) { + return "all"; + } + std::vector<std::string> suffixes = get_suffixes(usb); + for (const std::string &suffix : suffixes) { + if (suffix == slot) + return slot; + } + fprintf(stderr, "Slot %s does not exist. supported slots are:\n", slot); + for (const std::string &suffix : suffixes) { + fprintf(stderr, "%s\n", suffix.c_str()); + } + exit(1); +} + +static void do_for_partition(usb_handle* usb, const char *part, const char *slot, std::function<void(const std::string&)> func, bool force_slot) { + std::string has_slot; + std::string current_slot; + + if (!fb_getvar(usb, std::string("has-slot:")+part, &has_slot)) { + /* If has-slot is not supported, the answer is no. */ + has_slot = "no"; + } + if (has_slot == "yes") { + if (!slot || slot[0] == 0) { + if (!fb_getvar(usb, "current-slot", ¤t_slot)) { + die("Failed to identify current slot.\n"); + } + func(std::string(part) + current_slot); + } else { + func(std::string(part) + slot); + } + } else { + if (force_slot && slot && slot[0]) { + fprintf(stderr, "Warning: %s does not support slots, and slot %s was requested.\n", part, slot); + } + func(part); + } +} + +/* This function will find the real partition name given a base name, and a slot. If slot is NULL or empty, + * it will use the current slot. If slot is "all", it will return a list of all possible partition names. + * If force_slot is true, it will fail if a slot is specified, and the given partition does not support slots. + */ +static void do_for_partitions(usb_handle* usb, const char *part, const char *slot, std::function<void(const std::string&)> func, bool force_slot) { + std::string has_slot; + + if (slot && strcmp(slot, "all") == 0) { + if (!fb_getvar(usb, std::string("has-slot:") + part, &has_slot)) { + die("Could not check if partition %s has slot.", part); + } + if (has_slot == "yes") { + std::vector<std::string> suffixes = get_suffixes(usb); + for (std::string &suffix : suffixes) { + do_for_partition(usb, part, suffix.c_str(), func, force_slot); + } + } else { + do_for_partition(usb, part, "", func, force_slot); + } + } else { + do_for_partition(usb, part, slot, func, force_slot); + } +} + static void do_flash(usb_handle* usb, const char* pname, const char* fname) { struct fastboot_buffer buf; @@ -702,7 +804,7 @@ static void do_update_signature(ZipArchiveHandle zip, char* fn) { fb_queue_command("signature", "installing signature"); } -static void do_update(usb_handle* usb, const char* filename, bool erase_first) { +static void do_update(usb_handle* usb, const char* filename, const char* slot_override, bool erase_first) { queue_info_dump(); fb_queue_query_save("product", cur_product, sizeof(cur_product)); @@ -735,15 +837,19 @@ static void do_update(usb_handle* usb, const char* filename, bool erase_first) { fastboot_buffer buf; int rc = load_buf_fd(usb, fd, &buf); if (rc) die("cannot load %s from flash", images[i].img_name); - do_update_signature(zip, images[i].sig_name); - if (erase_first && needs_erase(usb, images[i].part_name)) { - fb_queue_erase(images[i].part_name); - } - flash_buf(images[i].part_name, &buf); - /* not closing the fd here since the sparse code keeps the fd around - * but hasn't mmaped data yet. The tmpfile will get cleaned up when the - * program exits. - */ + + auto update = [&](const std::string &partition) { + do_update_signature(zip, images[i].sig_name); + if (erase_first && needs_erase(usb, partition.c_str())) { + fb_queue_erase(partition.c_str()); + } + flash_buf(partition.c_str(), &buf); + /* not closing the fd here since the sparse code keeps the fd around + * but hasn't mmaped data yet. The tmpfile will get cleaned up when the + * program exits. + */ + }; + do_for_partitions(usb, images[i].part_name, slot_override, update, false); } CloseArchive(zip); @@ -765,7 +871,7 @@ static void do_send_signature(char* fn) { fb_queue_command("signature", "installing signature"); } -static void do_flashall(usb_handle* usb, int erase_first) { +static void do_flashall(usb_handle* usb, const char *slot_override, int erase_first) { queue_info_dump(); fb_queue_query_save("product", cur_product, sizeof(cur_product)); @@ -787,18 +893,43 @@ static void do_flashall(usb_handle* usb, int erase_first) { continue; die("could not load %s\n", images[i].img_name); } - do_send_signature(fname); - if (erase_first && needs_erase(usb, images[i].part_name)) { - fb_queue_erase(images[i].part_name); - } - flash_buf(images[i].part_name, &buf); + + auto flashall = [&](const std::string &partition) { + do_send_signature(fname); + if (erase_first && needs_erase(usb, partition.c_str())) { + fb_queue_erase(partition.c_str()); + } + flash_buf(partition.c_str(), &buf); + }; + do_for_partitions(usb, images[i].part_name, slot_override, flashall, false); } } #define skip(n) do { argc -= (n); argv += (n); } while (0) #define require(n) do { if (argc < (n)) {usage(); exit(1);}} while (0) -static int do_oem_command(int argc, char** argv) { +static int do_bypass_unlock_command(int argc, char **argv) +{ + if (argc <= 2) return 0; + skip(2); + + /* + * Process unlock_bootloader, we have to load the message file + * and send that to the remote device. + */ + require(1); + + int64_t sz; + void* data = load_file(*argv, &sz); + if (data == nullptr) die("could not load '%s': %s", *argv, strerror(errno)); + fb_queue_download("unlock_message", data, sz); + fb_queue_command("flashing unlock_bootloader", "unlocking bootloader"); + skip(1); + return 0; +} + +static int do_oem_command(int argc, char **argv) +{ char command[256]; if (argc <= 1) return 0; @@ -896,6 +1027,11 @@ static void fb_perform_format(usb_handle* usb, } partition_size = size_override; } + // Some bootloaders (angler, for example), send spurious leading whitespace. + partition_size = android::base::Trim(partition_size); + // Some bootloaders (hammerhead, for example) use implicit hex. + // This code used to use strtol with base 16. + if (!android::base::StartsWith(partition_size, "0x")) partition_size = "0x" + partition_size; gen = fs_get_generator(partition_type); if (!gen) { @@ -940,35 +1076,50 @@ failed: int main(int argc, char **argv) { - int wants_wipe = 0; - int wants_reboot = 0; - int wants_reboot_bootloader = 0; + bool wants_wipe = false; + bool wants_reboot = false; + bool wants_reboot_bootloader = false; + bool wants_set_active = false; bool erase_first = true; void *data; int64_t sz; int longindex; + std::string slot_override; + std::string next_active; const struct option longopts[] = { {"base", required_argument, 0, 'b'}, {"kernel_offset", required_argument, 0, 'k'}, + {"kernel-offset", required_argument, 0, 'k'}, {"page_size", required_argument, 0, 'n'}, + {"page-size", required_argument, 0, 'n'}, {"ramdisk_offset", required_argument, 0, 'r'}, + {"ramdisk-offset", required_argument, 0, 'r'}, {"tags_offset", required_argument, 0, 't'}, + {"tags-offset", required_argument, 0, 't'}, {"help", no_argument, 0, 'h'}, {"unbuffered", no_argument, 0, 0}, {"version", no_argument, 0, 0}, + {"slot", required_argument, 0, 0}, + {"set_active", optional_argument, 0, 'a'}, + {"set-active", optional_argument, 0, 'a'}, {0, 0, 0, 0} }; serial = getenv("ANDROID_SERIAL"); while (1) { - int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:h", longopts, &longindex); + int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:ha::", longopts, &longindex); if (c < 0) { break; } /* Alphabetical cases */ switch (c) { + case 'a': + wants_set_active = true; + if (optarg) + next_active = optarg; + break; case 'b': base_addr = strtoul(optarg, 0, 16); break; @@ -1020,7 +1171,7 @@ int main(int argc, char **argv) erase_first = false; break; case 'w': - wants_wipe = 1; + wants_wipe = true; break; case '?': return 1; @@ -1031,6 +1182,8 @@ int main(int argc, char **argv) } else if (strcmp("version", longopts[longindex].name) == 0) { fprintf(stdout, "fastboot version %s\n", FASTBOOT_REVISION); return 0; + } else if (strcmp("slot", longopts[longindex].name) == 0) { + slot_override = std::string(optarg); } break; default: @@ -1041,7 +1194,7 @@ int main(int argc, char **argv) argc -= optind; argv += optind; - if (argc == 0 && !wants_wipe) { + if (argc == 0 && !wants_wipe && !wants_set_active) { usage(); return 1; } @@ -1058,6 +1211,20 @@ int main(int argc, char **argv) } usb_handle* usb = open_device(); + if (slot_override != "") + slot_override = verify_slot(usb, slot_override.c_str()); + if (next_active != "") + next_active = verify_slot(usb, next_active.c_str()); + + if (wants_set_active) { + if (next_active == "") { + if (slot_override == "") { + wants_set_active = false; + } else { + next_active = slot_override; + } + } + } while (argc > 0) { if (!strcmp(*argv, "getvar")) { @@ -1067,14 +1234,17 @@ int main(int argc, char **argv) } else if(!strcmp(*argv, "erase")) { require(2); + auto erase = [&](const std::string &partition) { std::string partition_type; - if (fb_getvar(usb, std::string("partition-type:") + argv[1], &partition_type) && - fs_get_generator(partition_type) != nullptr) { - fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n", - partition_type.c_str()); - } + if (fb_getvar(usb, std::string("partition-type:") + argv[1], &partition_type) && + fs_get_generator(partition_type) != nullptr) { + fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n", + partition_type.c_str()); + } - fb_queue_erase(argv[1]); + fb_queue_erase(partition.c_str()); + }; + do_for_partitions(usb, argv[1], slot_override.c_str(), erase, true); skip(2); } else if(!strncmp(*argv, "format", strlen("format"))) { char *overrides; @@ -1102,10 +1272,14 @@ int main(int argc, char **argv) } if (type_override && !type_override[0]) type_override = nullptr; if (size_override && !size_override[0]) size_override = nullptr; - if (erase_first && needs_erase(usb, argv[1])) { - fb_queue_erase(argv[1]); - } - fb_perform_format(usb, argv[1], 0, type_override, size_override); + + auto format = [&](const std::string &partition) { + if (erase_first && needs_erase(usb, partition.c_str())) { + fb_queue_erase(partition.c_str()); + } + fb_perform_format(usb, partition.c_str(), 0, type_override, size_override); + }; + do_for_partitions(usb, argv[1], slot_override.c_str(), format, true); skip(2); } else if(!strcmp(*argv, "signature")) { require(2); @@ -1116,18 +1290,18 @@ int main(int argc, char **argv) fb_queue_command("signature", "installing signature"); skip(2); } else if(!strcmp(*argv, "reboot")) { - wants_reboot = 1; + wants_reboot = true; skip(1); if (argc > 0) { if (!strcmp(*argv, "bootloader")) { - wants_reboot = 0; - wants_reboot_bootloader = 1; + wants_reboot = false; + wants_reboot_bootloader = true; skip(1); } } require(0); } else if(!strcmp(*argv, "reboot-bootloader")) { - wants_reboot_bootloader = 1; + wants_reboot_bootloader = true; skip(1); } else if (!strcmp(*argv, "continue")) { fb_queue_command("continue", "resuming boot"); @@ -1165,12 +1339,15 @@ int main(int argc, char **argv) skip(2); } if (fname == 0) die("cannot determine image filename for '%s'", pname); - if (erase_first && needs_erase(usb, pname)) { - fb_queue_erase(pname); - } - do_flash(usb, pname, fname); + + auto flash = [&](const std::string &partition) { + if (erase_first && needs_erase(usb, partition.c_str())) { + fb_queue_erase(partition.c_str()); + } + do_flash(usb, partition.c_str(), fname); + }; + do_for_partitions(usb, pname, slot_override.c_str(), flash, true); } else if(!strcmp(*argv, "flash:raw")) { - char *pname = argv[1]; char *kname = argv[2]; char *rname = 0; char *sname = 0; @@ -1186,28 +1363,37 @@ int main(int argc, char **argv) } data = load_bootable_image(kname, rname, sname, &sz, cmdline); if (data == 0) die("cannot load bootable image"); - fb_queue_flash(pname, data, sz); + auto flashraw = [&](const std::string &partition) { + fb_queue_flash(partition.c_str(), data, sz); + }; + do_for_partitions(usb, argv[1], slot_override.c_str(), flashraw, true); } else if(!strcmp(*argv, "flashall")) { skip(1); - do_flashall(usb, erase_first); - wants_reboot = 1; + do_flashall(usb, slot_override.c_str(), erase_first); + wants_reboot = true; } else if(!strcmp(*argv, "update")) { if (argc > 1) { - do_update(usb, argv[1], erase_first); + do_update(usb, argv[1], slot_override.c_str(), erase_first); skip(2); } else { - do_update(usb, "update.zip", erase_first); + do_update(usb, "update.zip", slot_override.c_str(), erase_first); skip(1); } - wants_reboot = 1; + wants_reboot = true; } else if(!strcmp(*argv, "oem")) { argc = do_oem_command(argc, argv); - } else if(!strcmp(*argv, "flashing") && argc == 2) { - if(!strcmp(*(argv+1), "unlock") || !strcmp(*(argv+1), "lock") - || !strcmp(*(argv+1), "unlock_critical") - || !strcmp(*(argv+1), "lock_critical") - || !strcmp(*(argv+1), "get_unlock_ability")) { - argc = do_oem_command(argc, argv); + } else if(!strcmp(*argv, "flashing")) { + if (argc == 2 && (!strcmp(*(argv+1), "unlock") || + !strcmp(*(argv+1), "lock") || + !strcmp(*(argv+1), "unlock_critical") || + !strcmp(*(argv+1), "lock_critical") || + !strcmp(*(argv+1), "get_unlock_ability") || + !strcmp(*(argv+1), "get_unlock_bootloader_nonce") || + !strcmp(*(argv+1), "lock_bootloader"))) { + argc = do_oem_command(argc, argv); + } else + if (argc == 3 && !strcmp(*(argv+1), "unlock_bootloader")) { + argc = do_bypass_unlock_command(argc, argv); } else { usage(); return 1; @@ -1230,6 +1416,9 @@ int main(int argc, char **argv) fb_perform_format(usb, "cache", 1, nullptr, nullptr); } } + if (wants_set_active) { + fb_set_active(next_active.c_str()); + } if (wants_reboot) { fb_queue_reboot(); fb_queue_wait_for_disconnect(); diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h index 9e33531a4..784ec5c10 100644 --- a/fastboot/fastboot.h +++ b/fastboot/fastboot.h @@ -64,6 +64,7 @@ void fb_queue_download(const char *name, void *data, uint32_t size); void fb_queue_notice(const char *notice); void fb_queue_wait_for_disconnect(void); int fb_execute_queue(usb_handle *usb); +void fb_set_active(const char *slot); /* util stuff */ double now(); diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h index 75fe11dc1..c8010ca17 100644 --- a/gatekeeperd/SoftGateKeeper.h +++ b/gatekeeperd/SoftGateKeeper.h @@ -25,8 +25,10 @@ extern "C" { #include <crypto_scrypt.h> } +#include <base/memory.h> #include <UniquePtr.h> #include <gatekeeper/gatekeeper.h> + #include <iostream> #include <unordered_map> @@ -150,14 +152,15 @@ public: } bool DoVerify(const password_handle_t *expected_handle, const SizedBuffer &password) { - FastHashMap::const_iterator it = fast_hash_map_.find(expected_handle->user_id); + uint64_t user_id = android::base::get_unaligned(&expected_handle->user_id); + FastHashMap::const_iterator it = fast_hash_map_.find(user_id); if (it != fast_hash_map_.end() && VerifyFast(it->second, password)) { return true; } else { if (GateKeeper::DoVerify(expected_handle, password)) { uint64_t salt; GetRandom(&salt, sizeof(salt)); - fast_hash_map_[expected_handle->user_id] = ComputeFastHash(password, salt); + fast_hash_map_[user_id] = ComputeFastHash(password, salt); return true; } } diff --git a/gatekeeperd/tests/Android.mk b/gatekeeperd/tests/Android.mk index 6fc4ac075..a62b1d424 100644 --- a/gatekeeperd/tests/Android.mk +++ b/gatekeeperd/tests/Android.mk @@ -20,7 +20,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := gatekeeperd-unit-tests LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers -LOCAL_SHARED_LIBRARIES := libgatekeeper libcrypto +LOCAL_SHARED_LIBRARIES := libgatekeeper libcrypto libbase LOCAL_STATIC_LIBRARIES := libscrypt_static LOCAL_C_INCLUDES := external/scrypt/lib/crypto LOCAL_SRC_FILES := \ diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp index e280fdc32..63904b653 100644 --- a/healthd/BatteryMonitor.cpp +++ b/healthd/BatteryMonitor.cpp @@ -136,6 +136,9 @@ BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String { "USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC }, { "USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC }, { "USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC }, + { "USB_C", ANDROID_POWER_SUPPLY_TYPE_AC }, + { "USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC }, + { "USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB }, { "Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS }, { NULL, 0 }, }; @@ -183,6 +186,7 @@ bool BatteryMonitor::update(void) { props.chargerWirelessOnline = false; props.batteryStatus = BATTERY_STATUS_UNKNOWN; props.batteryHealth = BATTERY_HEALTH_UNKNOWN; + props.maxChargingCurrent = 0; if (!mHealthdConfig->batteryPresentPath.isEmpty()) props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath); @@ -246,6 +250,15 @@ bool BatteryMonitor::update(void) { KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n", mChargerNames[i].string()); } + path.clear(); + path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH, + mChargerNames[i].string()); + if (access(path.string(), R_OK) == 0) { + int maxChargingCurrent = getIntField(path); + if (props.maxChargingCurrent < maxChargingCurrent) { + props.maxChargingCurrent = maxChargingCurrent; + } + } } } } @@ -382,9 +395,9 @@ void BatteryMonitor::dumpState(int fd) { int v; char vs[128]; - snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d\n", + snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d\n", props.chargerAcOnline, props.chargerUsbOnline, - props.chargerWirelessOnline); + props.chargerWirelessOnline, props.maxChargingCurrent); write(fd, vs, strlen(vs)); snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n", props.batteryStatus, props.batteryHealth, props.batteryPresent); diff --git a/include/log/log.h b/include/log/log.h index 1cdf7bc49..e0dc3a3d0 100644 --- a/include/log/log.h +++ b/include/log/log.h @@ -616,7 +616,12 @@ typedef enum log_id { * Use the per-tag properties "log.tag.<tagname>" to generate a runtime * result of non-zero to expose a log. */ -int __android_log_is_loggable(int prio, const char *tag, int def); +/* default prio ANDROID_LOG_VERBOSE to ANDROID_LOG_FATAL if no property */ +#define ANDROID_LOGGABLE_FLAG_DEFAULT_MASK 0x000F +/* Save 2 syscalls if caller guarantees to never call within signal handler */ +#define ANDROID_LOGGABLE_FLAG_NOT_WITHIN_SIGNAL 0x8000 + +int __android_log_is_loggable(int prio, const char *tag, int flag); int __android_log_error_write(int tag, const char *subTag, int32_t uid, const char *data, uint32_t dataLen); diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h index c7eb34b01..e2133e905 100644 --- a/include/private/android_filesystem_config.h +++ b/include/private/android_filesystem_config.h @@ -101,6 +101,7 @@ #define AID_NET_BW_STATS 3006 /* read bandwidth statistics */ #define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */ #define AID_NET_BT_STACK 3008 /* bluetooth: access config files */ +#define AID_READPROC 3009 /* Allow /proc read access */ /* The range 5000-5999 is also reserved for OEM, and must never be used here. */ #define AID_OEM_RESERVED_2_START 5000 @@ -191,6 +192,7 @@ static const struct android_id_info android_ids[] = { { "net_bw_stats", AID_NET_BW_STATS, }, { "net_bw_acct", AID_NET_BW_ACCT, }, { "net_bt_stack", AID_NET_BT_STACK, }, + { "readproc", AID_READPROC, }, { "everybody", AID_EVERYBODY, }, { "misc", AID_MISC, }, diff --git a/include/utils/Errors.h b/include/utils/Errors.h index 08ddcd25c..16e1fa2c2 100644 --- a/include/utils/Errors.h +++ b/include/utils/Errors.h @@ -72,6 +72,7 @@ enum { UNKNOWN_TRANSACTION = (UNKNOWN_ERROR + 6), #endif FDS_NOT_ALLOWED = (UNKNOWN_ERROR + 7), + UNEXPECTED_NULL = (UNKNOWN_ERROR + 8), }; // Restore define; enumeration is in "android" namespace, so the value defined diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h index 20a379ecc..1741fb2e4 100644 --- a/include/utils/LruCache.h +++ b/include/utils/LruCache.h @@ -17,10 +17,9 @@ #ifndef ANDROID_UTILS_LRU_CACHE_H #define ANDROID_UTILS_LRU_CACHE_H +#include <memory> #include <unordered_set> -#include <UniquePtr.h> - #include "utils/TypeHelpers.h" // hash_t namespace android { @@ -91,7 +90,7 @@ private: return result; } - UniquePtr<LruCacheSet> mSet; + std::unique_ptr<LruCacheSet> mSet; OnEntryRemoved<TKey, TValue>* mListener; Entry* mOldest; Entry* mYoungest; diff --git a/init/builtins.cpp b/init/builtins.cpp index 3ffa2e82c..36ecbb8e9 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -788,13 +788,13 @@ static int do_loglevel(const std::vector<std::string>& args) { return 0; } -int do_load_persist_props(const std::vector<std::string>& args) { +static int do_load_persist_props(const std::vector<std::string>& args) { load_persist_props(); return 0; } -static int do_load_all_props(const std::vector<std::string>& args) { - load_all_props(); +static int do_load_system_props(const std::vector<std::string>& args) { + load_system_props(); return 0; } @@ -818,16 +818,26 @@ static int do_installkeys_ensure_dir_exists(const char* dir) { return 0; } +static bool is_file_crypto() { + std::string value = property_get("ro.crypto.type"); + return value == "file"; +} + static int do_installkey(const std::vector<std::string>& args) { - std::string prop_value = property_get("ro.crypto.type"); - if (prop_value != "file") { + if (!is_file_crypto()) { return 0; } - return e4crypt_create_device_key(args[1].c_str(), do_installkeys_ensure_dir_exists); } +static int do_setusercryptopolicies(const std::vector<std::string>& args) { + if (!is_file_crypto()) { + return 0; + } + return e4crypt_set_user_crypto_policies(args[1].c_str()); +} + BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const { constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max(); static const Map builtin_functions = { @@ -846,8 +856,8 @@ BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const { {"ifup", {1, 1, do_ifup}}, {"insmod", {1, kMax, do_insmod}}, {"installkey", {1, 1, do_installkey}}, - {"load_all_props", {0, 0, do_load_all_props}}, {"load_persist_props", {0, 0, do_load_persist_props}}, + {"load_system_props", {0, 0, do_load_system_props}}, {"loglevel", {1, 1, do_loglevel}}, {"mkdir", {1, 4, do_mkdir}}, {"mount_all", {1, 1, do_mount_all}}, @@ -860,6 +870,7 @@ BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const { {"rmdir", {1, 1, do_rmdir}}, {"setprop", {2, 2, do_setprop}}, {"setrlimit", {3, 3, do_setrlimit}}, + {"setusercryptopolicies", {1, 1, do_setusercryptopolicies}}, {"start", {1, 1, do_start}}, {"stop", {1, 1, do_stop}}, {"swapon_all", {1, 1, do_swapon_all}}, diff --git a/init/init.cpp b/init/init.cpp index a898b03e3..86aed9ac7 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -386,7 +386,7 @@ static void process_kernel_dt() { struct dirent *dp; while ((dp = readdir(dir.get())) != NULL) { - if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible")) { + if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) { continue; } @@ -546,7 +546,8 @@ int main(int argc, char** argv) { mkdir("/dev/pts", 0755); mkdir("/dev/socket", 0755); mount("devpts", "/dev/pts", "devpts", 0, NULL); - mount("proc", "/proc", "proc", 0, NULL); + #define MAKE_STR(x) __STRING(x) + mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)); mount("sysfs", "/sys", "sysfs", 0, NULL); } diff --git a/init/property_service.cpp b/init/property_service.cpp index ebf768eec..96b4a37b2 100644 --- a/init/property_service.cpp +++ b/init/property_service.cpp @@ -559,16 +559,10 @@ void load_recovery_id_prop() { close(fd); } -void load_all_props() { +void load_system_props() { load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL); load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL); load_properties_from_file(PROP_PATH_FACTORY, "ro.*"); - - load_override_properties(); - - /* Read persistent properties after all default values have been loaded. */ - load_persistent_properties(); - load_recovery_id_prop(); } diff --git a/init/property_service.h b/init/property_service.h index 1a48fb188..8b76d2b3f 100644 --- a/init/property_service.h +++ b/init/property_service.h @@ -30,7 +30,7 @@ struct property_audit_data { extern void property_init(void); extern void property_load_boot_defaults(void); extern void load_persist_props(void); -extern void load_all_props(void); +extern void load_system_props(void); extern void start_property_service(void); void get_property_workspace(int *fd, int *sz); std::string property_get(const char* name); diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c index 2a178aab1..f4454bb5d 100644 --- a/libcutils/fs_config.c +++ b/libcutils/fs_config.c @@ -88,6 +88,8 @@ static const struct fs_path_config android_dirs[] = { { 00771, AID_SHARED_RELRO, AID_SHARED_RELRO, 0, "data/misc/shared_relro" }, { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" }, { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" }, + { 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest" }, + { 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest64" }, { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" }, { 00755, AID_ROOT, AID_SYSTEM, 0, "mnt" }, { 00755, AID_ROOT, AID_ROOT, 0, "root" }, @@ -125,6 +127,8 @@ static const struct fs_path_config android_files[] = { { 00644, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/*" }, { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private/*" }, { 00644, AID_APP, AID_APP, 0, "data/data/*" }, + { 00640, AID_ROOT, AID_SHELL, 0, "data/nativetest/tests.txt" }, + { 00640, AID_ROOT, AID_SHELL, 0, "data/nativetest64/tests.txt" }, { 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest/*" }, { 00750, AID_ROOT, AID_SHELL, 0, "data/nativetest64/*" }, diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c index 8a8ece250..fcdb6c928 100644 --- a/liblog/fake_log_device.c +++ b/liblog/fake_log_device.c @@ -24,6 +24,7 @@ #include <ctype.h> #include <errno.h> #include <fcntl.h> +#include <signal.h> #include <stdlib.h> #include <string.h> @@ -97,18 +98,33 @@ typedef struct LogState { */ static pthread_mutex_t fakeLogDeviceLock = PTHREAD_MUTEX_INITIALIZER; -static void lock() +static void lock(sigset_t *sigflags) { + /* + * If we trigger a signal handler in the middle of locked activity and the + * signal handler logs a message, we could get into a deadlock state. + */ + sigset_t all; + + sigfillset(&all); + pthread_sigmask(SIG_BLOCK, &all, sigflags); pthread_mutex_lock(&fakeLogDeviceLock); } -static void unlock() +static void unlock(sigset_t *sigflags) { pthread_mutex_unlock(&fakeLogDeviceLock); + pthread_sigmask(SIG_UNBLOCK, sigflags, NULL); } + +#define DECLARE_SIGSET(name) sigset_t name + #else // !defined(_WIN32) -#define lock() ((void)0) -#define unlock() ((void)0) + +#define lock(sigflags) ((void)0) +#define unlock(sigflags) ((void)0) +#define DECLARE_SIGSET(name) + #endif // !defined(_WIN32) @@ -154,8 +170,9 @@ static LogState *fdToLogState(int fd) static void deleteFakeFd(int fd) { LogState *ls; + DECLARE_SIGSET(sigflags); - lock(); + lock(&sigflags); ls = fdToLogState(fd); if (ls != NULL) { @@ -164,7 +181,7 @@ static void deleteFakeFd(int fd) free(ls); } - unlock(); + unlock(&sigflags); } /* @@ -548,12 +565,13 @@ static void showLog(LogState *state, static ssize_t logWritev(int fd, const struct iovec* vector, int count) { LogState* state; + DECLARE_SIGSET(sigflags); /* Make sure that no-one frees the LogState while we're using it. * Also guarantees that only one thread is in showLog() at a given * time (if it matters). */ - lock(); + lock(&sigflags); state = fdToLogState(fd); if (state == NULL) { @@ -598,10 +616,10 @@ static ssize_t logWritev(int fd, const struct iovec* vector, int count) } bail: - unlock(); + unlock(&sigflags); return vector[0].iov_len + vector[1].iov_len + vector[2].iov_len; error: - unlock(); + unlock(&sigflags); return -1; } @@ -621,8 +639,9 @@ static int logOpen(const char* pathName, int flags __unused) { LogState *logState; int fd = -1; + DECLARE_SIGSET(sigflags); - lock(); + lock(&sigflags); logState = createLogState(); if (logState != NULL) { @@ -632,7 +651,7 @@ static int logOpen(const char* pathName, int flags __unused) errno = ENFILE; } - unlock(); + unlock(&sigflags); return fd; } diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c index 814d96d09..9d043ff48 100644 --- a/liblog/log_is_loggable.c +++ b/liblog/log_is_loggable.c @@ -16,12 +16,39 @@ #include <ctype.h> #include <pthread.h> +#include <signal.h> #include <stdlib.h> #include <string.h> #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ #include <sys/_system_properties.h> #include <android/log.h> +#include <log/log.h> + +static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER; + +static void lock(sigset_t *sigflags) +{ + /* + * If we trigger a signal handler in the middle of locked activity and the + * signal handler logs a message, we could get into a deadlock state. + */ + if (sigflags) { + sigset_t all; + + sigfillset(&all); + pthread_sigmask(SIG_BLOCK, &all, sigflags); + } + pthread_mutex_lock(&lock_loggable); +} + +static void unlock(sigset_t *sigflags) +{ + pthread_mutex_unlock(&lock_loggable); + if (sigflags) { + pthread_sigmask(SIG_UNBLOCK, sigflags, NULL); + } +} struct cache { const prop_info *pinfo; @@ -49,9 +76,7 @@ static void refresh_cache(struct cache *cache, const char *key) cache->c = buf[0]; } -static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; - -static int __android_log_level(const char *tag, int def) +static int __android_log_level(const char *tag, int flag) { /* sizeof() is used on this array below */ static const char log_namespace[] = "persist.log.tag."; @@ -83,10 +108,11 @@ static int __android_log_level(const char *tag, int def) { NULL, -1, 0 }, { NULL, -1, 0 } }; + sigset_t sigflags; strcpy(key, log_namespace); - pthread_mutex_lock(&lock); + lock((flag & ANDROID_LOGGABLE_FLAG_NOT_WITHIN_SIGNAL) ? NULL : &sigflags); current_global_serial = __system_property_area_serial(); @@ -156,7 +182,7 @@ static int __android_log_level(const char *tag, int def) global_serial = current_global_serial; - pthread_mutex_unlock(&lock); + unlock((flag & ANDROID_LOGGABLE_FLAG_NOT_WITHIN_SIGNAL) ? NULL : &sigflags); switch (toupper(c)) { case 'V': return ANDROID_LOG_VERBOSE; @@ -168,36 +194,46 @@ static int __android_log_level(const char *tag, int def) case 'A': return ANDROID_LOG_FATAL; case 'S': return -1; /* ANDROID_LOG_SUPPRESS */ } - return def; + return flag & ANDROID_LOGGABLE_FLAG_DEFAULT_MASK; } -int __android_log_is_loggable(int prio, const char *tag, int def) +int __android_log_is_loggable(int prio, const char *tag, int flag) { - int logLevel = __android_log_level(tag, def); + int logLevel = __android_log_level(tag, flag); return logLevel >= 0 && prio >= logLevel; } +/* + * Timestamp state generally remains constant, since a change is + * rare, we can accept a trylock failure gracefully. + */ +static pthread_mutex_t lock_timestamp = PTHREAD_MUTEX_INITIALIZER; + char android_log_timestamp() { static struct cache r_time_cache = { NULL, -1, 0 }; static struct cache p_time_cache = { NULL, -1, 0 }; - static uint32_t serial; - uint32_t current_serial; char retval; - pthread_mutex_lock(&lock); + if (pthread_mutex_trylock(&lock_timestamp)) { + /* We are willing to accept some race in this context */ + if (!(retval = p_time_cache.c)) { + retval = r_time_cache.c; + } + } else { + static uint32_t serial; + uint32_t current_serial = __system_property_area_serial(); + if (current_serial != serial) { + refresh_cache(&r_time_cache, "ro.logd.timestamp"); + refresh_cache(&p_time_cache, "persist.logd.timestamp"); + serial = current_serial; + } + if (!(retval = p_time_cache.c)) { + retval = r_time_cache.c; + } - current_serial = __system_property_area_serial(); - if (current_serial != serial) { - refresh_cache(&r_time_cache, "ro.logd.timestamp"); - refresh_cache(&p_time_cache, "persist.logd.timestamp"); - serial = current_serial; + pthread_mutex_unlock(&lock_timestamp); } - if (!(retval = p_time_cache.c)) { - retval = r_time_cache.c; - } - - pthread_mutex_unlock(&lock); return tolower(retval ?: 'r'); } diff --git a/liblog/logd_write.c b/liblog/logd_write.c index bdee28f50..a8ecc8d45 100644 --- a/liblog/logd_write.c +++ b/liblog/logd_write.c @@ -20,6 +20,7 @@ #include <fcntl.h> #if !defined(_WIN32) #include <pthread.h> +#include <signal.h> #endif #include <stdarg.h> #include <stdatomic.h> @@ -54,14 +55,43 @@ static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr); static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init; -#if !defined(_WIN32) -static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; -#endif #ifndef __unused #define __unused __attribute__((__unused__)) #endif +#if !defined(_WIN32) +static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; + +static void lock(sigset_t *sigflags) +{ + /* + * If we trigger a signal handler in the middle of locked activity and the + * signal handler logs a message, we could get into a deadlock state. + */ + sigset_t all; + + sigfillset(&all); + pthread_sigmask(SIG_BLOCK, &all, sigflags); + pthread_mutex_lock(&log_init_lock); +} + +static void unlock(sigset_t *sigflags) +{ + pthread_mutex_unlock(&log_init_lock); + pthread_sigmask(SIG_UNBLOCK, sigflags, NULL); +} + +#define DECLARE_SIGSET(name) sigset_t name + +#else /* !defined(_WIN32) */ + +#define lock(sigflags) ((void)0) +#define unlock(sigflags) ((void)0) +#define DECLARE_SIGSET(name) + +#endif /* !defined(_WIN32) */ + #if FAKE_LOG_DEVICE static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1, -1 }; #else @@ -191,7 +221,11 @@ static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr) * }; */ - clock_gettime(CLOCK_REALTIME, &ts); + if (android_log_timestamp() == 'm') { + clock_gettime(CLOCK_MONOTONIC, &ts); + } else { + clock_gettime(CLOCK_REALTIME, &ts); + } pmsg_header.magic = LOGGER_MAGIC; pmsg_header.len = sizeof(pmsg_header) + sizeof(header); @@ -271,17 +305,15 @@ static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr) */ ret = TEMP_FAILURE_RETRY(writev(logd_fd, newVec + 1, i - 1)); if (ret < 0) { + DECLARE_SIGSET(sigflags); + ret = -errno; if (ret == -ENOTCONN) { -#if !defined(_WIN32) - pthread_mutex_lock(&log_init_lock); -#endif + lock(&sigflags); close(logd_fd); logd_fd = -1; ret = __write_to_log_initialize(); -#if !defined(_WIN32) - pthread_mutex_unlock(&log_init_lock); -#endif + unlock(&sigflags); if (ret < 0) { return ret; @@ -325,18 +357,16 @@ const char *android_log_id_to_name(log_id_t log_id) static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) { -#if !defined(_WIN32) - pthread_mutex_lock(&log_init_lock); -#endif + DECLARE_SIGSET(sigflags); + + lock(&sigflags); if (write_to_log == __write_to_log_init) { int ret; ret = __write_to_log_initialize(); if (ret < 0) { -#if !defined(_WIN32) - pthread_mutex_unlock(&log_init_lock); -#endif + unlock(&sigflags); #if (FAKE_LOG_DEVICE == 0) if (pstore_fd >= 0) { __write_to_log_daemon(log_id, vec, nr); @@ -348,9 +378,7 @@ static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) write_to_log = __write_to_log_daemon; } -#if !defined(_WIN32) - pthread_mutex_unlock(&log_init_lock); -#endif + unlock(&sigflags); return write_to_log(log_id, vec, nr); } diff --git a/liblog/logprint.c b/liblog/logprint.c index 0ea2269ea..9f12c96db 100644 --- a/liblog/logprint.c +++ b/liblog/logprint.c @@ -203,7 +203,7 @@ AndroidLogFormat *android_log_format_new() p_ret->year_output = false; p_ret->zone_output = false; p_ret->epoch_output = false; - p_ret->monotonic_output = false; + p_ret->monotonic_output = android_log_timestamp() == 'm'; return p_ret; } @@ -1261,10 +1261,13 @@ char *android_log_formatLogLine ( now = entry->tv_sec; nsec = entry->tv_nsec; if (p_format->monotonic_output) { - struct timespec time; - convertMonotonic(&time, entry); - now = time.tv_sec; - nsec = time.tv_nsec; + // prevent convertMonotonic from being called if logd is monotonic + if (android_log_timestamp() != 'm') { + struct timespec time; + convertMonotonic(&time, entry); + now = time.tv_sec; + nsec = time.tv_nsec; + } } if (now < 0) { nsec = NS_PER_SEC - nsec; diff --git a/packagelistparser/Android.mk b/libpackagelistparser/Android.mk index 802a3cb25..c8be050e0 100644 --- a/packagelistparser/Android.mk +++ b/libpackagelistparser/Android.mk @@ -6,9 +6,9 @@ include $(CLEAR_VARS) LOCAL_MODULE := libpackagelistparser LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := packagelistparser.c -LOCAL_COPY_HEADERS_TO := packagelistparser -LOCAL_COPY_HEADERS := packagelistparser.h LOCAL_SHARED_LIBRARIES := liblog +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_CLANG := true LOCAL_SANITIZE := integer @@ -22,9 +22,9 @@ include $(CLEAR_VARS) LOCAL_MODULE := libpackagelistparser LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := packagelistparser.c -LOCAL_COPY_HEADERS_TO := packagelistparser -LOCAL_COPY_HEADERS := packagelistparser.h LOCAL_STATIC_LIBRARIES := liblog +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_CLANG := true LOCAL_SANITIZE := integer diff --git a/packagelistparser/packagelistparser.h b/libpackagelistparser/include/packagelistparser/packagelistparser.h index d602c26dc..d602c26dc 100644 --- a/packagelistparser/packagelistparser.h +++ b/libpackagelistparser/include/packagelistparser/packagelistparser.h diff --git a/packagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c index 3e2539c76..16052e299 100644 --- a/packagelistparser/packagelistparser.c +++ b/libpackagelistparser/packagelistparser.c @@ -29,7 +29,7 @@ #define LOG_TAG "packagelistparser" #include <utils/Log.h> -#include "packagelistparser.h" +#include <packagelistparser/packagelistparser.h> #define CLOGE(fmt, ...) \ do {\ diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk index 11e7988b3..9a937f8f4 100644 --- a/libpixelflinger/Android.mk +++ b/libpixelflinger/Android.mk @@ -14,8 +14,6 @@ PIXELFLINGER_SRC_FILES:= \ codeflinger/load_store.cpp \ codeflinger/blending.cpp \ codeflinger/texturing.cpp \ - codeflinger/tinyutils/SharedBuffer.cpp \ - codeflinger/tinyutils/VectorImpl.cpp \ fixed.cpp.arm \ picker.cpp.arm \ pixelflinger.cpp.arm \ @@ -52,6 +50,14 @@ PIXELFLINGER_SRC_FILES_mips := \ arch-mips/t32cb16blend.S \ endif + +PIXELFLINGER_SRC_FILES_mips64 := \ + codeflinger/MIPSAssembler.cpp \ + codeflinger/MIPS64Assembler.cpp \ + codeflinger/mips64_disassem.c \ + arch-mips64/col32cb16blend.S \ + arch-mips64/t32cb16blend.S \ + # # Shared library # @@ -61,10 +67,12 @@ LOCAL_SRC_FILES := $(PIXELFLINGER_SRC_FILES) LOCAL_SRC_FILES_arm := $(PIXELFLINGER_SRC_FILES_arm) LOCAL_SRC_FILES_arm64 := $(PIXELFLINGER_SRC_FILES_arm64) LOCAL_SRC_FILES_mips := $(PIXELFLINGER_SRC_FILES_mips) +LOCAL_SRC_FILES_mips64 := $(PIXELFLINGER_SRC_FILES_mips64) LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS) LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include -LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS) -LOCAL_SHARED_LIBRARIES := libcutils liblog +LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS) \ + external/safe-iop/include +LOCAL_SHARED_LIBRARIES := libcutils liblog libutils # Really this should go away entirely or at least not depend on # libhardware, but this at least gets us built. diff --git a/libpixelflinger/arch-mips/col32cb16blend.S b/libpixelflinger/arch-mips/col32cb16blend.S new file mode 100644 index 000000000..5d18e5533 --- /dev/null +++ b/libpixelflinger/arch-mips/col32cb16blend.S @@ -0,0 +1,134 @@ +/* +** Copyright 2015, 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. +*/ + + .macro pixel dreg src f sR sG sB shift + +#if __mips==32 && __mips_isa_rev>=2 + /* extract red */ + ext $t4,\src,\shift+11,5 + mul $t4,$t4,\f + + /* extract green */ + ext $t5,\src,\shift+5,6 + mul $t5,$t5,\f + + /* extract blue */ + ext $t6,\src,\shift,5 + mul $t6,$t6,\f +#else + /* extract red */ + srl $t4,\src,\shift+11 + andi $t4, 0x1f + mul $t4,$t4,\f + + /* extract green */ + srl $t5,\src,\shift+5 + andi $t5, 0x3f + mul $t5,$t5,\f + + /* extract blue */ + srl $t6,\src,\shift + andi $t6, 0x1f + mul $t6,$t6,\f +#endif + + srl $t4,$t4,8 + srl $t5,$t5,8 + srl $t6,$t6,8 + addu $t4,$t4,\sR + addu $t5,$t5,\sG + addu \dreg,$t6,\sB + sll $t4,$t4,11 + sll $t5,$t5,5 + or \dreg,\dreg,$t4 + or \dreg,\dreg,$t5 + andi \dreg, 0xffff + .endm + + .text + .align + + .global scanline_col32cb16blend_mips + .ent scanline_col32cb16blend_mips +scanline_col32cb16blend_mips: + + /* check if count is zero */ + srl $v0,$a1,24 /* sA */ + beqz $a2,done + li $t4, 0x100 + srl $v1,$v0,7 + addu $v0,$v1,$v0 + subu $v0,$t4,$v0 /* f */ +#if __mips==32 && __mips_isa_rev>=2 + ext $a3,$a1,3,5 /* sR */ + ext $t0,$a1,10,6 /* sG */ + ext $t1,$a1,19,5 /* sB */ +#else + srl $a3, $a1, 3 + andi $a3, 0x1f /* sR */ + srl $t0, $a1, 10 + andi $t0, 0x3f /* sG */ + srl $t1, $a1, 19 + andi $t1, 0x1f /* sB */ +#endif + + /* check if cnt is at least 4 */ + addiu $a2,$a2,-4 + bltz $a2,tail + +loop_4pixels: + lw $t7,0($a0) + lw $t8,4($a0) + addiu $a0,$a0,8 + addiu $a2,$a2,-4 + pixel $t2 $t7 $v0 $a3 $t0 $t1 0 + pixel $t3 $t7 $v0 $a3 $t0 $t1 16 +#if __mips==32 && __mips_isa_rev>=2 + ins $t2,$t3,16,16 +#else + sll $t3, 16 + or $t2, $t2, $t3 +#endif + pixel $t7 $t8 $v0 $a3 $t0 $t1 0 + pixel $t3 $t8 $v0 $a3 $t0 $t1 16 +#if __mips==32 && __mips_isa_rev>=2 + ins $t7,$t3,16,16 +#else + sll $t3, 16 + or $t7, $t7, $t3 +#endif + sw $t2,-8($a0) + sw $t7,-4($a0) + bgez $a2, loop_4pixels + +tail: + /* the pixel count underran, restore it now */ + addiu $a2,$a2,4 + + /* handle the last 0..3 pixels */ + beqz $a2,done + +loop_1pixel: + lhu $t7,0($a0) + addiu $a0,$a0,2 + addiu $a2,$a2,-1 + pixel $t2 $t7 $v0 $a3 $t0 $t1 0 + sh $t2, -2($a0) + bnez $a2,loop_1pixel + +done: + j $ra + .end scanline_col32cb16blend_mips diff --git a/libpixelflinger/arch-mips/t32cb16blend.S b/libpixelflinger/arch-mips/t32cb16blend.S index c911fbba2..236a2c96e 100644 --- a/libpixelflinger/arch-mips/t32cb16blend.S +++ b/libpixelflinger/arch-mips/t32cb16blend.S @@ -33,232 +33,241 @@ */ #if __mips==32 && __mips_isa_rev>=2 - .macro pixel dreg src fb shift - /* - * sA = s >> 24 - * f = 0x100 - (sA + (sA>>7)) - */ -DBG .set noat -DBG rdhwr $at,$2 -DBG .set at + .macro pixel dreg src fb shift + /* + * sA = s >> 24 + * f = 0x100 - (sA + (sA>>7)) + */ +DBG .set noat +DBG rdhwr $at,$2 +DBG .set at - srl $t7,\src,24 - srl $t6,$t7,7 - addu $t7,$t6 - li $t6,0x100 - subu $t7,$t6,$t7 + srl $t7,\src,24 + srl $t6,$t7,7 + addu $t7,$t6 + li $t6,0x100 + subu $t7,$t6,$t7 - /* red */ - ext $t8,\dreg,\shift+6+5,5 # dst[\shift:15..11] - mul $t6,$t8,$t7 - ext $t0,\dreg,\shift+5,6 # start green extraction dst[\shift:10..5] - ext $t8,\src,3,5 # src[7..3] - srl $t6,8 - addu $t8,$t6 - ins \fb,$t8,\shift+6+5,5 # dst[\shift:15..11] + /* red */ + ext $t8,\dreg,\shift+6+5,5 # dst[\shift:15..11] + mul $t6,$t8,$t7 + ext $t0,\dreg,\shift+5,6 # start green extraction dst[\shift:10..5] + ext $t8,\src,3,5 # src[7..3] + srl $t6,8 + addu $t8,$t6 +.if \shift!=0 + sll $t8,\shift+11 + or \fb,$t8 +.else + sll \fb,$t8,11 +.endif - /* green */ - mul $t8,$t0,$t7 - ext $t0,\dreg,\shift,5 # start blue extraction dst[\shift:4..0] - ext $t6,\src,2+8,6 # src[15..10] - srl $t8,8 - addu $t8,$t6 + /* green */ + mul $t8,$t0,$t7 + ext $t0,\dreg,\shift,5 # start blue extraction dst[\shift:4..0] + ext $t6,\src,2+8,6 # src[15..10] + srl $t8,8 + addu $t8,$t6 - /* blue */ - mul $t0,$t0,$t7 - ins \fb,$t8,\shift+5,6 # finish green insertion dst[\shift:10..5] - ext $t6,\src,(3+8+8),5 - srl $t8,$t0,8 - addu $t8,$t6 - ins \fb,$t8,\shift,5 + /* blue */ + mul $t0,$t0,$t7 + sll $t8, $t8, \shift+5 + or \fb, \fb, $t8 + ext $t6,\src,(3+8+8),5 + srl $t8,$t0,8 + addu $t8,$t6 + sll $t8, $t8, \shift + or \fb, \fb, $t8 -DBG .set noat -DBG rdhwr $t8,$2 -DBG subu $t8,$at -DBG sltu $at,$t8,$v0 -DBG movn $v0,$t8,$at -DBG sgtu $at,$t8,$v1 -DBG movn $v1,$t8,$at -DBG .set at - .endm +DBG .set noat +DBG rdhwr $t8,$2 +DBG subu $t8,$at +DBG sltu $at,$t8,$v0 +DBG movn $v0,$t8,$at +DBG sgtu $at,$t8,$v1 +DBG movn $v1,$t8,$at +DBG .set at + .endm #else - .macro pixel dreg src fb shift - /* - * sA = s >> 24 - * f = 0x100 - (sA + (sA>>7)) - */ -DBG .set push -DBG .set noat -DBG .set mips32r2 -DBG rdhwr $at,$2 -DBG .set pop + .macro pixel dreg src fb shift + /* + * sA = s >> 24 + * f = 0x100 - (sA + (sA>>7)) + */ +DBG .set push +DBG .set noat +DBG .set mips32r2 +DBG rdhwr $at,$2 +DBG .set pop - srl $t7,\src,24 - srl $t6,$t7,7 - addu $t7,$t6 - li $t6,0x100 - subu $t7,$t6,$t7 + srl $t7,\src,24 + srl $t6,$t7,7 + addu $t7,$t6 + li $t6,0x100 + subu $t7,$t6,$t7 - /* - * red - * dR = (d >> (6 + 5)) & 0x1f; - * dR = (f*dR)>>8 - * sR = (s >> ( 3)) & 0x1f; - * sR += dR - * fb |= sR << 11 - */ - srl $t8,\dreg,\shift+6+5 + /* + * red + * dR = (d >> (6 + 5)) & 0x1f; + * dR = (f*dR)>>8 + * sR = (s >> ( 3)) & 0x1f; + * sR += dR + * fb |= sR << 11 + */ + srl $t8,\dreg,\shift+6+5 .if \shift==0 - and $t8,0x1f + and $t8,0x1f .endif - mul $t8,$t8,$t7 - srl $t6,\src,3 - and $t6,0x1f - srl $t8,8 - addu $t8,$t6 + mul $t8,$t8,$t7 + srl $t6,\src,3 + and $t6,0x1f + srl $t8,8 + addu $t8,$t6 .if \shift!=0 - sll $t8,\shift+11 - or \fb,$t8 + sll $t8,\shift+11 + or \fb,$t8 .else - sll \fb,$t8,11 + sll \fb,$t8,11 .endif /* - * green - * dG = (d >> 5) & 0x3f - * dG = (f*dG) >> 8 - * sG = (s >> ( 8+2))&0x3F; - */ - srl $t8,\dreg,\shift+5 - and $t8,0x3f - mul $t8,$t8,$t7 - srl $t6,\src,8+2 - and $t6,0x3f - srl $t8,8 - addu $t8,$t6 - sll $t8,\shift + 5 - or \fb,$t8 + * green + * dG = (d >> 5) & 0x3f + * dG = (f*dG) >> 8 + * sG = (s >> ( 8+2))&0x3F; + */ + srl $t8,\dreg,\shift+5 + and $t8,0x3f + mul $t8,$t8,$t7 + srl $t6,\src,8+2 + and $t6,0x3f + srl $t8,8 + addu $t8,$t6 + sll $t8,\shift + 5 + or \fb,$t8 - /* blue */ + /* blue */ .if \shift!=0 - srl $t8,\dreg,\shift - and $t8,0x1f + srl $t8,\dreg,\shift + and $t8,0x1f .else - and $t8,\dreg,0x1f + and $t8,\dreg,0x1f .endif - mul $t8,$t8,$t7 - srl $t6,\src,(8+8+3) - and $t6,0x1f - srl $t8,8 - addu $t8,$t6 + mul $t8,$t8,$t7 + srl $t6,\src,(8+8+3) + and $t6,0x1f + srl $t8,8 + addu $t8,$t6 .if \shift!=0 - sll $t8,\shift + sll $t8,\shift .endif - or \fb,$t8 -DBG .set push -DBG .set noat -DBG .set mips32r2 -DBG rdhwr $t8,$2 -DBG subu $t8,$at -DBG sltu $at,$t8,$v0 -DBG movn $v0,$t8,$at -DBG sgtu $at,$t8,$v1 -DBG movn $v1,$t8,$at -DBG .set pop - .endm + or \fb,$t8 +DBG .set push +DBG .set noat +DBG .set mips32r2 +DBG rdhwr $t8,$2 +DBG subu $t8,$at +DBG sltu $at,$t8,$v0 +DBG movn $v0,$t8,$at +DBG sgtu $at,$t8,$v1 +DBG movn $v1,$t8,$at +DBG .set pop + .endm #endif - .text - .align + .text + .align - .global scanline_t32cb16blend_mips - .ent scanline_t32cb16blend_mips + .global scanline_t32cb16blend_mips + .ent scanline_t32cb16blend_mips scanline_t32cb16blend_mips: -DBG li $v0,0xffffffff -DBG li $v1,0 - /* Align the destination if necessary */ - and $t0,$a0,3 - beqz $t0,aligned +DBG li $v0,0xffffffff +DBG li $v1,0 + /* Align the destination if necessary */ + and $t0,$a0,3 + beqz $t0,aligned - /* as long as there is at least one pixel */ - beqz $a2,done + /* as long as there is at least one pixel */ + beqz $a2,done - lw $t4,($a1) - addu $a0,2 - addu $a1,4 - beqz $t4,1f - lhu $t3,-2($a0) - pixel $t3,$t4,$t1,0 - sh $t1,-2($a0) -1: subu $a2,1 + lw $t4,($a1) + addu $a0,2 + addu $a1,4 + beqz $t4,1f + lhu $t3,-2($a0) + pixel $t3,$t4,$t1,0 + sh $t1,-2($a0) +1: subu $a2,1 aligned: - /* Check to see if its worth unrolling the loop */ - subu $a2,4 - bltz $a2,tail + /* Check to see if its worth unrolling the loop */ + subu $a2,4 + bltz $a2,tail - /* Process 4 pixels at a time */ + /* Process 4 pixels at a time */ fourpixels: - /* 1st pair of pixels */ - lw $t4,0($a1) - lw $t5,4($a1) - addu $a0,8 - addu $a1,16 + /* 1st pair of pixels */ + lw $t4,0($a1) + lw $t5,4($a1) + addu $a0,8 + addu $a1,16 - /* both are zero, skip this pair */ - or $t3,$t4,$t5 - beqz $t3,1f + /* both are zero, skip this pair */ + or $t3,$t4,$t5 + beqz $t3,1f - /* load the destination */ - lw $t3,-8($a0) + /* load the destination */ + lw $t3,-8($a0) - pixel $t3,$t4,$t1,0 - pixel $t3,$t5,$t1,16 - sw $t1,-8($a0) + pixel $t3,$t4,$t1,0 + andi $t1, 0xFFFF + pixel $t3,$t5,$t1,16 + sw $t1,-8($a0) 1: - /* 2nd pair of pixels */ - lw $t4,-8($a1) - lw $t5,-4($a1) + /* 2nd pair of pixels */ + lw $t4,-8($a1) + lw $t5,-4($a1) - /* both are zero, skip this pair */ - or $t3,$t4,$t5 - beqz $t3,1f + /* both are zero, skip this pair */ + or $t3,$t4,$t5 + beqz $t3,1f - /* load the destination */ - lw $t3,-4($a0) + /* load the destination */ + lw $t3,-4($a0) - pixel $t3,$t4,$t1,0 - pixel $t3,$t5,$t1,16 - sw $t1,-4($a0) + pixel $t3,$t4,$t1,0 + andi $t1, 0xFFFF + pixel $t3,$t5,$t1,16 + sw $t1,-4($a0) -1: subu $a2,4 - bgtz $a2,fourpixels +1: subu $a2,4 + bgtz $a2,fourpixels tail: - /* the pixel count underran, restore it now */ - addu $a2,4 + /* the pixel count underran, restore it now */ + addu $a2,4 - /* handle the last 0..3 pixels */ - beqz $a2,done + /* handle the last 0..3 pixels */ + beqz $a2,done onepixel: - lw $t4,($a1) - addu $a0,2 - addu $a1,4 - beqz $t4,1f - lhu $t3,-2($a0) - pixel $t3,$t4,$t1,0 - sh $t1,-2($a0) -1: subu $a2,1 - bnez $a2,onepixel + lw $t4,($a1) + addu $a0,2 + addu $a1,4 + beqz $t4,1f + lhu $t3,-2($a0) + pixel $t3,$t4,$t1,0 + sh $t1,-2($a0) +1: subu $a2,1 + bnez $a2,onepixel done: -DBG .set push -DBG .set mips32r2 -DBG rdhwr $a0,$3 -DBG mul $v0,$a0 -DBG mul $v1,$a0 -DBG .set pop - j $ra - .end scanline_t32cb16blend_mips +DBG .set push +DBG .set mips32r2 +DBG rdhwr $a0,$3 +DBG mul $v0,$a0 +DBG mul $v1,$a0 +DBG .set pop + j $ra + .end scanline_t32cb16blend_mips diff --git a/libpixelflinger/arch-mips64/col32cb16blend.S b/libpixelflinger/arch-mips64/col32cb16blend.S new file mode 100644 index 000000000..fea44911d --- /dev/null +++ b/libpixelflinger/arch-mips64/col32cb16blend.S @@ -0,0 +1,108 @@ +/* +** Copyright 2015, 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. +*/ + + .macro pixel dreg src f sR sG sB shift + + /* extract red */ +.if \shift < 32 + dext $t0,\src,\shift+11,5 +.else + dextu $t0,\src,\shift+11,5 +.endif + mul $t0,$t0,\f + + /* extract green */ +.if \shift < 32 + dext $t1,\src,\shift+5,6 +.else + dextu $t1,\src,\shift+5,6 +.endif + mul $t1,$t1,\f + + /* extract blue */ +.if \shift < 32 + dext $t2,\src,\shift,5 +.else + dextu $t2,\src,\shift,5 +.endif + mul $t2,$t2,\f + + srl $t0,$t0,8 + srl $t1,$t1,8 + srl $t2,$t2,8 + addu $t0,$t0,\sR + addu $t1,$t1,\sG + addu \dreg,$t2,\sB + sll $t0,$t0,11 + sll $t1,$t1,5 + or \dreg,\dreg,$t0 + or \dreg,\dreg,$t1 + .endm + + .text + .align + + .global scanline_col32cb16blend_mips64 + .ent scanline_col32cb16blend_mips64 +scanline_col32cb16blend_mips64: + + /* check if count is zero */ + srl $v0,$a1,24 /* sA */ + beqz $a2,done + li $t0, 0x100 + srl $v1,$v0,7 + addu $v0,$v1,$v0 + subu $v0,$t0,$v0 /* f */ + ext $a3,$a1,3,5 /* sR */ + ext $a4,$a1,10,6 /* sG */ + ext $a5,$a1,19,5 /* sB */ + + /* check if cnt is at least 4 */ + addiu $a2,$a2,-4 + bltz $a2,tail + +loop_4pixels: + ld $t3,0($a0) + daddiu $a0,$a0,8 + addiu $a2,$a2,-4 + pixel $a6 $t3 $v0 $a3 $a4 $a5 0 + pixel $a7 $t3 $v0 $a3 $a4 $a5 16 + pixel $t8 $t3 $v0 $a3 $a4 $a5 32 + pixel $t9 $t3 $v0 $a3 $a4 $a5 48 + dins $a6,$a7,16,16 + dinsu $a6,$t8,32,16 + dinsu $a6,$t9,48,16 + sd $a6,-8($a0) + bgez $a2, loop_4pixels + +tail: + /* the pixel count underran, restore it now */ + addiu $a2,$a2,4 + + /* handle the last 0..3 pixels */ + beqz $a2,done + +loop_1pixel: + lhu $t3,0($a0) + daddiu $a0,$a0,2 + addiu $a2,$a2,-1 + pixel $a6 $t3 $v0 $a3 $a4 $a5 0 + sh $a6, -2($a0) + bnez $a2,loop_1pixel + +done: + j $ra + .end scanline_col32cb16blend_mips64 diff --git a/libpixelflinger/arch-mips64/t32cb16blend.S b/libpixelflinger/arch-mips64/t32cb16blend.S new file mode 100644 index 000000000..d2f4d4905 --- /dev/null +++ b/libpixelflinger/arch-mips64/t32cb16blend.S @@ -0,0 +1,172 @@ +/* +** Copyright 2015, 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. +*/ + +#ifdef DEBUG +#define DBG +#else +#define DBG # +#endif + +/* + * blend one of 2 16bpp RGB pixels held in dreg selected by shift + * with the 32bpp ABGR pixel held in src and store the result in fb + * + * Assumes that the dreg data is little endian and that + * the the second pixel (shift==16) will be merged into + * the fb result + * + * Uses $a4,$t2,$t3,$t8 + */ + + .macro pixel dreg src fb shift + /* + * sA = s >> 24 + * f = 0x100 - (sA + (sA>>7)) + */ + srl $t3,\src,24 + srl $t2,$t3,7 + addu $t3,$t2 + li $t2,0x100 + subu $t3,$t2,$t3 + + /* red */ + ext $t8,\dreg,\shift+6+5,5 # dst[\shift:15..11] + mul $t2,$t8,$t3 + ext $a4,\dreg,\shift+5,6 # start green extraction dst[\shift:10..5] + ext $t8,\src,3,5 # src[7..3] + srl $t2,8 + addu $t8,$t2 +.if \shift!=0 + sll $t8,\shift+11 # dst[\shift:15..11] + or \fb,$t8 +.else + sll \fb,$t8,11 +.endif + + /* green */ + mul $t8,$a4,$t3 + ext $a4,\dreg,\shift,5 # start blue extraction dst[\shift:4..0] + ext $t2,\src,2+8,6 # src[15..10] + srl $t8,8 + addu $t8,$t2 + + /* blue */ + mul $a4,$a4,$t3 + sll $t8, $t8, \shift+5 # finish green insertion dst[\shift:10..5] + or \fb, \fb, $t8 + ext $t2,\src,(3+8+8),5 + srl $t8,$a4,8 + addu $t8,$t2 + sll $t8, $t8, \shift + or \fb, \fb, $t8 + .endm + + .text + .align + + .global scanline_t32cb16blend_mips64 + .ent scanline_t32cb16blend_mips64 +scanline_t32cb16blend_mips64: + daddiu $sp, $sp, -40 +DBG li $v0,0xffffffff +DBG li $v1,0 + /* Align the destination if necessary */ + and $a4,$a0,3 + beqz $a4,aligned + + /* as long as there is at least one pixel */ + beqz $a2,done + + lw $t0,($a1) + daddu $a0,2 + daddu $a1,4 + beqz $t0,1f + lhu $a7,-2($a0) + pixel $a7,$t0,$a5,0 + sh $a5,-2($a0) +1: subu $a2,1 + +aligned: + /* Check to see if its worth unrolling the loop */ + subu $a2,4 + bltz $a2,tail + + /* Process 4 pixels at a time */ +fourpixels: + /* 1st pair of pixels */ + lw $t0,0($a1) + lw $t1,4($a1) + daddu $a0,8 + daddu $a1,16 + + /* both are zero, skip this pair */ + or $a7,$t0,$t1 + beqz $a7,1f + + /* load the destination */ + lw $a7,-8($a0) + + pixel $a7,$t0,$a5,0 + andi $a5, 0xFFFF + pixel $a7,$t1,$a5,16 + sw $a5,-8($a0) + +1: + /* 2nd pair of pixels */ + lw $t0,-8($a1) + lw $t1,-4($a1) + + /* both are zero, skip this pair */ + or $a7,$t0,$t1 + beqz $a7,1f + + /* load the destination */ + lw $a7,-4($a0) + + pixel $a7,$t0,$a5,0 + andi $a5, 0xFFFF + pixel $a7,$t1,$a5,16 + sw $a5,-4($a0) + +1: subu $a2,4 + bgtz $a2,fourpixels + +tail: + /* the pixel count underran, restore it now */ + addu $a2,4 + + /* handle the last 0..3 pixels */ + beqz $a2,done +onepixel: + lw $t0,($a1) + daddu $a0,2 + daddu $a1,4 + beqz $t0,1f + lhu $a7,-2($a0) + pixel $a7,$t0,$a5,0 + sh $a5,-2($a0) +1: subu $a2,1 + bnez $a2,onepixel +done: +DBG .set push +DBG .set mips32r2 +DBG rdhwr $a0,$3 +DBG mul $v0,$a0 +DBG mul $v1,$a0 +DBG .set pop + daddiu $sp, $sp, 40 + j $ra + .end scanline_t32cb16blend_mips64 diff --git a/libpixelflinger/codeflinger/ARMAssembler.h b/libpixelflinger/codeflinger/ARMAssembler.h index c03dd9a39..e0c7646cf 100644 --- a/libpixelflinger/codeflinger/ARMAssembler.h +++ b/libpixelflinger/codeflinger/ARMAssembler.h @@ -21,9 +21,9 @@ #include <stdint.h> #include <sys/types.h> -#include "tinyutils/Vector.h" -#include "tinyutils/KeyedVector.h" #include "tinyutils/smartpointer.h" +#include "utils/Vector.h" +#include "utils/KeyedVector.h" #include "ARMAssemblerInterface.h" #include "CodeCache.h" diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.h b/libpixelflinger/codeflinger/ARMAssemblerInterface.h index 40cbfcfb9..72935acd2 100644 --- a/libpixelflinger/codeflinger/ARMAssemblerInterface.h +++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.h @@ -63,7 +63,7 @@ public: }; enum { - CODEGEN_ARCH_ARM = 1, CODEGEN_ARCH_MIPS, CODEGEN_ARCH_ARM64 + CODEGEN_ARCH_ARM = 1, CODEGEN_ARCH_MIPS, CODEGEN_ARCH_ARM64, CODEGEN_ARCH_MIPS64 }; // ----------------------------------------------------------------------- @@ -115,7 +115,8 @@ public: // data processing... enum { opAND, opEOR, opSUB, opRSB, opADD, opADC, opSBC, opRSC, - opTST, opTEQ, opCMP, opCMN, opORR, opMOV, opBIC, opMVN + opTST, opTEQ, opCMP, opCMN, opORR, opMOV, opBIC, opMVN, + opADD64, opSUB64 }; virtual void diff --git a/libpixelflinger/codeflinger/Arm64Assembler.h b/libpixelflinger/codeflinger/Arm64Assembler.h index 847927009..c9be11614 100644 --- a/libpixelflinger/codeflinger/Arm64Assembler.h +++ b/libpixelflinger/codeflinger/Arm64Assembler.h @@ -32,9 +32,9 @@ #include <stdint.h> #include <sys/types.h> -#include "tinyutils/Vector.h" -#include "tinyutils/KeyedVector.h" #include "tinyutils/smartpointer.h" +#include "utils/Vector.h" +#include "utils/KeyedVector.h" #include "tinyutils/smartpointer.h" #include "codeflinger/ARMAssemblerInterface.h" diff --git a/libpixelflinger/codeflinger/CodeCache.h b/libpixelflinger/codeflinger/CodeCache.h index fa67dd072..0fb6fd5fe 100644 --- a/libpixelflinger/codeflinger/CodeCache.h +++ b/libpixelflinger/codeflinger/CodeCache.h @@ -23,7 +23,7 @@ #include <pthread.h> #include <sys/types.h> -#include "tinyutils/KeyedVector.h" +#include "utils/KeyedVector.h" #include "tinyutils/smartpointer.h" namespace android { diff --git a/libpixelflinger/codeflinger/GGLAssembler.cpp b/libpixelflinger/codeflinger/GGLAssembler.cpp index 325caba8a..346779f47 100644 --- a/libpixelflinger/codeflinger/GGLAssembler.cpp +++ b/libpixelflinger/codeflinger/GGLAssembler.cpp @@ -893,7 +893,8 @@ void GGLAssembler::build_and_immediate(int d, int s, uint32_t mask, int bits) return; } - if (getCodegenArch() == CODEGEN_ARCH_MIPS) { + if ((getCodegenArch() == CODEGEN_ARCH_MIPS) || + (getCodegenArch() == CODEGEN_ARCH_MIPS64)) { // MIPS can do 16-bit imm in 1 instr, 32-bit in 3 instr // the below ' while (mask)' code is buggy on mips // since mips returns true on isValidImmediate() @@ -1057,7 +1058,8 @@ RegisterAllocator::RegisterFile& RegisterAllocator::registerFile() RegisterAllocator::RegisterFile::RegisterFile(int codegen_arch) : mRegs(0), mTouched(0), mStatus(0), mArch(codegen_arch), mRegisterOffset(0) { - if (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) { + if ((mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) || + (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS64)) { mRegisterOffset = 2; // ARM has regs 0..15, MIPS offset to 2..17 } reserve(ARMAssemblerInterface::SP); @@ -1067,7 +1069,8 @@ RegisterAllocator::RegisterFile::RegisterFile(int codegen_arch) RegisterAllocator::RegisterFile::RegisterFile(const RegisterFile& rhs, int codegen_arch) : mRegs(rhs.mRegs), mTouched(rhs.mTouched), mArch(codegen_arch), mRegisterOffset(0) { - if (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) { + if ((mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) || + (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS64)) { mRegisterOffset = 2; // ARM has regs 0..15, MIPS offset to 2..17 } } diff --git a/libpixelflinger/codeflinger/MIPS64Assembler.cpp b/libpixelflinger/codeflinger/MIPS64Assembler.cpp new file mode 100644 index 000000000..a5305cca2 --- /dev/null +++ b/libpixelflinger/codeflinger/MIPS64Assembler.cpp @@ -0,0 +1,1452 @@ +/* libs/pixelflinger/codeflinger/MIPS64Assembler.cpp +** +** Copyright 2015, 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. +*/ + + +/* MIPS64 assembler and ARM->MIPS64 assembly translator +** +** The approach is utilize MIPSAssembler generator, using inherited MIPS64Assembler +** that overrides just the specific MIPS64r6 instructions. +** For now ArmToMips64Assembler is copied over from ArmToMipsAssembler class, +** changing some MIPS64r6 related stuff. +** +*/ + + +#define LOG_TAG "MIPS64Assembler" + +#include <stdio.h> +#include <stdlib.h> +#include <cutils/log.h> +#include <cutils/properties.h> + +#if defined(WITH_LIB_HARDWARE) +#include <hardware_legacy/qemu_tracing.h> +#endif + +#include <private/pixelflinger/ggl_context.h> + +#include "MIPS64Assembler.h" +#include "CodeCache.h" +#include "mips64_disassem.h" + + +#define NOT_IMPLEMENTED() LOG_ALWAYS_FATAL("Arm instruction %s not yet implemented\n", __func__) + + +// ---------------------------------------------------------------------------- + +namespace android { + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark ArmToMips64Assembler... +#endif + +ArmToMips64Assembler::ArmToMips64Assembler(const sp<Assembly>& assembly, + char *abuf, int linesz, int instr_count) + : ARMAssemblerInterface(), + mArmDisassemblyBuffer(abuf), + mArmLineLength(linesz), + mArmInstrCount(instr_count), + mInum(0), + mAssembly(assembly) +{ + mMips = new MIPS64Assembler(assembly, this); + mArmPC = (uint32_t **) malloc(ARM_MAX_INSTUCTIONS * sizeof(uint32_t *)); + init_conditional_labels(); +} + +ArmToMips64Assembler::ArmToMips64Assembler(void* assembly) + : ARMAssemblerInterface(), + mArmDisassemblyBuffer(NULL), + mInum(0), + mAssembly(NULL) +{ + mMips = new MIPS64Assembler(assembly, this); + mArmPC = (uint32_t **) malloc(ARM_MAX_INSTUCTIONS * sizeof(uint32_t *)); + init_conditional_labels(); +} + +ArmToMips64Assembler::~ArmToMips64Assembler() +{ + delete mMips; + free((void *) mArmPC); +} + +uint32_t* ArmToMips64Assembler::pc() const +{ + return mMips->pc(); +} + +uint32_t* ArmToMips64Assembler::base() const +{ + return mMips->base(); +} + +void ArmToMips64Assembler::reset() +{ + cond.labelnum = 0; + mInum = 0; + mMips->reset(); +} + +int ArmToMips64Assembler::getCodegenArch() +{ + return CODEGEN_ARCH_MIPS64; +} + +void ArmToMips64Assembler::comment(const char* string) +{ + mMips->comment(string); +} + +void ArmToMips64Assembler::label(const char* theLabel) +{ + mMips->label(theLabel); +} + +void ArmToMips64Assembler::disassemble(const char* name) +{ + mMips->disassemble(name); +} + +void ArmToMips64Assembler::init_conditional_labels() +{ + int i; + for (i=0;i<99; ++i) { + sprintf(cond.label[i], "cond_%d", i); + } +} + + + +#if 0 +#pragma mark - +#pragma mark Prolog/Epilog & Generate... +#endif + +void ArmToMips64Assembler::prolog() +{ + mArmPC[mInum++] = pc(); // save starting PC for this instr + + mMips->DADDIU(R_sp, R_sp, -(5 * 8)); + mMips->SD(R_s0, R_sp, 0); + mMips->SD(R_s1, R_sp, 8); + mMips->SD(R_s2, R_sp, 16); + mMips->SD(R_s3, R_sp, 24); + mMips->SD(R_s4, R_sp, 32); + mMips->MOVE(R_v0, R_a0); // move context * passed in a0 to v0 (arm r0) +} + +void ArmToMips64Assembler::epilog(uint32_t touched) +{ + mArmPC[mInum++] = pc(); // save starting PC for this instr + + mMips->LD(R_s0, R_sp, 0); + mMips->LD(R_s1, R_sp, 8); + mMips->LD(R_s2, R_sp, 16); + mMips->LD(R_s3, R_sp, 24); + mMips->LD(R_s4, R_sp, 32); + mMips->DADDIU(R_sp, R_sp, (5 * 8)); + mMips->JR(R_ra); + +} + +int ArmToMips64Assembler::generate(const char* name) +{ + return mMips->generate(name); +} + +void ArmToMips64Assembler::fix_branches() +{ + mMips->fix_branches(); +} + +uint32_t* ArmToMips64Assembler::pcForLabel(const char* label) +{ + return mMips->pcForLabel(label); +} + +void ArmToMips64Assembler::set_condition(int mode, int R1, int R2) { + if (mode == 2) { + cond.type = SBIT_COND; + } else { + cond.type = CMP_COND; + } + cond.r1 = R1; + cond.r2 = R2; +} + +//---------------------------------------------------------- + +#if 0 +#pragma mark - +#pragma mark Addressing modes & shifters... +#endif + + +// do not need this for MIPS, but it is in the Interface (virtual) +int ArmToMips64Assembler::buildImmediate( + uint32_t immediate, uint32_t& rot, uint32_t& imm) +{ + // for MIPS, any 32-bit immediate is OK + rot = 0; + imm = immediate; + return 0; +} + +// shifters... + +bool ArmToMips64Assembler::isValidImmediate(uint32_t immediate) +{ + // for MIPS, any 32-bit immediate is OK + return true; +} + +uint32_t ArmToMips64Assembler::imm(uint32_t immediate) +{ + amode.value = immediate; + return AMODE_IMM; +} + +uint32_t ArmToMips64Assembler::reg_imm(int Rm, int type, uint32_t shift) +{ + amode.reg = Rm; + amode.stype = type; + amode.value = shift; + return AMODE_REG_IMM; +} + +uint32_t ArmToMips64Assembler::reg_rrx(int Rm) +{ + // reg_rrx mode is not used in the GLLAssember code at this time + return AMODE_UNSUPPORTED; +} + +uint32_t ArmToMips64Assembler::reg_reg(int Rm, int type, int Rs) +{ + // reg_reg mode is not used in the GLLAssember code at this time + return AMODE_UNSUPPORTED; +} + + +// addressing modes... +// LDR(B)/STR(B)/PLD (immediate and Rm can be negative, which indicate U=0) +uint32_t ArmToMips64Assembler::immed12_pre(int32_t immed12, int W) +{ + LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800, + "LDR(B)/STR(B)/PLD immediate too big (%08x)", + immed12); + amode.value = immed12; + amode.writeback = W; + return AMODE_IMM_12_PRE; +} + +uint32_t ArmToMips64Assembler::immed12_post(int32_t immed12) +{ + LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800, + "LDR(B)/STR(B)/PLD immediate too big (%08x)", + immed12); + + amode.value = immed12; + return AMODE_IMM_12_POST; +} + +uint32_t ArmToMips64Assembler::reg_scale_pre(int Rm, int type, + uint32_t shift, int W) +{ + LOG_ALWAYS_FATAL_IF(W | type | shift, "reg_scale_pre adv modes not yet implemented"); + + amode.reg = Rm; + // amode.stype = type; // more advanced modes not used in GGLAssembler yet + // amode.value = shift; + // amode.writeback = W; + return AMODE_REG_SCALE_PRE; +} + +uint32_t ArmToMips64Assembler::reg_scale_post(int Rm, int type, uint32_t shift) +{ + LOG_ALWAYS_FATAL("adr mode reg_scale_post not yet implemented\n"); + return AMODE_UNSUPPORTED; +} + +// LDRH/LDRSB/LDRSH/STRH (immediate and Rm can be negative, which indicate U=0) +uint32_t ArmToMips64Assembler::immed8_pre(int32_t immed8, int W) +{ + LOG_ALWAYS_FATAL("adr mode immed8_pre not yet implemented\n"); + + LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100, + "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)", + immed8); + return AMODE_IMM_8_PRE; +} + +uint32_t ArmToMips64Assembler::immed8_post(int32_t immed8) +{ + LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100, + "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)", + immed8); + amode.value = immed8; + return AMODE_IMM_8_POST; +} + +uint32_t ArmToMips64Assembler::reg_pre(int Rm, int W) +{ + LOG_ALWAYS_FATAL_IF(W, "reg_pre writeback not yet implemented"); + amode.reg = Rm; + return AMODE_REG_PRE; +} + +uint32_t ArmToMips64Assembler::reg_post(int Rm) +{ + LOG_ALWAYS_FATAL("adr mode reg_post not yet implemented\n"); + return AMODE_UNSUPPORTED; +} + + + +// ---------------------------------------------------------------------------- + +#if 0 +#pragma mark - +#pragma mark Data Processing... +#endif + + +static const char * const dpOpNames[] = { + "AND", "EOR", "SUB", "RSB", "ADD", "ADC", "SBC", "RSC", + "TST", "TEQ", "CMP", "CMN", "ORR", "MOV", "BIC", "MVN" +}; + +// check if the operand registers from a previous CMP or S-bit instruction +// would be overwritten by this instruction. If so, move the value to a +// safe register. +// Note that we cannot tell at _this_ instruction time if a future (conditional) +// instruction will _also_ use this value (a defect of the simple 1-pass, one- +// instruction-at-a-time translation). Therefore we must be conservative and +// save the value before it is overwritten. This costs an extra MOVE instr. + +void ArmToMips64Assembler::protectConditionalOperands(int Rd) +{ + if (Rd == cond.r1) { + mMips->MOVE(R_cmp, cond.r1); + cond.r1 = R_cmp; + } + if (cond.type == CMP_COND && Rd == cond.r2) { + mMips->MOVE(R_cmp2, cond.r2); + cond.r2 = R_cmp2; + } +} + + +// interprets the addressing mode, and generates the common code +// used by the majority of data-processing ops. Many MIPS instructions +// have a register-based form and a different immediate form. See +// opAND below for an example. (this could be inlined) +// +// this works with the imm(), reg_imm() methods above, which are directly +// called by the GLLAssembler. +// note: _signed parameter defaults to false (un-signed) +// note: tmpReg parameter defaults to 1, MIPS register AT +int ArmToMips64Assembler::dataProcAdrModes(int op, int& source, bool _signed, int tmpReg) +{ + if (op < AMODE_REG) { + source = op; + return SRC_REG; + } else if (op == AMODE_IMM) { + if ((!_signed && amode.value > 0xffff) + || (_signed && ((int)amode.value < -32768 || (int)amode.value > 32767) )) { + mMips->LUI(tmpReg, (amode.value >> 16)); + if (amode.value & 0x0000ffff) { + mMips->ORI(tmpReg, tmpReg, (amode.value & 0x0000ffff)); + } + source = tmpReg; + return SRC_REG; + } else { + source = amode.value; + return SRC_IMM; + } + } else if (op == AMODE_REG_IMM) { + switch (amode.stype) { + case LSL: mMips->SLL(tmpReg, amode.reg, amode.value); break; + case LSR: mMips->SRL(tmpReg, amode.reg, amode.value); break; + case ASR: mMips->SRA(tmpReg, amode.reg, amode.value); break; + case ROR: mMips->ROTR(tmpReg, amode.reg, amode.value); break; + } + source = tmpReg; + return SRC_REG; + } else { // adr mode RRX is not used in GGL Assembler at this time + // we are screwed, this should be exception, assert-fail or something + LOG_ALWAYS_FATAL("adr mode reg_rrx not yet implemented\n"); + return SRC_ERROR; + } +} + + +void ArmToMips64Assembler::dataProcessing(int opcode, int cc, + int s, int Rd, int Rn, uint32_t Op2) +{ + int src; // src is modified by dataProcAdrModes() - passed as int& + + if (cc != AL) { + protectConditionalOperands(Rd); + // the branch tests register(s) set by prev CMP or instr with 'S' bit set + // inverse the condition to jump past this conditional instruction + ArmToMips64Assembler::B(cc^1, cond.label[++cond.labelnum]); + } else { + mArmPC[mInum++] = pc(); // save starting PC for this instr + } + + switch (opcode) { + case opAND: + if (dataProcAdrModes(Op2, src) == SRC_REG) { + mMips->AND(Rd, Rn, src); + } else { // adr mode was SRC_IMM + mMips->ANDI(Rd, Rn, src); + } + break; + + case opADD: + // set "signed" to true for adr modes + if (dataProcAdrModes(Op2, src, true) == SRC_REG) { + mMips->ADDU(Rd, Rn, src); + } else { // adr mode was SRC_IMM + mMips->ADDIU(Rd, Rn, src); + } + break; + + case opSUB: + // set "signed" to true for adr modes + if (dataProcAdrModes(Op2, src, true) == SRC_REG) { + mMips->SUBU(Rd, Rn, src); + } else { // adr mode was SRC_IMM + mMips->SUBIU(Rd, Rn, src); + } + break; + + case opADD64: + // set "signed" to true for adr modes + if (dataProcAdrModes(Op2, src, true) == SRC_REG) { + mMips->DADDU(Rd, Rn, src); + } else { // adr mode was SRC_IMM + mMips->DADDIU(Rd, Rn, src); + } + break; + + case opSUB64: + // set "signed" to true for adr modes + if (dataProcAdrModes(Op2, src, true) == SRC_REG) { + mMips->DSUBU(Rd, Rn, src); + } else { // adr mode was SRC_IMM + mMips->DSUBIU(Rd, Rn, src); + } + break; + + case opEOR: + if (dataProcAdrModes(Op2, src) == SRC_REG) { + mMips->XOR(Rd, Rn, src); + } else { // adr mode was SRC_IMM + mMips->XORI(Rd, Rn, src); + } + break; + + case opORR: + if (dataProcAdrModes(Op2, src) == SRC_REG) { + mMips->OR(Rd, Rn, src); + } else { // adr mode was SRC_IMM + mMips->ORI(Rd, Rn, src); + } + break; + + case opBIC: + if (dataProcAdrModes(Op2, src) == SRC_IMM) { + // if we are 16-bit imnmediate, load to AT reg + mMips->ORI(R_at, 0, src); + src = R_at; + } + mMips->NOT(R_at, src); + mMips->AND(Rd, Rn, R_at); + break; + + case opRSB: + if (dataProcAdrModes(Op2, src) == SRC_IMM) { + // if we are 16-bit imnmediate, load to AT reg + mMips->ORI(R_at, 0, src); + src = R_at; + } + mMips->SUBU(Rd, src, Rn); // subu with the parameters reversed + break; + + case opMOV: + if (Op2 < AMODE_REG) { // op2 is reg # in this case + mMips->MOVE(Rd, Op2); + } else if (Op2 == AMODE_IMM) { + if (amode.value > 0xffff) { + mMips->LUI(Rd, (amode.value >> 16)); + if (amode.value & 0x0000ffff) { + mMips->ORI(Rd, Rd, (amode.value & 0x0000ffff)); + } + } else { + mMips->ORI(Rd, 0, amode.value); + } + } else if (Op2 == AMODE_REG_IMM) { + switch (amode.stype) { + case LSL: mMips->SLL(Rd, amode.reg, amode.value); break; + case LSR: mMips->SRL(Rd, amode.reg, amode.value); break; + case ASR: mMips->SRA(Rd, amode.reg, amode.value); break; + case ROR: mMips->ROTR(Rd, amode.reg, amode.value); break; + } + } + else { + // adr mode RRX is not used in GGL Assembler at this time + mMips->UNIMPL(); + } + break; + + case opMVN: // this is a 1's complement: NOT + if (Op2 < AMODE_REG) { // op2 is reg # in this case + mMips->NOR(Rd, Op2, 0); // NOT is NOR with 0 + break; + } else if (Op2 == AMODE_IMM) { + if (amode.value > 0xffff) { + mMips->LUI(Rd, (amode.value >> 16)); + if (amode.value & 0x0000ffff) { + mMips->ORI(Rd, Rd, (amode.value & 0x0000ffff)); + } + } else { + mMips->ORI(Rd, 0, amode.value); + } + } else if (Op2 == AMODE_REG_IMM) { + switch (amode.stype) { + case LSL: mMips->SLL(Rd, amode.reg, amode.value); break; + case LSR: mMips->SRL(Rd, amode.reg, amode.value); break; + case ASR: mMips->SRA(Rd, amode.reg, amode.value); break; + case ROR: mMips->ROTR(Rd, amode.reg, amode.value); break; + } + } + else { + // adr mode RRX is not used in GGL Assembler at this time + mMips->UNIMPL(); + } + mMips->NOR(Rd, Rd, 0); // NOT is NOR with 0 + break; + + case opCMP: + // Either operand of a CMP instr could get overwritten by a subsequent + // conditional instruction, which is ok, _UNLESS_ there is a _second_ + // conditional instruction. Under MIPS, this requires doing the comparison + // again (SLT), and the original operands must be available. (and this + // pattern of multiple conditional instructions from same CMP _is_ used + // in GGL-Assembler) + // + // For now, if a conditional instr overwrites the operands, we will + // move them to dedicated temp regs. This is ugly, and inefficient, + // and should be optimized. + // + // WARNING: making an _Assumption_ that CMP operand regs will NOT be + // trashed by intervening NON-conditional instructions. In the general + // case this is legal, but it is NOT currently done in GGL-Assembler. + + cond.type = CMP_COND; + cond.r1 = Rn; + if (dataProcAdrModes(Op2, src, false, R_cmp2) == SRC_REG) { + cond.r2 = src; + } else { // adr mode was SRC_IMM + mMips->ORI(R_cmp2, R_zero, src); + cond.r2 = R_cmp2; + } + + break; + + + case opTST: + case opTEQ: + case opCMN: + case opADC: + case opSBC: + case opRSC: + mMips->UNIMPL(); // currently unused in GGL Assembler code + break; + } + + if (cc != AL) { + mMips->label(cond.label[cond.labelnum]); + } + if (s && opcode != opCMP) { + cond.type = SBIT_COND; + cond.r1 = Rd; + } +} + + + +#if 0 +#pragma mark - +#pragma mark Multiply... +#endif + +// multiply, accumulate +void ArmToMips64Assembler::MLA(int cc, int s, + int Rd, int Rm, int Rs, int Rn) { + + //ALOGW("MLA"); + mArmPC[mInum++] = pc(); // save starting PC for this instr + + mMips->MUL(R_at, Rm, Rs); + mMips->ADDU(Rd, R_at, Rn); + if (s) { + cond.type = SBIT_COND; + cond.r1 = Rd; + } +} + +void ArmToMips64Assembler::MUL(int cc, int s, + int Rd, int Rm, int Rs) { + mArmPC[mInum++] = pc(); + mMips->MUL(Rd, Rm, Rs); + if (s) { + cond.type = SBIT_COND; + cond.r1 = Rd; + } +} + +void ArmToMips64Assembler::UMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + mArmPC[mInum++] = pc(); + mMips->MUH(RdHi, Rm, Rs); + mMips->MUL(RdLo, Rm, Rs); + + if (s) { + cond.type = SBIT_COND; + cond.r1 = RdHi; // BUG... + LOG_ALWAYS_FATAL("Condition on UMULL must be on 64-bit result\n"); + } +} + +void ArmToMips64Assembler::UMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi, + "UMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs); + // *mPC++ = (cc<<28) | (1<<23) | (1<<21) | (s<<20) | + // (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); + if (s) { + cond.type = SBIT_COND; + cond.r1 = RdHi; // BUG... + LOG_ALWAYS_FATAL("Condition on UMULL must be on 64-bit result\n"); + } +} + +void ArmToMips64Assembler::SMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi, + "SMULL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs); + // *mPC++ = (cc<<28) | (1<<23) | (1<<22) | (s<<20) | + // (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); + if (s) { + cond.type = SBIT_COND; + cond.r1 = RdHi; // BUG... + LOG_ALWAYS_FATAL("Condition on SMULL must be on 64-bit result\n"); + } +} +void ArmToMips64Assembler::SMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs) { + LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi, + "SMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs); + // *mPC++ = (cc<<28) | (1<<23) | (1<<22) | (1<<21) | (s<<20) | + // (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); + if (s) { + cond.type = SBIT_COND; + cond.r1 = RdHi; // BUG... + LOG_ALWAYS_FATAL("Condition on SMUAL must be on 64-bit result\n"); + } +} + + + +#if 0 +#pragma mark - +#pragma mark Branches... +#endif + +// branches... + +void ArmToMips64Assembler::B(int cc, const char* label) +{ + mArmPC[mInum++] = pc(); + if (cond.type == SBIT_COND) { cond.r2 = R_zero; } + + switch(cc) { + case EQ: mMips->BEQ(cond.r1, cond.r2, label); break; + case NE: mMips->BNE(cond.r1, cond.r2, label); break; + case HS: mMips->BGEU(cond.r1, cond.r2, label); break; + case LO: mMips->BLTU(cond.r1, cond.r2, label); break; + case MI: mMips->BLT(cond.r1, cond.r2, label); break; + case PL: mMips->BGE(cond.r1, cond.r2, label); break; + + case HI: mMips->BGTU(cond.r1, cond.r2, label); break; + case LS: mMips->BLEU(cond.r1, cond.r2, label); break; + case GE: mMips->BGE(cond.r1, cond.r2, label); break; + case LT: mMips->BLT(cond.r1, cond.r2, label); break; + case GT: mMips->BGT(cond.r1, cond.r2, label); break; + case LE: mMips->BLE(cond.r1, cond.r2, label); break; + case AL: mMips->B(label); break; + case NV: /* B Never - no instruction */ break; + + case VS: + case VC: + default: + LOG_ALWAYS_FATAL("Unsupported cc: %02x\n", cc); + break; + } +} + +void ArmToMips64Assembler::BL(int cc, const char* label) +{ + LOG_ALWAYS_FATAL("branch-and-link not supported yet\n"); + mArmPC[mInum++] = pc(); +} + +// no use for Branches with integer PC, but they're in the Interface class .... +void ArmToMips64Assembler::B(int cc, uint32_t* to_pc) +{ + LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n"); + mArmPC[mInum++] = pc(); +} + +void ArmToMips64Assembler::BL(int cc, uint32_t* to_pc) +{ + LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n"); + mArmPC[mInum++] = pc(); +} + +void ArmToMips64Assembler::BX(int cc, int Rn) +{ + LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n"); + mArmPC[mInum++] = pc(); +} + + + +#if 0 +#pragma mark - +#pragma mark Data Transfer... +#endif + +// data transfer... +void ArmToMips64Assembler::LDR(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed12_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + amode.writeback = 0; + // fall thru to next case .... + case AMODE_IMM_12_PRE: + if (Rn == ARMAssemblerInterface::SP) { + Rn = R_sp; // convert LDR via Arm SP to LW via Mips SP + } + mMips->LW(Rd, Rn, amode.value); + if (amode.writeback) { // OPTIONAL writeback on pre-index mode + mMips->DADDIU(Rn, Rn, amode.value); + } + break; + case AMODE_IMM_12_POST: + if (Rn == ARMAssemblerInterface::SP) { + Rn = R_sp; // convert STR thru Arm SP to STR thru Mips SP + } + mMips->LW(Rd, Rn, 0); + mMips->DADDIU(Rn, Rn, amode.value); + break; + case AMODE_REG_SCALE_PRE: + // we only support simple base + index, no advanced modes for this one yet + mMips->DADDU(R_at, Rn, amode.reg); + mMips->LW(Rd, R_at, 0); + break; + } +} + +void ArmToMips64Assembler::LDRB(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed12_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + amode.writeback = 0; + // fall thru to next case .... + case AMODE_IMM_12_PRE: + mMips->LBU(Rd, Rn, amode.value); + if (amode.writeback) { // OPTIONAL writeback on pre-index mode + mMips->DADDIU(Rn, Rn, amode.value); + } + break; + case AMODE_IMM_12_POST: + mMips->LBU(Rd, Rn, 0); + mMips->DADDIU(Rn, Rn, amode.value); + break; + case AMODE_REG_SCALE_PRE: + // we only support simple base + index, no advanced modes for this one yet + mMips->DADDU(R_at, Rn, amode.reg); + mMips->LBU(Rd, R_at, 0); + break; + } + +} + +void ArmToMips64Assembler::STR(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed12_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + amode.writeback = 0; + // fall thru to next case .... + case AMODE_IMM_12_PRE: + if (Rn == ARMAssemblerInterface::SP) { + Rn = R_sp; // convert STR thru Arm SP to SW thru Mips SP + } + if (amode.writeback) { // OPTIONAL writeback on pre-index mode + // If we will writeback, then update the index reg, then store. + // This correctly handles stack-push case. + mMips->DADDIU(Rn, Rn, amode.value); + mMips->SW(Rd, Rn, 0); + } else { + // No writeback so store offset by value + mMips->SW(Rd, Rn, amode.value); + } + break; + case AMODE_IMM_12_POST: + mMips->SW(Rd, Rn, 0); + mMips->DADDIU(Rn, Rn, amode.value); // post index always writes back + break; + case AMODE_REG_SCALE_PRE: + // we only support simple base + index, no advanced modes for this one yet + mMips->DADDU(R_at, Rn, amode.reg); + mMips->SW(Rd, R_at, 0); + break; + } +} + +void ArmToMips64Assembler::STRB(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed12_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + amode.writeback = 0; + // fall thru to next case .... + case AMODE_IMM_12_PRE: + mMips->SB(Rd, Rn, amode.value); + if (amode.writeback) { // OPTIONAL writeback on pre-index mode + mMips->DADDIU(Rn, Rn, amode.value); + } + break; + case AMODE_IMM_12_POST: + mMips->SB(Rd, Rn, 0); + mMips->DADDIU(Rn, Rn, amode.value); + break; + case AMODE_REG_SCALE_PRE: + // we only support simple base + index, no advanced modes for this one yet + mMips->DADDU(R_at, Rn, amode.reg); + mMips->SB(Rd, R_at, 0); + break; + } +} + +void ArmToMips64Assembler::LDRH(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed8_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + // fall thru to next case .... + case AMODE_IMM_8_PRE: // no support yet for writeback + mMips->LHU(Rd, Rn, amode.value); + break; + case AMODE_IMM_8_POST: + mMips->LHU(Rd, Rn, 0); + mMips->DADDIU(Rn, Rn, amode.value); + break; + case AMODE_REG_PRE: + // we only support simple base +/- index + if (amode.reg >= 0) { + mMips->DADDU(R_at, Rn, amode.reg); + } else { + mMips->DSUBU(R_at, Rn, abs(amode.reg)); + } + mMips->LHU(Rd, R_at, 0); + break; + } +} + +void ArmToMips64Assembler::LDRSB(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMips64Assembler::LDRSH(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMips64Assembler::STRH(int cc, int Rd, int Rn, uint32_t offset) +{ + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed8_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + // fall thru to next case .... + case AMODE_IMM_8_PRE: // no support yet for writeback + mMips->SH(Rd, Rn, amode.value); + break; + case AMODE_IMM_8_POST: + mMips->SH(Rd, Rn, 0); + mMips->DADDIU(Rn, Rn, amode.value); + break; + case AMODE_REG_PRE: + // we only support simple base +/- index + if (amode.reg >= 0) { + mMips->DADDU(R_at, Rn, amode.reg); + } else { + mMips->DSUBU(R_at, Rn, abs(amode.reg)); + } + mMips->SH(Rd, R_at, 0); + break; + } +} + + + +#if 0 +#pragma mark - +#pragma mark Block Data Transfer... +#endif + +// block data transfer... +void ArmToMips64Assembler::LDM(int cc, int dir, + int Rn, int W, uint32_t reg_list) +{ // ED FD EA FA IB IA DB DA + // const uint8_t P[8] = { 1, 0, 1, 0, 1, 0, 1, 0 }; + // const uint8_t U[8] = { 1, 1, 0, 0, 1, 1, 0, 0 }; + // *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) | + // (uint32_t(U[dir])<<23) | (1<<20) | (W<<21) | (Rn<<16) | reg_list; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMips64Assembler::STM(int cc, int dir, + int Rn, int W, uint32_t reg_list) +{ // FA EA FD ED IB IA DB DA + // const uint8_t P[8] = { 0, 1, 0, 1, 1, 0, 1, 0 }; + // const uint8_t U[8] = { 0, 0, 1, 1, 1, 1, 0, 0 }; + // *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) | + // (uint32_t(U[dir])<<23) | (0<<20) | (W<<21) | (Rn<<16) | reg_list; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + + + +#if 0 +#pragma mark - +#pragma mark Special... +#endif + +// special... +void ArmToMips64Assembler::SWP(int cc, int Rn, int Rd, int Rm) { + // *mPC++ = (cc<<28) | (2<<23) | (Rn<<16) | (Rd << 12) | 0x90 | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMips64Assembler::SWPB(int cc, int Rn, int Rd, int Rm) { + // *mPC++ = (cc<<28) | (2<<23) | (1<<22) | (Rn<<16) | (Rd << 12) | 0x90 | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMips64Assembler::SWI(int cc, uint32_t comment) { + // *mPC++ = (cc<<28) | (0xF<<24) | comment; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + + +#if 0 +#pragma mark - +#pragma mark DSP instructions... +#endif + +// DSP instructions... +void ArmToMips64Assembler::PLD(int Rn, uint32_t offset) { + LOG_ALWAYS_FATAL_IF(!((offset&(1<<24)) && !(offset&(1<<21))), + "PLD only P=1, W=0"); + // *mPC++ = 0xF550F000 | (Rn<<16) | offset; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMips64Assembler::CLZ(int cc, int Rd, int Rm) +{ + mArmPC[mInum++] = pc(); + mMips->CLZ(Rd, Rm); +} + +void ArmToMips64Assembler::QADD(int cc, int Rd, int Rm, int Rn) +{ + // *mPC++ = (cc<<28) | 0x1000050 | (Rn<<16) | (Rd<<12) | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMips64Assembler::QDADD(int cc, int Rd, int Rm, int Rn) +{ + // *mPC++ = (cc<<28) | 0x1400050 | (Rn<<16) | (Rd<<12) | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMips64Assembler::QSUB(int cc, int Rd, int Rm, int Rn) +{ + // *mPC++ = (cc<<28) | 0x1200050 | (Rn<<16) | (Rd<<12) | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMips64Assembler::QDSUB(int cc, int Rd, int Rm, int Rn) +{ + // *mPC++ = (cc<<28) | 0x1600050 | (Rn<<16) | (Rd<<12) | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +// 16 x 16 signed multiply (like SMLAxx without the accumulate) +void ArmToMips64Assembler::SMUL(int cc, int xy, + int Rd, int Rm, int Rs) +{ + mArmPC[mInum++] = pc(); + + // the 16 bits may be in the top or bottom half of 32-bit source reg, + // as defined by the codes BB, BT, TB, TT (compressed param xy) + // where x corresponds to Rm and y to Rs + + // select half-reg for Rm + if (xy & xyTB) { + // use top 16-bits + mMips->SRA(R_at, Rm, 16); + } else { + // use bottom 16, but sign-extend to 32 + mMips->SEH(R_at, Rm); + } + // select half-reg for Rs + if (xy & xyBT) { + // use top 16-bits + mMips->SRA(R_at2, Rs, 16); + } else { + // use bottom 16, but sign-extend to 32 + mMips->SEH(R_at2, Rs); + } + mMips->MUL(Rd, R_at, R_at2); +} + +// signed 32b x 16b multiple, save top 32-bits of 48-bit result +void ArmToMips64Assembler::SMULW(int cc, int y, + int Rd, int Rm, int Rs) +{ + mArmPC[mInum++] = pc(); + + // the selector yT or yB refers to reg Rs + if (y & yT) { + // zero the bottom 16-bits, with 2 shifts, it can affect result + mMips->SRL(R_at, Rs, 16); + mMips->SLL(R_at, R_at, 16); + + } else { + // move low 16-bit half, to high half + mMips->SLL(R_at, Rs, 16); + } + mMips->MUH(Rd, Rm, R_at); +} + +// 16 x 16 signed multiply, accumulate: Rd = Rm{16} * Rs{16} + Rn +void ArmToMips64Assembler::SMLA(int cc, int xy, + int Rd, int Rm, int Rs, int Rn) +{ + mArmPC[mInum++] = pc(); + + // the 16 bits may be in the top or bottom half of 32-bit source reg, + // as defined by the codes BB, BT, TB, TT (compressed param xy) + // where x corresponds to Rm and y to Rs + + // select half-reg for Rm + if (xy & xyTB) { + // use top 16-bits + mMips->SRA(R_at, Rm, 16); + } else { + // use bottom 16, but sign-extend to 32 + mMips->SEH(R_at, Rm); + } + // select half-reg for Rs + if (xy & xyBT) { + // use top 16-bits + mMips->SRA(R_at2, Rs, 16); + } else { + // use bottom 16, but sign-extend to 32 + mMips->SEH(R_at2, Rs); + } + + mMips->MUL(R_at, R_at, R_at2); + mMips->ADDU(Rd, R_at, Rn); +} + +void ArmToMips64Assembler::SMLAL(int cc, int xy, + int RdHi, int RdLo, int Rs, int Rm) +{ + // *mPC++ = (cc<<28) | 0x1400080 | (RdHi<<16) | (RdLo<<12) | (Rs<<8) | (xy<<4) | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +void ArmToMips64Assembler::SMLAW(int cc, int y, + int Rd, int Rm, int Rs, int Rn) +{ + // *mPC++ = (cc<<28) | 0x1200080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (y<<4) | Rm; + mArmPC[mInum++] = pc(); + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +// used by ARMv6 version of GGLAssembler::filter32 +void ArmToMips64Assembler::UXTB16(int cc, int Rd, int Rm, int rotate) +{ + mArmPC[mInum++] = pc(); + + //Rd[31:16] := ZeroExtend((Rm ROR (8 * sh))[23:16]), + //Rd[15:0] := ZeroExtend((Rm ROR (8 * sh))[7:0]). sh 0-3. + + mMips->ROTR(R_at2, Rm, rotate * 8); + mMips->LUI(R_at, 0xFF); + mMips->ORI(R_at, R_at, 0xFF); + mMips->AND(Rd, R_at2, R_at); +} + +void ArmToMips64Assembler::UBFX(int cc, int Rd, int Rn, int lsb, int width) +{ + /* Placeholder for UBFX */ + mArmPC[mInum++] = pc(); + + mMips->NOP2(); + NOT_IMPLEMENTED(); +} + +// ---------------------------------------------------------------------------- +// Address Processing... +// ---------------------------------------------------------------------------- + +void ArmToMips64Assembler::ADDR_ADD(int cc, + int s, int Rd, int Rn, uint32_t Op2) +{ +// if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required +// if(s != 0) { NOT_IMPLEMENTED(); return;} //Not required + dataProcessing(opADD64, cc, s, Rd, Rn, Op2); +} + +void ArmToMips64Assembler::ADDR_SUB(int cc, + int s, int Rd, int Rn, uint32_t Op2) +{ +// if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required +// if(s != 0) { NOT_IMPLEMENTED(); return;} //Not required + dataProcessing(opSUB64, cc, s, Rd, Rn, Op2); +} + +void ArmToMips64Assembler::ADDR_LDR(int cc, int Rd, int Rn, uint32_t offset) { + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed12_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + amode.writeback = 0; + // fall thru to next case .... + case AMODE_IMM_12_PRE: + if (Rn == ARMAssemblerInterface::SP) { + Rn = R_sp; // convert LDR via Arm SP to LW via Mips SP + } + mMips->LD(Rd, Rn, amode.value); + if (amode.writeback) { // OPTIONAL writeback on pre-index mode + mMips->DADDIU(Rn, Rn, amode.value); + } + break; + case AMODE_IMM_12_POST: + if (Rn == ARMAssemblerInterface::SP) { + Rn = R_sp; // convert STR thru Arm SP to STR thru Mips SP + } + mMips->LD(Rd, Rn, 0); + mMips->DADDIU(Rn, Rn, amode.value); + break; + case AMODE_REG_SCALE_PRE: + // we only support simple base + index, no advanced modes for this one yet + mMips->DADDU(R_at, Rn, amode.reg); + mMips->LD(Rd, R_at, 0); + break; + } +} + +void ArmToMips64Assembler::ADDR_STR(int cc, int Rd, int Rn, uint32_t offset) { + mArmPC[mInum++] = pc(); + // work-around for ARM default address mode of immed12_pre(0) + if (offset > AMODE_UNSUPPORTED) offset = 0; + switch (offset) { + case 0: + amode.value = 0; + amode.writeback = 0; + // fall thru to next case .... + case AMODE_IMM_12_PRE: + if (Rn == ARMAssemblerInterface::SP) { + Rn = R_sp; // convert STR thru Arm SP to SW thru Mips SP + } + if (amode.writeback) { // OPTIONAL writeback on pre-index mode + // If we will writeback, then update the index reg, then store. + // This correctly handles stack-push case. + mMips->DADDIU(Rn, Rn, amode.value); + mMips->SD(Rd, Rn, 0); + } else { + // No writeback so store offset by value + mMips->SD(Rd, Rn, amode.value); + } + break; + case AMODE_IMM_12_POST: + mMips->SD(Rd, Rn, 0); + mMips->DADDIU(Rn, Rn, amode.value); // post index always writes back + break; + case AMODE_REG_SCALE_PRE: + // we only support simple base + index, no advanced modes for this one yet + mMips->DADDU(R_at, Rn, amode.reg); + mMips->SD(Rd, R_at, 0); + break; + } +} + +#if 0 +#pragma mark - +#pragma mark MIPS Assembler... +#endif + + +//************************************************************************** +//************************************************************************** +//************************************************************************** + + +/* MIPS64 assembler +** this is a subset of mips64r6, targeted specifically at ARM instruction +** replacement in the pixelflinger/codeflinger code. +** +** This class is extended from MIPSAssembler class and overrides only +** MIPS64r6 specific stuff. +*/ + +MIPS64Assembler::MIPS64Assembler(const sp<Assembly>& assembly, ArmToMips64Assembler *parent) + : mParent(parent), + MIPSAssembler::MIPSAssembler(assembly, NULL) +{ +} + +MIPS64Assembler::MIPS64Assembler(void* assembly, ArmToMips64Assembler *parent) + : mParent(parent), + MIPSAssembler::MIPSAssembler(NULL, NULL) +{ + mBase = mPC = (uint32_t *)assembly; +} + +MIPS64Assembler::~MIPS64Assembler() +{ +} + +void MIPS64Assembler::reset() +{ + if (mAssembly != NULL) { + mBase = mPC = (uint32_t *)mAssembly->base(); + } else { + mPC = mBase = base(); + } + mBranchTargets.clear(); + mLabels.clear(); + mLabelsInverseMapping.clear(); + mComments.clear(); +} + + +void MIPS64Assembler::disassemble(const char* name) +{ + char di_buf[140]; + + bool arm_disasm_fmt = (mParent->mArmDisassemblyBuffer == NULL) ? false : true; + + typedef char dstr[40]; + dstr *lines = (dstr *)mParent->mArmDisassemblyBuffer; + + if (mParent->mArmDisassemblyBuffer != NULL) { + for (int i=0; i<mParent->mArmInstrCount; ++i) { + string_detab(lines[i]); + } + } + + // iArm is an index to Arm instructions 1...n for this assembly sequence + // mArmPC[iArm] holds the value of the Mips-PC for the first MIPS + // instruction corresponding to that Arm instruction number + + int iArm = 0; + size_t count = pc()-base(); + uint32_t* mipsPC = base(); + + while (count--) { + ssize_t label = mLabelsInverseMapping.indexOfKey(mipsPC); + if (label >= 0) { + ALOGW("%s:\n", mLabelsInverseMapping.valueAt(label)); + } + ssize_t comment = mComments.indexOfKey(mipsPC); + if (comment >= 0) { + ALOGW("; %s\n", mComments.valueAt(comment)); + } + ::mips_disassem(mipsPC, di_buf, arm_disasm_fmt); + string_detab(di_buf); + string_pad(di_buf, 30); + ALOGW("%08lx: %08x %s", uintptr_t(mipsPC), uint32_t(*mipsPC), di_buf); + mipsPC++; + } +} + +void MIPS64Assembler::fix_branches() +{ + // fixup all the branches + size_t count = mBranchTargets.size(); + while (count--) { + const branch_target_t& bt = mBranchTargets[count]; + uint32_t* target_pc = mLabels.valueFor(bt.label); + LOG_ALWAYS_FATAL_IF(!target_pc, + "error resolving branch targets, target_pc is null"); + int32_t offset = int32_t(target_pc - (bt.pc+1)); + *bt.pc |= offset & 0x00FFFF; + } +} + +void MIPS64Assembler::DADDU(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (daddu_fn<<FUNC_SHF) + | (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF); +} + +void MIPS64Assembler::DADDIU(int Rt, int Rs, int16_t imm) +{ + *mPC++ = (daddiu_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16); +} + +void MIPS64Assembler::DSUBU(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (dsubu_fn<<FUNC_SHF) | + (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ; +} + +void MIPS64Assembler::DSUBIU(int Rt, int Rs, int16_t imm) // really addiu(d, s, -j) +{ + *mPC++ = (daddiu_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | ((-imm) & MSK_16); +} + +void MIPS64Assembler::MUL(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (mul_fn<<RE_SHF) | (sop30_fn<<FUNC_SHF) | + (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ; +} + +void MIPS64Assembler::MUH(int Rd, int Rs, int Rt) +{ + *mPC++ = (spec_op<<OP_SHF) | (muh_fn<<RE_SHF) | (sop30_fn<<FUNC_SHF) | + (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ; +} + +void MIPS64Assembler::CLO(int Rd, int Rs) +{ + *mPC++ = (spec_op<<OP_SHF) | (17<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (1<<RE_SHF); +} + +void MIPS64Assembler::CLZ(int Rd, int Rs) +{ + *mPC++ = (spec_op<<OP_SHF) | (16<<FUNC_SHF) | + (Rd<<RD_SHF) | (Rs<<RS_SHF) | (1<<RE_SHF); +} + +void MIPS64Assembler::LD(int Rt, int Rbase, int16_t offset) +{ + *mPC++ = (ld_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + +void MIPS64Assembler::SD(int Rt, int Rbase, int16_t offset) +{ + *mPC++ = (sd_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + +void MIPS64Assembler::LUI(int Rt, int16_t offset) +{ + *mPC++ = (aui_op<<OP_SHF) | (Rt<<RT_SHF) | (offset & MSK_16); +} + + +void MIPS64Assembler::JR(int Rs) +{ + *mPC++ = (spec_op<<OP_SHF) | (Rs<<RS_SHF) | (jalr_fn << FUNC_SHF); + MIPS64Assembler::NOP(); +} + +}; // namespace android: diff --git a/libpixelflinger/codeflinger/MIPS64Assembler.h b/libpixelflinger/codeflinger/MIPS64Assembler.h new file mode 100644 index 000000000..b43e5da04 --- /dev/null +++ b/libpixelflinger/codeflinger/MIPS64Assembler.h @@ -0,0 +1,404 @@ +/* libs/pixelflinger/codeflinger/MIPS64Assembler.h +** +** Copyright 2015, 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. +*/ + +#ifndef ANDROID_MIPS64ASSEMBLER_H +#define ANDROID_MIPS64ASSEMBLER_H + +#include <stdint.h> +#include <sys/types.h> + +#include "utils/KeyedVector.h" +#include "utils/Vector.h" +#include "tinyutils/smartpointer.h" + +#include "ARMAssemblerInterface.h" +#include "MIPSAssembler.h" +#include "CodeCache.h" + +namespace android { + +class MIPS64Assembler; // forward reference + +// this class mimics ARMAssembler interface +// intent is to translate each ARM instruction to 1 or more MIPS instr +// implementation calls MIPS64Assembler class to generate mips code +class ArmToMips64Assembler : public ARMAssemblerInterface +{ +public: + ArmToMips64Assembler(const sp<Assembly>& assembly, + char *abuf = 0, int linesz = 0, int instr_count = 0); + ArmToMips64Assembler(void* assembly); + virtual ~ArmToMips64Assembler(); + + uint32_t* base() const; + uint32_t* pc() const; + void disassemble(const char* name); + + virtual void reset(); + + virtual int generate(const char* name); + virtual int getCodegenArch(); + + virtual void prolog(); + virtual void epilog(uint32_t touched); + virtual void comment(const char* string); + // for testing purposes + void fix_branches(); + void set_condition(int mode, int R1, int R2); + + + // ----------------------------------------------------------------------- + // shifters and addressing modes + // ----------------------------------------------------------------------- + + // shifters... + virtual bool isValidImmediate(uint32_t immed); + virtual int buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm); + + virtual uint32_t imm(uint32_t immediate); + virtual uint32_t reg_imm(int Rm, int type, uint32_t shift); + virtual uint32_t reg_rrx(int Rm); + virtual uint32_t reg_reg(int Rm, int type, int Rs); + + // addressing modes... + // LDR(B)/STR(B)/PLD + // (immediate and Rm can be negative, which indicates U=0) + virtual uint32_t immed12_pre(int32_t immed12, int W=0); + virtual uint32_t immed12_post(int32_t immed12); + virtual uint32_t reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0); + virtual uint32_t reg_scale_post(int Rm, int type=0, uint32_t shift=0); + + // LDRH/LDRSB/LDRSH/STRH + // (immediate and Rm can be negative, which indicates U=0) + virtual uint32_t immed8_pre(int32_t immed8, int W=0); + virtual uint32_t immed8_post(int32_t immed8); + virtual uint32_t reg_pre(int Rm, int W=0); + virtual uint32_t reg_post(int Rm); + + + + + virtual void dataProcessing(int opcode, int cc, int s, + int Rd, int Rn, + uint32_t Op2); + virtual void MLA(int cc, int s, + int Rd, int Rm, int Rs, int Rn); + virtual void MUL(int cc, int s, + int Rd, int Rm, int Rs); + virtual void UMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + virtual void UMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + virtual void SMULL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + virtual void SMUAL(int cc, int s, + int RdLo, int RdHi, int Rm, int Rs); + + virtual void B(int cc, uint32_t* pc); + virtual void BL(int cc, uint32_t* pc); + virtual void BX(int cc, int Rn); + virtual void label(const char* theLabel); + virtual void B(int cc, const char* label); + virtual void BL(int cc, const char* label); + + virtual uint32_t* pcForLabel(const char* label); + + virtual void LDR (int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void LDRB(int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void STR (int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void STRB(int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void LDRH (int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void LDRSB(int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void LDRSH(int cc, int Rd, + int Rn, uint32_t offset = 0); + virtual void STRH (int cc, int Rd, + int Rn, uint32_t offset = 0); + + virtual void LDM(int cc, int dir, + int Rn, int W, uint32_t reg_list); + virtual void STM(int cc, int dir, + int Rn, int W, uint32_t reg_list); + + virtual void SWP(int cc, int Rn, int Rd, int Rm); + virtual void SWPB(int cc, int Rn, int Rd, int Rm); + virtual void SWI(int cc, uint32_t comment); + + virtual void PLD(int Rn, uint32_t offset); + virtual void CLZ(int cc, int Rd, int Rm); + virtual void QADD(int cc, int Rd, int Rm, int Rn); + virtual void QDADD(int cc, int Rd, int Rm, int Rn); + virtual void QSUB(int cc, int Rd, int Rm, int Rn); + virtual void QDSUB(int cc, int Rd, int Rm, int Rn); + virtual void SMUL(int cc, int xy, + int Rd, int Rm, int Rs); + virtual void SMULW(int cc, int y, + int Rd, int Rm, int Rs); + virtual void SMLA(int cc, int xy, + int Rd, int Rm, int Rs, int Rn); + virtual void SMLAL(int cc, int xy, + int RdHi, int RdLo, int Rs, int Rm); + virtual void SMLAW(int cc, int y, + int Rd, int Rm, int Rs, int Rn); + + // byte/half word extract... + virtual void UXTB16(int cc, int Rd, int Rm, int rotate); + + // bit manipulation... + virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width); + + // Address loading/storing/manipulation + virtual void ADDR_LDR(int cc, int Rd, int Rn, uint32_t offset = __immed12_pre(0)); + virtual void ADDR_STR(int cc, int Rd, int Rn, uint32_t offset = __immed12_pre(0)); + virtual void ADDR_ADD(int cc, int s, int Rd, int Rn, uint32_t Op2); + virtual void ADDR_SUB(int cc, int s, int Rd, int Rn, uint32_t Op2); + + // this is some crap to share is MIPS64Assembler class for debug + char * mArmDisassemblyBuffer; + int mArmLineLength; + int mArmInstrCount; + + int mInum; // current arm instuction number (0..n) + uint32_t** mArmPC; // array: PC for 1st mips instr of + // each translated ARM instr + + +private: + ArmToMips64Assembler(const ArmToMips64Assembler& rhs); + ArmToMips64Assembler& operator = (const ArmToMips64Assembler& rhs); + + void init_conditional_labels(void); + + void protectConditionalOperands(int Rd); + + // reg__tmp set to MIPS AT, reg 1 + int dataProcAdrModes(int op, int& source, bool sign = false, int reg_tmp = 1); + + sp<Assembly> mAssembly; + MIPS64Assembler* mMips; + + + enum misc_constants_t { + ARM_MAX_INSTUCTIONS = 512 // based on ASSEMBLY_SCRATCH_SIZE + }; + + enum { + SRC_REG = 0, + SRC_IMM, + SRC_ERROR = -1 + }; + + enum addr_modes { + // start above the range of legal mips reg #'s (0-31) + AMODE_REG = 0x20, + AMODE_IMM, AMODE_REG_IMM, // for data processing + AMODE_IMM_12_PRE, AMODE_IMM_12_POST, // for load/store + AMODE_REG_SCALE_PRE, AMODE_IMM_8_PRE, + AMODE_IMM_8_POST, AMODE_REG_PRE, + AMODE_UNSUPPORTED + }; + + struct addr_mode_t { // address modes for current ARM instruction + int reg; + int stype; + uint32_t value; + bool writeback; // writeback the adr reg after modification + } amode; + + enum cond_types { + CMP_COND = 1, + SBIT_COND + }; + + struct cond_mode_t { // conditional-execution info for current ARM instruction + cond_types type; + int r1; + int r2; + int labelnum; + char label[100][10]; + } cond; +}; + + + + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- + +// This is the basic MIPS64 assembler, which just creates the opcodes in memory. +// All the more complicated work is done in ArmToMips64Assember above. +// Inherits MIPSAssembler class, and overrides only MIPS64r6 specific stuff + +class MIPS64Assembler : public MIPSAssembler +{ +public: + MIPS64Assembler(const sp<Assembly>& assembly, ArmToMips64Assembler *parent); + MIPS64Assembler(void* assembly, ArmToMips64Assembler *parent); + virtual ~MIPS64Assembler(); + + virtual void reset(); + virtual void disassemble(const char* name); + + void fix_branches(); + + // ------------------------------------------------------------------------ + // MIPS64AssemblerInterface... + // ------------------------------------------------------------------------ + +#if 0 +#pragma mark - +#pragma mark Arithmetic... +#endif + + void DADDU(int Rd, int Rs, int Rt); + void DADDIU(int Rt, int Rs, int16_t imm); + void DSUBU(int Rd, int Rs, int Rt); + void DSUBIU(int Rt, int Rs, int16_t imm); + virtual void MUL(int Rd, int Rs, int Rt); + void MUH(int Rd, int Rs, int Rt); + +#if 0 +#pragma mark - +#pragma mark Logical... +#endif + + virtual void CLO(int Rd, int Rs); + virtual void CLZ(int Rd, int Rs); + +#if 0 +#pragma mark - +#pragma mark Load/store... +#endif + + void LD(int Rt, int Rbase, int16_t offset); + void SD(int Rt, int Rbase, int16_t offset); + virtual void LUI(int Rt, int16_t offset); + +#if 0 +#pragma mark - +#pragma mark Branch... +#endif + + void JR(int Rs); + + +protected: + ArmToMips64Assembler *mParent; + + // opcode field of all instructions + enum opcode_field { + spec_op, regimm_op, j_op, jal_op, // 0x00 - 0x03 + beq_op, bne_op, pop06_op, pop07_op, // 0x04 - 0x07 + pop10_op, addiu_op, slti_op, sltiu_op, // 0x08 - 0x0b + andi_op, ori_op, xori_op, aui_op, // 0x0c - 0x0f + cop0_op, cop1_op, cop2_op, rsrv_opc_0, // 0x10 - 0x13 + rsrv_opc_1, rsrv_opc_2, pop26_op, pop27_op, // 0x14 - 0x17 + pop30_op, daddiu_op, rsrv_opc_3, rsrv_opc_4, // 0x18 - 0x1b + rsrv_opc_5, daui_op, msa_op, spec3_op, // 0x1c - 0x1f + lb_op, lh_op, rsrv_opc_6, lw_op, // 0x20 - 0x23 + lbu_op, lhu_op, rsrv_opc_7, lwu_op, // 0x24 - 0x27 + sb_op, sh_op, rsrv_opc_8, sw_op, // 0x28 - 0x2b + rsrv_opc_9, rsrv_opc_10, rsrv_opc_11, rsrv_opc_12, // 0x2c - 0x2f + rsrv_opc_13, lwc1_op, bc_op, rsrv_opc_14, // 0x2c - 0x2f + rsrv_opc_15, ldc1_op, pop66_op, ld_op, // 0x30 - 0x33 + rsrv_opc_16, swc1_op, balc_op, pcrel_op, // 0x34 - 0x37 + rsrv_opc_17, sdc1_op, pop76_op, sd_op // 0x38 - 0x3b + }; + + + // func field for special opcode + enum func_spec_op { + sll_fn, rsrv_spec_0, srl_fn, sra_fn, + sllv_fn, lsa_fn, srlv_fn, srav_fn, + rsrv_spec_1, jalr_fn, rsrv_spec_2, rsrv_spec_3, + syscall_fn, break_fn, sdbbp_fn, sync_fn, + clz_fn, clo_fn, dclz_fn, dclo_fn, + dsllv_fn, dlsa_fn, dsrlv_fn, dsrav_fn, + sop30_fn, sop31_fn, sop32_fn, sop33_fn, + sop34_fn, sop35_fn, sop36_fn, sop37_fn, + add_fn, addu_fn, sub_fn, subu_fn, + and_fn, or_fn, xor_fn, nor_fn, + rsrv_spec_4, rsrv_spec_5, slt_fn, sltu_fn, + dadd_fn, daddu_fn, dsub_fn, dsubu_fn, + tge_fn, tgeu_fn, tlt_fn, tltu_fn, + teq_fn, seleqz_fn, tne_fn, selnez_fn, + dsll_fn, rsrv_spec_6, dsrl_fn, dsra_fn, + dsll32_fn, rsrv_spec_7, dsrl32_fn, dsra32_fn + }; + + // func field for spec3 opcode + enum func_spec3_op { + ext_fn, dextm_fn, dextu_fn, dext_fn, + ins_fn, dinsm_fn, dinsu_fn, dins_fn, + cachee_fn = 0x1b, sbe_fn, she_fn, sce_fn, swe_fn, + bshfl_fn, prefe_fn = 0x23, dbshfl_fn, cache_fn, sc_fn, scd_fn, + lbue_fn, lhue_fn, lbe_fn = 0x2c, lhe_fn, lle_fn, lwe_fn, + pref_fn = 0x35, ll_fn, lld_fn, rdhwr_fn = 0x3b + }; + + // sa field for spec3 opcodes, with BSHFL function + enum func_spec3_bshfl { + bitswap_fn, + wsbh_fn = 0x02, + dshd_fn = 0x05, + seb_fn = 0x10, + seh_fn = 0x18 + }; + + // rt field of regimm opcodes. + enum regimm_fn { + bltz_fn, bgez_fn, + dahi_fn = 0x6, + nal_fn = 0x10, bal_fn, bltzall_fn, bgezall_fn, + sigrie_fn = 0x17, + dati_fn = 0x1e, synci_fn + }; + + enum muldiv_fn { + mul_fn = 0x02, muh_fn + }; + + enum mips_inst_shifts { + OP_SHF = 26, + JTARGET_SHF = 0, + RS_SHF = 21, + RT_SHF = 16, + RD_SHF = 11, + RE_SHF = 6, + SA_SHF = RE_SHF, // synonym + IMM_SHF = 0, + FUNC_SHF = 0, + + // mask values + MSK_16 = 0xffff, + + + CACHEOP_SHF = 18, + CACHESEL_SHF = 16, + }; +}; + + +}; // namespace android + +#endif //ANDROID_MIPS64ASSEMBLER_H diff --git a/libpixelflinger/codeflinger/MIPSAssembler.cpp b/libpixelflinger/codeflinger/MIPSAssembler.cpp index a88d2fe00..daa231fe0 100644 --- a/libpixelflinger/codeflinger/MIPSAssembler.cpp +++ b/libpixelflinger/codeflinger/MIPSAssembler.cpp @@ -1358,7 +1358,7 @@ void MIPSAssembler::disassemble(const char* name) ::mips_disassem(mipsPC, di_buf, arm_disasm_fmt); string_detab(di_buf); string_pad(di_buf, 30); - ALOGW("%08x: %08x %s", uint32_t(mipsPC), uint32_t(*mipsPC), di_buf); + ALOGW("%08x: %08x %s", uintptr_t(mipsPC), uint32_t(*mipsPC), di_buf); mipsPC++; } } @@ -1407,7 +1407,7 @@ int MIPSAssembler::generate(const char* name) #if defined(WITH_LIB_HARDWARE) if (__builtin_expect(mQemuTracing, 0)) { - int err = qemu_add_mapping(int(base()), name); + int err = qemu_add_mapping(uintptr_t(base()), name); mQemuTracing = (err >= 0); } #endif diff --git a/libpixelflinger/codeflinger/MIPSAssembler.h b/libpixelflinger/codeflinger/MIPSAssembler.h index 430ab064c..06cb0d0df 100644 --- a/libpixelflinger/codeflinger/MIPSAssembler.h +++ b/libpixelflinger/codeflinger/MIPSAssembler.h @@ -21,9 +21,9 @@ #include <stdint.h> #include <sys/types.h> -#include "tinyutils/KeyedVector.h" -#include "tinyutils/Vector.h" #include "tinyutils/smartpointer.h" +#include "utils/KeyedVector.h" +#include "utils/Vector.h" #include "ARMAssemblerInterface.h" #include "CodeCache.h" @@ -244,20 +244,20 @@ public: MIPSAssembler(const sp<Assembly>& assembly, ArmToMipsAssembler *parent); virtual ~MIPSAssembler(); - uint32_t* base() const; - uint32_t* pc() const; - void reset(); + virtual uint32_t* base() const; + virtual uint32_t* pc() const; + virtual void reset(); - void disassemble(const char* name); + virtual void disassemble(const char* name); - void prolog(); - void epilog(uint32_t touched); - int generate(const char* name); - void comment(const char* string); - void label(const char* string); + virtual void prolog(); + virtual void epilog(uint32_t touched); + virtual int generate(const char* name); + virtual void comment(const char* string); + virtual void label(const char* string); // valid only after generate() has been called - uint32_t* pcForLabel(const char* label); + virtual uint32_t* pcForLabel(const char* label); // ------------------------------------------------------------------------ @@ -399,9 +399,9 @@ public: -private: - void string_detab(char *s); - void string_pad(char *s, int padded_len); +protected: + virtual void string_detab(char *s); + virtual void string_pad(char *s, int padded_len); ArmToMipsAssembler *mParent; sp<Assembly> mAssembly; @@ -537,7 +537,11 @@ private: enum mips_regnames { R_zero = 0, R_at, R_v0, R_v1, R_a0, R_a1, R_a2, R_a3, +#if __mips_isa_rev < 6 R_t0, R_t1, R_t2, R_t3, R_t4, R_t5, R_t6, R_t7, +#else + R_a4, R_a5, R_a6, R_a7, R_t0, R_t1, R_t2, R_t3, +#endif R_s0, R_s1, R_s2, R_s3, R_s4, R_s5, R_s6, R_s7, R_t8, R_t9, R_k0, R_k1, R_gp, R_sp, R_s8, R_ra, R_lr = R_s8, diff --git a/libpixelflinger/codeflinger/mips64_disassem.c b/libpixelflinger/codeflinger/mips64_disassem.c new file mode 100644 index 000000000..44b7fe7cd --- /dev/null +++ b/libpixelflinger/codeflinger/mips64_disassem.c @@ -0,0 +1,582 @@ +/* $NetBSD: db_disasm.c,v 1.19 2007/02/28 04:21:53 thorpej Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)kadb.c 8.1 (Berkeley) 6/10/93 + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdarg.h> +#include <stdbool.h> +#include <sys/cdefs.h> + +#include <sys/types.h> +#include "mips_opcode.h" + +#include <cutils/log.h> + +static char *sprintf_buffer; +static int sprintf_buf_len; + + +typedef uint64_t db_addr_t; +static void db_printf(const char* fmt, ...); + +static const char * const op_name[64] = { +/* 0 */ "spec", "bcond", "j", "jal", "beq", "bne", "blez", "bgtz", +/* 8 */ "pop10", "addiu", "slti", "sltiu", "andi", "ori", "xori", "aui", +/*16 */ "cop0", "cop1", "cop2", "?", "?", "?", "pop26", "pop27", +/*24 */ "pop30", "daddiu", "?", "?", "?", "daui", "msa", "op37", +/*32 */ "lb", "lh", "?", "lw", "lbu", "lhu", "?", "lwu", +/*40 */ "sb", "sh", "?", "sw", "?", "?", "?", "?", +/*48 */ "?", "lwc1", "bc", "?", "?", "ldc1", "pop66", "ld", +/*56 */ "?", "swc1", "balc", "pcrel", "?", "sdc1", "pop76", "sd" +}; + +static const char * const spec_name[64] = { +/* 0 */ "sll", "?", "srl", "sra", "sllv", "?", "srlv", "srav", +/* 8 */ "?", "jalr", "?", "?", "syscall", "break", "sdbpp", "sync", +/*16 */ "clz", "clo", "dclz", "dclo", "dsllv", "dlsa", "dsrlv", "dsrav", +/*24 */ "sop30", "sop31", "sop32", "sop33", "sop34", "sop35", "sop36", "sop37", +/*32 */ "add", "addu", "sub", "subu", "and", "or", "xor", "nor", +/*40 */ "?", "?", "slt", "sltu", "dadd", "daddu", "dsub", "dsubu", +/*48 */ "tge", "tgeu", "tlt", "tltu", "teq", "seleqz", "tne", "selnez", +/*56 */ "dsll", "?", "dsrl", "dsra", "dsll32", "?", "dsrl32", "dsra32" +}; + +static const char * const bcond_name[32] = { +/* 0 */ "bltz", "bgez", "?", "?", "?", "?", "dahi", "?", +/* 8 */ "?", "?", "?", "?", "?", "?", "?", "?", +/*16 */ "nal", "bal", "?", "?", "?", "?", "?", "sigrie", +/*24 */ "?", "?", "?", "?", "?", "?", "dati", "synci", +}; + +static const char * const cop1_name[64] = { +/* 0 */ "fadd", "fsub", "fmpy", "fdiv", "fsqrt","fabs", "fmov", "fneg", +/* 8 */ "fop08","fop09","fop0a","fop0b","fop0c","fop0d","fop0e","fop0f", +/*16 */ "fop10","fop11","fop12","fop13","fop14","fop15","fop16","fop17", +/*24 */ "fop18","fop19","fop1a","fop1b","fop1c","fop1d","fop1e","fop1f", +/*32 */ "fcvts","fcvtd","fcvte","fop23","fcvtw","fop25","fop26","fop27", +/*40 */ "fop28","fop29","fop2a","fop2b","fop2c","fop2d","fop2e","fop2f", +/*48 */ "fcmp.f","fcmp.un","fcmp.eq","fcmp.ueq","fcmp.olt","fcmp.ult", + "fcmp.ole","fcmp.ule", +/*56 */ "fcmp.sf","fcmp.ngle","fcmp.seq","fcmp.ngl","fcmp.lt","fcmp.nge", + "fcmp.le","fcmp.ngt" +}; + +static const char * const fmt_name[16] = { + "s", "d", "e", "fmt3", + "w", "fmt5", "fmt6", "fmt7", + "fmt8", "fmt9", "fmta", "fmtb", + "fmtc", "fmtd", "fmte", "fmtf" +}; + +static char * const mips_reg_name[32] = { + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra" +}; + +static char * alt_arm_reg_name[32] = { // hacked names for comparison with ARM code + "zero", "at", "r0", "r1", "r2", "r3", "r4", "r5", + "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", + "r14", "r15", "at2", "cmp", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra" +}; + +static char ** reg_name = &mips_reg_name[0]; + +static const char * const c0_opname[64] = { + "c0op00","tlbr", "tlbwi", "c0op03","c0op04","c0op05","tlbwr", "c0op07", + "tlbp", "c0op11","c0op12","c0op13","c0op14","c0op15","c0op16","c0op17", + "rfe", "c0op21","c0op22","c0op23","c0op24","c0op25","c0op26","c0op27", + "eret", "c0op31","c0op32","c0op33","c0op34","c0op35","c0op36","c0op37", + "c0op40","c0op41","c0op42","c0op43","c0op44","c0op45","c0op46","c0op47", + "c0op50","c0op51","c0op52","c0op53","c0op54","c0op55","c0op56","c0op57", + "c0op60","c0op61","c0op62","c0op63","c0op64","c0op65","c0op66","c0op67", + "c0op70","c0op71","c0op72","c0op73","c0op74","c0op75","c0op77","c0op77", +}; + +static const char * const c0_reg[32] = { + "index", "random", "tlblo0", "tlblo1", + "context", "pagemask", "wired", "cp0r7", + "badvaddr", "count", "tlbhi", "compare", + "status", "cause", "epc", "prid", + "config", "lladdr", "watchlo", "watchhi", + "xcontext", "cp0r21", "cp0r22", "debug", + "depc", "perfcnt", "ecc", "cacheerr", + "taglo", "taghi", "errepc", "desave" +}; + +static void print_addr(db_addr_t); +db_addr_t mips_disassem(db_addr_t loc, char *di_buffer, int alt_dis_format); + + +/* + * Disassemble instruction 'insn' nominally at 'loc'. + * 'loc' may in fact contain a breakpoint instruction. + */ +static db_addr_t +db_disasm_insn(int insn, db_addr_t loc, bool altfmt) +{ + bool bdslot = false; + InstFmt i; + + i.word = insn; + + switch (i.JType.op) { + case OP_SPECIAL: + if (i.word == 0) { + db_printf("nop"); + break; + } + if (i.word == 0x0080) { + db_printf("NIY"); + break; + } + if (i.word == 0x00c0) { + db_printf("NOT IMPL"); + break; + } + /* Special cases -------------------------------------------------- + * "addu" is a "move" only in 32-bit mode. What's the correct + * answer - never decode addu/daddu as "move"? + */ + if ( (i.RType.func == OP_ADDU && i.RType.rt == 0) || + (i.RType.func == OP_OR && i.RType.rt == 0) ) { + db_printf("move\t%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rs]); + break; + } + + if (i.RType.func == OP_SRL && (i.RType.rs & 1) == 1) { + db_printf("rotr\t%s,%s,%d", reg_name[i.RType.rd], + reg_name[i.RType.rt], i.RType.shamt); + break; + } + if (i.RType.func == OP_SRLV && (i.RType.shamt & 1) == 1) { + db_printf("rotrv\t%s,%s,%s", reg_name[i.RType.rd], + reg_name[i.RType.rt], reg_name[i.RType.rs]); + break; + } + + if (i.RType.func == OP_SOP30) { + if (i.RType.shamt == OP_MUL) { + db_printf("mul"); + } else if (i.RType.shamt == OP_MUH) { + db_printf("muh"); + } + db_printf("\t%s,%s,%s", reg_name[i.RType.rd], + reg_name[i.RType.rs], reg_name[i.RType.rt]); + break; + } + if (i.RType.func == OP_SOP31) { + if (i.RType.shamt == OP_MUL) { + db_printf("mulu"); + } else if (i.RType.shamt == OP_MUH) { + db_printf("muhu"); + } + db_printf("\t%s,%s,%s", reg_name[i.RType.rd], + reg_name[i.RType.rs], reg_name[i.RType.rt]); + break; + } + + if (i.RType.func == OP_JALR && i.RType.rd == 0) { + db_printf("jr\t%s", reg_name[i.RType.rs]); + bdslot = true; + break; + } + + db_printf("%s", spec_name[i.RType.func]); + switch (i.RType.func) { + case OP_SLL: + case OP_SRL: + case OP_SRA: + case OP_DSLL: + + case OP_DSRL: + case OP_DSRA: + case OP_DSLL32: + case OP_DSRL32: + case OP_DSRA32: + db_printf("\t%s,%s,%d", + reg_name[i.RType.rd], + reg_name[i.RType.rt], + i.RType.shamt); + break; + + case OP_SLLV: + case OP_SRLV: + case OP_SRAV: + case OP_DSLLV: + case OP_DSRLV: + case OP_DSRAV: + db_printf("\t%s,%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rt], + reg_name[i.RType.rs]); + break; + + case OP_CLZ: + case OP_CLO: + case OP_DCLZ: + case OP_DCLO: + db_printf("\t%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rs]); + break; + + case OP_JALR: + db_printf("\t"); + if (i.RType.rd != 31) { + db_printf("%s,", reg_name[i.RType.rd]); + } + db_printf("%s", reg_name[i.RType.rs]); + bdslot = true; + break; + + case OP_SYSCALL: + case OP_SYNC: + break; + + case OP_BREAK: + db_printf("\t%d", (i.RType.rs << 5) | i.RType.rt); + break; + + default: + db_printf("\t%s,%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rs], + reg_name[i.RType.rt]); + } + break; + + case OP_SPECIAL3: + if (i.RType.func == OP_EXT) + db_printf("ext\t%s,%s,%d,%d", + reg_name[i.RType.rt], + reg_name[i.RType.rs], + i.RType.shamt, + i.RType.rd+1); + else if (i.RType.func == OP_DEXT) + db_printf("dext\t%s,%s,%d,%d", + reg_name[i.RType.rt], + reg_name[i.RType.rs], + i.RType.shamt, + i.RType.rd+1); + else if (i.RType.func == OP_DEXTM) + db_printf("dextm\t%s,%s,%d,%d", + reg_name[i.RType.rt], + reg_name[i.RType.rs], + i.RType.shamt, + i.RType.rd+33); + else if (i.RType.func == OP_DEXTU) + db_printf("dextu\t%s,%s,%d,%d", + reg_name[i.RType.rt], + reg_name[i.RType.rs], + i.RType.shamt+32, + i.RType.rd+1); + else if (i.RType.func == OP_INS) + db_printf("ins\t%s,%s,%d,%d", + reg_name[i.RType.rt], + reg_name[i.RType.rs], + i.RType.shamt, + i.RType.rd-i.RType.shamt+1); + else if (i.RType.func == OP_DINS) + db_printf("dins\t%s,%s,%d,%d", + reg_name[i.RType.rt], + reg_name[i.RType.rs], + i.RType.shamt, + i.RType.rd-i.RType.shamt+1); + else if (i.RType.func == OP_DINSM) + db_printf("dinsm\t%s,%s,%d,%d", + reg_name[i.RType.rt], + reg_name[i.RType.rs], + i.RType.shamt, + i.RType.rd-i.RType.shamt+33); + else if (i.RType.func == OP_DINSU) + db_printf("dinsu\t%s,%s,%d,%d", + reg_name[i.RType.rt], + reg_name[i.RType.rs], + i.RType.shamt+32, + i.RType.rd-i.RType.shamt+1); + else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_WSBH) + db_printf("wsbh\t%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rt]); + else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_SEB) + db_printf("seb\t%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rt]); + else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_SEH) + db_printf("seh\t%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rt]); + else if (i.RType.func == OP_RDHWR) + db_printf("rdhwr\t%s,%s", + reg_name[i.RType.rd], + reg_name[i.RType.rt]); + else + db_printf("Unknown"); + break; + + case OP_BCOND: + db_printf("%s\t%s,", bcond_name[i.IType.rt], + reg_name[i.IType.rs]); + goto pr_displ; + + case OP_BLEZ: + case OP_BGTZ: + db_printf("%s\t%s,", op_name[i.IType.op], + reg_name[i.IType.rs]); + goto pr_displ; + + case OP_BEQ: + if (i.IType.rs == 0 && i.IType.rt == 0) { + db_printf("b\t"); + goto pr_displ; + } + /* FALLTHROUGH */ + case OP_BNE: + db_printf("%s\t%s,%s,", op_name[i.IType.op], + reg_name[i.IType.rs], + reg_name[i.IType.rt]); + pr_displ: + print_addr(loc + 4 + ((short)i.IType.imm << 2)); + bdslot = true; + break; + + case OP_COP0: + switch (i.RType.rs) { + case OP_BCx: + case OP_BCy: + + db_printf("bc0%c\t", + "ft"[i.RType.rt & COPz_BC_TF_MASK]); + goto pr_displ; + + case OP_MT: + db_printf("mtc0\t%s,%s", + reg_name[i.RType.rt], + c0_reg[i.RType.rd]); + break; + + case OP_DMT: + db_printf("dmtc0\t%s,%s", + reg_name[i.RType.rt], + c0_reg[i.RType.rd]); + break; + + case OP_MF: + db_printf("mfc0\t%s,%s", + reg_name[i.RType.rt], + c0_reg[i.RType.rd]); + break; + + case OP_DMF: + db_printf("dmfc0\t%s,%s", + reg_name[i.RType.rt], + c0_reg[i.RType.rd]); + break; + + default: + db_printf("%s", c0_opname[i.FRType.func]); + } + break; + + case OP_COP1: + switch (i.RType.rs) { + case OP_BCx: + case OP_BCy: + db_printf("bc1%c\t", + "ft"[i.RType.rt & COPz_BC_TF_MASK]); + goto pr_displ; + + case OP_MT: + db_printf("mtc1\t%s,f%d", + reg_name[i.RType.rt], + i.RType.rd); + break; + + case OP_MF: + db_printf("mfc1\t%s,f%d", + reg_name[i.RType.rt], + i.RType.rd); + break; + + case OP_CT: + db_printf("ctc1\t%s,f%d", + reg_name[i.RType.rt], + i.RType.rd); + break; + + case OP_CF: + db_printf("cfc1\t%s,f%d", + reg_name[i.RType.rt], + i.RType.rd); + break; + + default: + db_printf("%s.%s\tf%d,f%d,f%d", + cop1_name[i.FRType.func], + fmt_name[i.FRType.fmt], + i.FRType.fd, i.FRType.fs, i.FRType.ft); + } + break; + + case OP_J: + case OP_JAL: + db_printf("%s\t", op_name[i.JType.op]); + print_addr((loc & 0xFFFFFFFFF0000000) | (i.JType.target << 2)); + bdslot = true; + break; + + case OP_LWC1: + case OP_SWC1: + db_printf("%s\tf%d,", op_name[i.IType.op], + i.IType.rt); + goto loadstore; + + case OP_LB: + case OP_LH: + case OP_LW: + case OP_LD: + case OP_LBU: + case OP_LHU: + case OP_LWU: + case OP_SB: + case OP_SH: + case OP_SW: + case OP_SD: + db_printf("%s\t%s,", op_name[i.IType.op], + reg_name[i.IType.rt]); + loadstore: + db_printf("%d(%s)", (short)i.IType.imm, + reg_name[i.IType.rs]); + break; + + case OP_ORI: + case OP_XORI: + if (i.IType.rs == 0) { + db_printf("li\t%s,0x%x", + reg_name[i.IType.rt], + i.IType.imm); + break; + } + /* FALLTHROUGH */ + case OP_ANDI: + db_printf("%s\t%s,%s,0x%x", op_name[i.IType.op], + reg_name[i.IType.rt], + reg_name[i.IType.rs], + i.IType.imm); + break; + + case OP_AUI: + if (i.IType.rs == 0) { + db_printf("lui\t%s,0x%x", reg_name[i.IType.rt], + i.IType.imm); + } else { + db_printf("%s\t%s,%s,%d", op_name[i.IType.op], + reg_name[i.IType.rt], reg_name[i.IType.rs], + (short)i.IType.imm); + } + break; + + case OP_ADDIU: + case OP_DADDIU: + if (i.IType.rs == 0) { + db_printf("li\t%s,%d", + reg_name[i.IType.rt], + (short)i.IType.imm); + break; + } + /* FALLTHROUGH */ + default: + db_printf("%s\t%s,%s,%d", op_name[i.IType.op], + reg_name[i.IType.rt], + reg_name[i.IType.rs], + (short)i.IType.imm); + } + // db_printf("\n"); + // if (bdslot) { + // db_printf(" bd: "); + // mips_disassem(loc+4); + // return (loc + 8); + // } + return (loc + 4); +} + +static void +print_addr(db_addr_t loc) +{ + db_printf("0x%08lx", loc); +} + +static void db_printf(const char* fmt, ...) +{ + int cnt; + va_list argp; + va_start(argp, fmt); + if (sprintf_buffer) { + cnt = vsnprintf(sprintf_buffer, sprintf_buf_len, fmt, argp); + sprintf_buffer += cnt; + sprintf_buf_len -= cnt; + } else { + vprintf(fmt, argp); + } +} + +/* + * Disassemble instruction at 'loc'. + * Return address of start of next instruction. + * Since this function is used by 'examine' and by 'step' + * "next instruction" does NOT mean the next instruction to + * be executed but the 'linear' next instruction. + */ +db_addr_t +mips_disassem(db_addr_t loc, char *di_buffer, int alt_dis_format) +{ + u_int32_t instr; + + if (alt_dis_format) { // use ARM register names for disassembly + reg_name = &alt_arm_reg_name[0]; + } + + sprintf_buffer = di_buffer; // quick 'n' dirty printf() vs sprintf() + sprintf_buf_len = 39; // should be passed in + + instr = *(u_int32_t *)loc; + return (db_disasm_insn(instr, loc, false)); +} diff --git a/libpixelflinger/codeflinger/mips64_disassem.h b/libpixelflinger/codeflinger/mips64_disassem.h new file mode 100644 index 000000000..c94f04f9b --- /dev/null +++ b/libpixelflinger/codeflinger/mips64_disassem.h @@ -0,0 +1,56 @@ +/* $NetBSD: db_disasm.c,v 1.19 2007/02/28 04:21:53 thorpej Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)kadb.c 8.1 (Berkeley) 6/10/93 + */ + + + +#ifndef ANDROID_MIPS_DISASSEM_H +#define ANDROID_MIPS_DISASSEM_H + +#include <sys/types.h> + +#if __cplusplus +extern "C" { +#endif + +/* Prototypes for callable functions */ + +void mips_disassem(uint32_t *location, char *di_buffer, int alt_fmt); + +#if __cplusplus +} +#endif + +#endif /* !ANDROID_MIPS_DISASSEM_H */ diff --git a/libpixelflinger/codeflinger/mips_disassem.c b/libpixelflinger/codeflinger/mips_disassem.c index 4ab9bd35d..3007b1534 100644 --- a/libpixelflinger/codeflinger/mips_disassem.c +++ b/libpixelflinger/codeflinger/mips_disassem.c @@ -323,14 +323,14 @@ db_disasm_insn(int insn, db_addr_t loc, bool altfmt) db_printf("ext\t%s,%s,%d,%d", reg_name[i.RType.rt], reg_name[i.RType.rs], - i.RType.rd+1, - i.RType.shamt); + i.RType.shamt, + i.RType.rd+1); else if (i.RType.func == OP_INS) db_printf("ins\t%s,%s,%d,%d", reg_name[i.RType.rt], reg_name[i.RType.rs], - i.RType.rd+1, - i.RType.shamt); + i.RType.shamt, + i.RType.rd-i.RType.shamt+1); else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_WSBH) db_printf("wsbh\t%s,%s", reg_name[i.RType.rd], diff --git a/libpixelflinger/codeflinger/mips_opcode.h b/libpixelflinger/codeflinger/mips_opcode.h index 7ed5ef579..45bb19ea1 100644 --- a/libpixelflinger/codeflinger/mips_opcode.h +++ b/libpixelflinger/codeflinger/mips_opcode.h @@ -125,69 +125,118 @@ typedef union { #define OP_BLEZ 006 #define OP_BGTZ 007 +#if __mips_isa_rev < 6 #define OP_ADDI 010 +#else +#define OP_POP10 010 +#endif + #define OP_ADDIU 011 #define OP_SLTI 012 #define OP_SLTIU 013 #define OP_ANDI 014 #define OP_ORI 015 #define OP_XORI 016 + +#if __mips_isa_rev < 6 #define OP_LUI 017 +#else +#define OP_AUI 017 +#endif #define OP_COP0 020 #define OP_COP1 021 #define OP_COP2 022 + +#if __mips_isa_rev < 6 #define OP_COP3 023 -#define OP_BEQL 024 /* MIPS-II, for r4000 port */ -#define OP_BNEL 025 /* MIPS-II, for r4000 port */ -#define OP_BLEZL 026 /* MIPS-II, for r4000 port */ -#define OP_BGTZL 027 /* MIPS-II, for r4000 port */ +#define OP_BEQL 024 +#define OP_BNEL 025 +#define OP_BLEZL 026 +#define OP_BGTZL 027 +#define OP_DADDI 030 +#else +#define OP_POP26 026 +#define OP_POP27 027 +#define OP_POP30 030 +#endif -#define OP_DADDI 030 /* MIPS-II, for r4000 port */ -#define OP_DADDIU 031 /* MIPS-II, for r4000 port */ -#define OP_LDL 032 /* MIPS-II, for r4000 port */ -#define OP_LDR 033 /* MIPS-II, for r4000 port */ +#define OP_DADDIU 031 -#define OP_SPECIAL2 034 /* QED opcodes */ -#define OP_SPECIAL3 037 /* mips32r2 opcodes */ +#if __mips_isa_rev < 6 +#define OP_LDL 032 +#define OP_LDR 033 +#define OP_SPECIAL2 034 +#else +#define OP_DAUI 035 +#endif + +#define OP_SPECIAL3 037 #define OP_LB 040 #define OP_LH 041 + +#if __mips_isa_rev < 6 #define OP_LWL 042 +#endif + #define OP_LW 043 #define OP_LBU 044 #define OP_LHU 045 #define OP_LWR 046 #define OP_LHU 045 + +#if __mips_isa_rev < 6 #define OP_LWR 046 -#define OP_LWU 047 /* MIPS-II, for r4000 port */ +#endif + +#define OP_LWU 047 #define OP_SB 050 #define OP_SH 051 + +#if __mips_isa_rev < 6 #define OP_SWL 052 +#endif + #define OP_SW 053 -#define OP_SDL 054 /* MIPS-II, for r4000 port */ -#define OP_SDR 055 /* MIPS-II, for r4000 port */ -#define OP_SWR 056 -#define OP_CACHE 057 /* MIPS-II, for r4000 port */ +#if __mips_isa_rev < 6 +#define OP_SDL 054 +#define OP_SDR 055 +#define OP_SWR 056 +#define OP_CACHE 057 #define OP_LL 060 -#define OP_LWC0 OP_LL /* backwards source compatibility */ +#define OP_LWC0 OP_LL #define OP_LWC1 061 #define OP_LWC2 062 #define OP_LWC3 063 -#define OP_LLD 064 /* MIPS-II, for r4000 port */ +#define OP_LLD 064 +#else +#define OP_LWC1 061 +#define OP_BC 062 +#endif + #define OP_LDC1 065 -#define OP_LD 067 /* MIPS-II, for r4000 port */ +#define OP_LD 067 +#if __mips_isa_rev < 6 #define OP_SC 070 -#define OP_SWC0 OP_SC /* backwards source compatibility */ +#define OP_SWC0 OP_SC +#endif + #define OP_SWC1 071 + +#if __mips_isa_rev < 6 #define OP_SWC2 072 #define OP_SWC3 073 -#define OP_SCD 074 /* MIPS-II, for r4000 port */ +#define OP_SCD 074 +#else +#define OP_BALC 072 +#endif + #define OP_SDC1 075 -#define OP_SD 077 /* MIPS-II, for r4000 port */ +#define OP_SD 077 /* * Values for the 'func' field when 'op' == OP_SPECIAL. @@ -199,28 +248,50 @@ typedef union { #define OP_SRLV 006 #define OP_SRAV 007 +#if __mips_isa_rev < 6 #define OP_JR 010 +#endif + #define OP_JALR 011 #define OP_SYSCALL 014 #define OP_BREAK 015 -#define OP_SYNC 017 /* MIPS-II, for r4000 port */ +#define OP_SYNC 017 +#if __mips_isa_rev < 6 #define OP_MFHI 020 #define OP_MTHI 021 #define OP_MFLO 022 #define OP_MTLO 023 -#define OP_DSLLV 024 /* MIPS-II, for r4000 port */ -#define OP_DSRLV 026 /* MIPS-II, for r4000 port */ -#define OP_DSRAV 027 /* MIPS-II, for r4000 port */ +#else +#define OP_CLZ 020 +#define OP_CLO 021 +#define OP_DCLZ 022 +#define OP_DCLO 023 +#endif + +#define OP_DSLLV 024 +#define OP_DSRLV 026 +#define OP_DSRAV 027 +#if __mips_isa_rev < 6 #define OP_MULT 030 #define OP_MULTU 031 #define OP_DIV 032 #define OP_DIVU 033 -#define OP_DMULT 034 /* MIPS-II, for r4000 port */ -#define OP_DMULTU 035 /* MIPS-II, for r4000 port */ -#define OP_DDIV 036 /* MIPS-II, for r4000 port */ -#define OP_DDIVU 037 /* MIPS-II, for r4000 port */ +#define OP_DMULT 034 +#define OP_DMULTU 035 +#define OP_DDIV 036 +#define OP_DDIVU 037 +#else +#define OP_SOP30 030 +#define OP_SOP31 031 +#define OP_SOP32 032 +#define OP_SOP33 033 +#define OP_SOP34 034 +#define OP_SOP35 035 +#define OP_SOP36 036 +#define OP_SOP37 037 +#endif #define OP_ADD 040 #define OP_ADDU 041 @@ -233,73 +304,96 @@ typedef union { #define OP_SLT 052 #define OP_SLTU 053 -#define OP_DADD 054 /* MIPS-II, for r4000 port */ -#define OP_DADDU 055 /* MIPS-II, for r4000 port */ -#define OP_DSUB 056 /* MIPS-II, for r4000 port */ -#define OP_DSUBU 057 /* MIPS-II, for r4000 port */ - -#define OP_TGE 060 /* MIPS-II, for r4000 port */ -#define OP_TGEU 061 /* MIPS-II, for r4000 port */ -#define OP_TLT 062 /* MIPS-II, for r4000 port */ -#define OP_TLTU 063 /* MIPS-II, for r4000 port */ -#define OP_TEQ 064 /* MIPS-II, for r4000 port */ -#define OP_TNE 066 /* MIPS-II, for r4000 port */ - -#define OP_DSLL 070 /* MIPS-II, for r4000 port */ -#define OP_DSRL 072 /* MIPS-II, for r4000 port */ -#define OP_DSRA 073 /* MIPS-II, for r4000 port */ -#define OP_DSLL32 074 /* MIPS-II, for r4000 port */ -#define OP_DSRL32 076 /* MIPS-II, for r4000 port */ -#define OP_DSRA32 077 /* MIPS-II, for r4000 port */ - +#define OP_DADD 054 +#define OP_DADDU 055 +#define OP_DSUB 056 +#define OP_DSUBU 057 + +#define OP_TGE 060 +#define OP_TGEU 061 +#define OP_TLT 062 +#define OP_TLTU 063 +#define OP_TEQ 064 +#define OP_TNE 066 + +#define OP_DSLL 070 +#define OP_DSRL 072 +#define OP_DSRA 073 +#define OP_DSLL32 074 +#define OP_DSRL32 076 +#define OP_DSRA32 077 + +#if __mips_isa_rev < 6 /* * Values for the 'func' field when 'op' == OP_SPECIAL2. + * OP_SPECIAL2 opcodes are removed in mips32r6 */ #define OP_MAD 000 /* QED */ #define OP_MADU 001 /* QED */ #define OP_MUL 002 /* QED */ +#endif /* * Values for the 'func' field when 'op' == OP_SPECIAL3. */ #define OP_EXT 000 +#define OP_DEXTM 001 +#define OP_DEXTU 002 +#define OP_DEXT 003 #define OP_INS 004 +#define OP_DINSM 005 +#define OP_DINSU 006 +#define OP_DINS 007 #define OP_BSHFL 040 +#define OP_RDHWR 073 /* * Values for the 'shamt' field when OP_SPECIAL3 && func OP_BSHFL. */ + #define OP_WSBH 002 #define OP_SEB 020 #define OP_SEH 030 +#if __mips_isa_rev == 6 +/* + * Values for the 'shamt' field when OP_SOP30. + */ +#define OP_MUL 002 +#define OP_MUH 003 +#endif + /* * Values for the 'func' field when 'op' == OP_BCOND. */ #define OP_BLTZ 000 #define OP_BGEZ 001 -#define OP_BLTZL 002 /* MIPS-II, for r4000 port */ -#define OP_BGEZL 003 /* MIPS-II, for r4000 port */ -#define OP_TGEI 010 /* MIPS-II, for r4000 port */ -#define OP_TGEIU 011 /* MIPS-II, for r4000 port */ -#define OP_TLTI 012 /* MIPS-II, for r4000 port */ -#define OP_TLTIU 013 /* MIPS-II, for r4000 port */ -#define OP_TEQI 014 /* MIPS-II, for r4000 port */ -#define OP_TNEI 016 /* MIPS-II, for r4000 port */ - -#define OP_BLTZAL 020 /* MIPS-II, for r4000 port */ +#if __mips_isa_rev < 6 +#define OP_BLTZL 002 +#define OP_BGEZL 003 +#define OP_TGEI 010 +#define OP_TGEIU 011 +#define OP_TLTI 012 +#define OP_TLTIU 013 +#define OP_TEQI 014 +#define OP_TNEI 016 +#define OP_BLTZAL 020 #define OP_BGEZAL 021 #define OP_BLTZALL 022 #define OP_BGEZALL 023 +#else +#define OP_NAL 020 +#define OP_BAL 021 +#endif /* * Values for the 'rs' field when 'op' == OP_COPz. */ #define OP_MF 000 -#define OP_DMF 001 /* MIPS-II, for r4000 port */ +#define OP_DMF 001 #define OP_MT 004 -#define OP_DMT 005 /* MIPS-II, for r4000 port */ +#define OP_DMT 005 #define OP_BCx 010 #define OP_BCy 014 #define OP_CF 002 @@ -311,6 +405,6 @@ typedef union { #define COPz_BC_TF_MASK 0x01 #define COPz_BC_TRUE 0x01 #define COPz_BC_FALSE 0x00 -#define COPz_BCL_TF_MASK 0x02 /* MIPS-II, for r4000 port */ -#define COPz_BCL_TRUE 0x02 /* MIPS-II, for r4000 port */ -#define COPz_BCL_FALSE 0x00 /* MIPS-II, for r4000 port */ +#define COPz_BCL_TF_MASK 0x02 +#define COPz_BCL_TRUE 0x02 +#define COPz_BCL_FALSE 0x00 diff --git a/libpixelflinger/codeflinger/tinyutils/Errors.h b/libpixelflinger/codeflinger/tinyutils/Errors.h deleted file mode 100644 index 47ae9d79e..000000000 --- a/libpixelflinger/codeflinger/tinyutils/Errors.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2007 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. - */ - -#ifndef ANDROID_PIXELFLINGER_ERRORS_H -#define ANDROID_PIXELFLINGER_ERRORS_H - -#include <sys/types.h> -#include <errno.h> - -namespace android { -namespace tinyutils { - -// use this type to return error codes -typedef int32_t status_t; - -/* - * Error codes. - * All error codes are negative values. - */ - -enum { - NO_ERROR = 0, // No errors. - NO_MEMORY = -ENOMEM, - BAD_VALUE = -EINVAL, - BAD_INDEX = -EOVERFLOW, - NAME_NOT_FOUND = -ENOENT, -}; - - -} // namespace tinyutils -} // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_PIXELFLINGER_ERRORS_H diff --git a/libpixelflinger/codeflinger/tinyutils/KeyedVector.h b/libpixelflinger/codeflinger/tinyutils/KeyedVector.h deleted file mode 100644 index 9d8668b9c..000000000 --- a/libpixelflinger/codeflinger/tinyutils/KeyedVector.h +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright 2005 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. - */ - -#ifndef ANDROID_PIXELFLINGER_KEYED_VECTOR_H -#define ANDROID_PIXELFLINGER_KEYED_VECTOR_H - -#include <assert.h> -#include <stdint.h> -#include <sys/types.h> - -#include "Errors.h" -#include "SortedVector.h" -#include "TypeHelpers.h" - -// --------------------------------------------------------------------------- - -namespace android { -namespace tinyutils { - -template <typename KEY, typename VALUE> -class KeyedVector -{ -public: - typedef KEY key_type; - typedef VALUE value_type; - - inline KeyedVector(); - - /* - * empty the vector - */ - - inline void clear() { mVector.clear(); } - - /*! - * vector stats - */ - - //! returns number of items in the vector - inline size_t size() const { return mVector.size(); } - //! returns wether or not the vector is empty - inline bool isEmpty() const { return mVector.isEmpty(); } - //! returns how many items can be stored without reallocating the backing store - inline size_t capacity() const { return mVector.capacity(); } - //! setst the capacity. capacity can never be reduced less than size() - inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); } - - /*! - * accessors - */ - const VALUE& valueFor(const KEY& key) const; - const VALUE& valueAt(size_t index) const; - const KEY& keyAt(size_t index) const; - ssize_t indexOfKey(const KEY& key) const; - - /*! - * modifing the array - */ - - VALUE& editValueFor(const KEY& key); - VALUE& editValueAt(size_t index); - - /*! - * add/insert/replace items - */ - - ssize_t add(const KEY& key, const VALUE& item); - ssize_t replaceValueFor(const KEY& key, const VALUE& item); - ssize_t replaceValueAt(size_t index, const VALUE& item); - - /*! - * remove items - */ - - ssize_t removeItem(const KEY& key); - ssize_t removeItemsAt(size_t index, size_t count = 1); - -private: - SortedVector< key_value_pair_t<KEY, VALUE> > mVector; -}; - -// --------------------------------------------------------------------------- - -/** - * Variation of KeyedVector that holds a default value to return when - * valueFor() is called with a key that doesn't exist. - */ -template <typename KEY, typename VALUE> -class DefaultKeyedVector : public KeyedVector<KEY, VALUE> -{ -public: - inline DefaultKeyedVector(const VALUE& defValue = VALUE()); - const VALUE& valueFor(const KEY& key) const; - -private: - VALUE mDefault; -}; - -// --------------------------------------------------------------------------- - -template<typename KEY, typename VALUE> inline -KeyedVector<KEY,VALUE>::KeyedVector() -{ -} - -template<typename KEY, typename VALUE> inline -ssize_t KeyedVector<KEY,VALUE>::indexOfKey(const KEY& key) const { - return mVector.indexOf( key_value_pair_t<KEY,VALUE>(key) ); -} - -template<typename KEY, typename VALUE> inline -const VALUE& KeyedVector<KEY,VALUE>::valueFor(const KEY& key) const { - ssize_t i = indexOfKey(key); - assert(i>=0); - return mVector.itemAt(i).value; -} - -template<typename KEY, typename VALUE> inline -const VALUE& KeyedVector<KEY,VALUE>::valueAt(size_t index) const { - return mVector.itemAt(index).value; -} - -template<typename KEY, typename VALUE> inline -const KEY& KeyedVector<KEY,VALUE>::keyAt(size_t index) const { - return mVector.itemAt(index).key; -} - -template<typename KEY, typename VALUE> inline -VALUE& KeyedVector<KEY,VALUE>::editValueFor(const KEY& key) { - ssize_t i = indexOfKey(key); - assert(i>=0); - return mVector.editItemAt(i).value; -} - -template<typename KEY, typename VALUE> inline -VALUE& KeyedVector<KEY,VALUE>::editValueAt(size_t index) { - return mVector.editItemAt(index).value; -} - -template<typename KEY, typename VALUE> inline -ssize_t KeyedVector<KEY,VALUE>::add(const KEY& key, const VALUE& value) { - return mVector.add( key_value_pair_t<KEY,VALUE>(key, value) ); -} - -template<typename KEY, typename VALUE> inline -ssize_t KeyedVector<KEY,VALUE>::replaceValueFor(const KEY& key, const VALUE& value) { - key_value_pair_t<KEY,VALUE> pair(key, value); - mVector.remove(pair); - return mVector.add(pair); -} - -template<typename KEY, typename VALUE> inline -ssize_t KeyedVector<KEY,VALUE>::replaceValueAt(size_t index, const VALUE& item) { - if (index<size()) { - mVector.editValueAt(index).value = item; - return index; - } - return BAD_INDEX; -} - -template<typename KEY, typename VALUE> inline -ssize_t KeyedVector<KEY,VALUE>::removeItem(const KEY& key) { - return mVector.remove(key_value_pair_t<KEY,VALUE>(key)); -} - -template<typename KEY, typename VALUE> inline -ssize_t KeyedVector<KEY, VALUE>::removeItemsAt(size_t index, size_t count) { - return mVector.removeItemsAt(index, count); -} - -// --------------------------------------------------------------------------- - -template<typename KEY, typename VALUE> inline -DefaultKeyedVector<KEY,VALUE>::DefaultKeyedVector(const VALUE& defValue) - : mDefault(defValue) -{ -} - -template<typename KEY, typename VALUE> inline -const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const { - ssize_t i = indexOfKey(key); - return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault; -} - -} // namespace tinyutils -} // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_PIXELFLINGER_KEYED_VECTOR_H diff --git a/libpixelflinger/codeflinger/tinyutils/SharedBuffer.cpp b/libpixelflinger/codeflinger/tinyutils/SharedBuffer.cpp deleted file mode 100644 index ef453fa6c..000000000 --- a/libpixelflinger/codeflinger/tinyutils/SharedBuffer.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2005 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 <stdlib.h> -#include <string.h> - -#include <cutils/atomic.h> - -#include "SharedBuffer.h" - -// --------------------------------------------------------------------------- - -namespace android { -namespace tinyutils { - -SharedBuffer* SharedBuffer::alloc(size_t size) -{ - SharedBuffer* sb = static_cast<SharedBuffer *>(malloc(sizeof(SharedBuffer) + size)); - if (sb) { - sb->mRefs = 1; - sb->mSize = size; - } - return sb; -} - - -ssize_t SharedBuffer::dealloc(const SharedBuffer* released) -{ - if (released->mRefs != 0) return -1; // XXX: invalid operation - free(const_cast<SharedBuffer*>(released)); - return 0; -} - -SharedBuffer* SharedBuffer::edit() const -{ - if (onlyOwner()) { - return const_cast<SharedBuffer*>(this); - } - SharedBuffer* sb = alloc(mSize); - if (sb) { - memcpy(sb->data(), data(), size()); - release(); - } - return sb; -} - -SharedBuffer* SharedBuffer::editResize(size_t newSize) const -{ - if (onlyOwner()) { - SharedBuffer* buf = const_cast<SharedBuffer*>(this); - if (buf->mSize == newSize) return buf; - buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize); - if (buf != NULL) { - buf->mSize = newSize; - return buf; - } - } - SharedBuffer* sb = alloc(newSize); - if (sb) { - const size_t mySize = mSize; - memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize); - release(); - } - return sb; -} - -SharedBuffer* SharedBuffer::attemptEdit() const -{ - if (onlyOwner()) { - return const_cast<SharedBuffer*>(this); - } - return 0; -} - -SharedBuffer* SharedBuffer::reset(size_t new_size) const -{ - // cheap-o-reset. - SharedBuffer* sb = alloc(new_size); - if (sb) { - release(); - } - return sb; -} - -void SharedBuffer::acquire() const { - android_atomic_inc(&mRefs); -} - -int32_t SharedBuffer::release(uint32_t flags) const -{ - int32_t prev = 1; - if (onlyOwner() || ((prev = android_atomic_dec(&mRefs)) == 1)) { - mRefs = 0; - if ((flags & eKeepStorage) == 0) { - free(const_cast<SharedBuffer*>(this)); - } - } - return prev; -} - -} // namespace tinyutils -} // namespace android diff --git a/libpixelflinger/codeflinger/tinyutils/SharedBuffer.h b/libpixelflinger/codeflinger/tinyutils/SharedBuffer.h deleted file mode 100644 index d69b4179b..000000000 --- a/libpixelflinger/codeflinger/tinyutils/SharedBuffer.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2005 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. - */ - -#ifndef ANDROID_PIXELFLINGER_SHARED_BUFFER_H -#define ANDROID_PIXELFLINGER_SHARED_BUFFER_H - -#include <stdint.h> -#include <sys/types.h> - -// --------------------------------------------------------------------------- - -namespace android { -namespace tinyutils { - -class SharedBuffer -{ -public: - - /* flags to use with release() */ - enum { - eKeepStorage = 0x00000001 - }; - - /*! allocate a buffer of size 'size' and acquire() it. - * call release() to free it. - */ - static SharedBuffer* alloc(size_t size); - - /*! free the memory associated with the SharedBuffer. - * Fails if there are any users associated with this SharedBuffer. - * In other words, the buffer must have been release by all its - * users. - */ - static ssize_t dealloc(const SharedBuffer* released); - - //! get the SharedBuffer from the data pointer - static inline const SharedBuffer* sharedBuffer(const void* data); - - //! access the data for read - inline const void* data() const; - - //! access the data for read/write - inline void* data(); - - //! get size of the buffer - inline size_t size() const; - - //! get back a SharedBuffer object from its data - static inline SharedBuffer* bufferFromData(void* data); - - //! get back a SharedBuffer object from its data - static inline const SharedBuffer* bufferFromData(const void* data); - - //! get the size of a SharedBuffer object from its data - static inline size_t sizeFromData(const void* data); - - //! edit the buffer (get a writtable, or non-const, version of it) - SharedBuffer* edit() const; - - //! edit the buffer, resizing if needed - SharedBuffer* editResize(size_t size) const; - - //! like edit() but fails if a copy is required - SharedBuffer* attemptEdit() const; - - //! resize and edit the buffer, loose it's content. - SharedBuffer* reset(size_t size) const; - - //! acquire/release a reference on this buffer - void acquire() const; - - /*! release a reference on this buffer, with the option of not - * freeing the memory associated with it if it was the last reference - * returns the previous reference count - */ - int32_t release(uint32_t flags = 0) const; - - //! returns wether or not we're the only owner - inline bool onlyOwner() const; - - -private: - inline SharedBuffer() { } - inline ~SharedBuffer() { } - inline SharedBuffer(const SharedBuffer&); - - // 16 bytes. must be sized to preserve correct alingment. - mutable int32_t mRefs; - size_t mSize; - uint32_t mReserved[2]; -}; - -// --------------------------------------------------------------------------- - -const SharedBuffer* SharedBuffer::sharedBuffer(const void* data) { - return data ? reinterpret_cast<const SharedBuffer *>(data)-1 : 0; -} - -const void* SharedBuffer::data() const { - return this + 1; -} - -void* SharedBuffer::data() { - return this + 1; -} - -size_t SharedBuffer::size() const { - return mSize; -} - -SharedBuffer* SharedBuffer::bufferFromData(void* data) -{ - return ((SharedBuffer*)data)-1; -} - -const SharedBuffer* SharedBuffer::bufferFromData(const void* data) -{ - return ((const SharedBuffer*)data)-1; -} - -size_t SharedBuffer::sizeFromData(const void* data) -{ - return (((const SharedBuffer*)data)-1)->mSize; -} - -bool SharedBuffer::onlyOwner() const { - return (mRefs == 1); -} - -} // namespace tinyutils -} // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_PIXELFLINGER_SHARED_BUFFER_H diff --git a/libpixelflinger/codeflinger/tinyutils/SortedVector.h b/libpixelflinger/codeflinger/tinyutils/SortedVector.h deleted file mode 100644 index a2b700542..000000000 --- a/libpixelflinger/codeflinger/tinyutils/SortedVector.h +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright 2005 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. - */ - -#ifndef ANDROID_PIXELFLINGER_SORTED_VECTOR_H -#define ANDROID_PIXELFLINGER_SORTED_VECTOR_H - -#include <assert.h> -#include <stdint.h> -#include <sys/types.h> - -#include "Vector.h" -#include "VectorImpl.h" -#include "TypeHelpers.h" - -// --------------------------------------------------------------------------- - -namespace android { -namespace tinyutils { - -template <class TYPE> -class SortedVector : private SortedVectorImpl -{ -public: - typedef TYPE value_type; - - /*! - * Constructors and destructors - */ - - SortedVector(); - SortedVector(const SortedVector<TYPE>& rhs); - virtual ~SortedVector(); - - /*! copy operator */ - const SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const; - SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs); - - /* - * empty the vector - */ - - inline void clear() { VectorImpl::clear(); } - - /*! - * vector stats - */ - - //! returns number of items in the vector - inline size_t size() const { return VectorImpl::size(); } - //! returns wether or not the vector is empty - inline bool isEmpty() const { return VectorImpl::isEmpty(); } - //! returns how many items can be stored without reallocating the backing store - inline size_t capacity() const { return VectorImpl::capacity(); } - //! setst the capacity. capacity can never be reduced less than size() - inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } - - /*! - * C-style array access - */ - - //! read-only C-style access - inline const TYPE* array() const; - - //! read-write C-style access. BE VERY CAREFUL when modifying the array - //! you ust keep it sorted! You usually don't use this function. - TYPE* editArray(); - - //! finds the index of an item - ssize_t indexOf(const TYPE& item) const; - - //! finds where this item should be inserted - size_t orderOf(const TYPE& item) const; - - - /*! - * accessors - */ - - //! read-only access to an item at a given index - inline const TYPE& operator [] (size_t index) const; - //! alternate name for operator [] - inline const TYPE& itemAt(size_t index) const; - //! stack-usage of the vector. returns the top of the stack (last element) - const TYPE& top() const; - //! same as operator [], but allows to access the vector backward (from the end) with a negative index - const TYPE& mirrorItemAt(ssize_t index) const; - - /*! - * modifing the array - */ - - //! add an item in the right place (and replace the one that is there) - ssize_t add(const TYPE& item); - - //! editItemAt() MUST NOT change the order of this item - TYPE& editItemAt(size_t index) { - return *( static_cast<TYPE *>(VectorImpl::editItemLocation(index)) ); - } - - //! merges a vector into this one - ssize_t merge(const Vector<TYPE>& vector); - ssize_t merge(const SortedVector<TYPE>& vector); - - //! removes an item - ssize_t remove(const TYPE&); - - //! remove several items - inline ssize_t removeItemsAt(size_t index, size_t count = 1); - //! remove one item - inline ssize_t removeAt(size_t index) { return removeItemsAt(index); } - -protected: - virtual void do_construct(void* storage, size_t num) const; - virtual void do_destroy(void* storage, size_t num) const; - virtual void do_copy(void* dest, const void* from, size_t num) const; - virtual void do_splat(void* dest, const void* item, size_t num) const; - virtual void do_move_forward(void* dest, const void* from, size_t num) const; - virtual void do_move_backward(void* dest, const void* from, size_t num) const; - virtual int do_compare(const void* lhs, const void* rhs) const; -}; - - -// --------------------------------------------------------------------------- -// No user serviceable parts from here... -// --------------------------------------------------------------------------- - -template<class TYPE> inline -SortedVector<TYPE>::SortedVector() - : SortedVectorImpl(sizeof(TYPE), - ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) - |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) - |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0) - |(traits<TYPE>::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0)) - ) -{ -} - -template<class TYPE> inline -SortedVector<TYPE>::SortedVector(const SortedVector<TYPE>& rhs) - : SortedVectorImpl(rhs) { -} - -template<class TYPE> inline -SortedVector<TYPE>::~SortedVector() { - finish_vector(); -} - -template<class TYPE> inline -SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) { - SortedVectorImpl::operator = (rhs); - return *this; -} - -template<class TYPE> inline -const SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const { - SortedVectorImpl::operator = (rhs); - return *this; -} - -template<class TYPE> inline -const TYPE* SortedVector<TYPE>::array() const { - return static_cast<const TYPE *>(arrayImpl()); -} - -template<class TYPE> inline -TYPE* SortedVector<TYPE>::editArray() { - return static_cast<TYPE *>(editArrayImpl()); -} - - -template<class TYPE> inline -const TYPE& SortedVector<TYPE>::operator[](size_t index) const { - assert( index<size() ); - return *(array() + index); -} - -template<class TYPE> inline -const TYPE& SortedVector<TYPE>::itemAt(size_t index) const { - return operator[](index); -} - -template<class TYPE> inline -const TYPE& SortedVector<TYPE>::mirrorItemAt(ssize_t index) const { - assert( (index>0 ? index : -index)<size() ); - return *(array() + ((index<0) ? (size()-index) : index)); -} - -template<class TYPE> inline -const TYPE& SortedVector<TYPE>::top() const { - return *(array() + size() - 1); -} - -template<class TYPE> inline -ssize_t SortedVector<TYPE>::add(const TYPE& item) { - return SortedVectorImpl::add(&item); -} - -template<class TYPE> inline -ssize_t SortedVector<TYPE>::indexOf(const TYPE& item) const { - return SortedVectorImpl::indexOf(&item); -} - -template<class TYPE> inline -size_t SortedVector<TYPE>::orderOf(const TYPE& item) const { - return SortedVectorImpl::orderOf(&item); -} - -template<class TYPE> inline -ssize_t SortedVector<TYPE>::merge(const Vector<TYPE>& vector) { - return SortedVectorImpl::merge(reinterpret_cast<const VectorImpl&>(vector)); -} - -template<class TYPE> inline -ssize_t SortedVector<TYPE>::merge(const SortedVector<TYPE>& vector) { - return SortedVectorImpl::merge(reinterpret_cast<const SortedVectorImpl&>(vector)); -} - -template<class TYPE> inline -ssize_t SortedVector<TYPE>::remove(const TYPE& item) { - return SortedVectorImpl::remove(&item); -} - -template<class TYPE> inline -ssize_t SortedVector<TYPE>::removeItemsAt(size_t index, size_t count) { - return VectorImpl::removeItemsAt(index, count); -} - -// --------------------------------------------------------------------------- - -template<class TYPE> -void SortedVector<TYPE>::do_construct(void* storage, size_t num) const { - construct_type( reinterpret_cast<TYPE*>(storage), num ); -} - -template<class TYPE> -void SortedVector<TYPE>::do_destroy(void* storage, size_t num) const { - destroy_type( reinterpret_cast<TYPE*>(storage), num ); -} - -template<class TYPE> -void SortedVector<TYPE>::do_copy(void* dest, const void* from, size_t num) const { - copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); -} - -template<class TYPE> -void SortedVector<TYPE>::do_splat(void* dest, const void* item, size_t num) const { - splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num ); -} - -template<class TYPE> -void SortedVector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const { - move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); -} - -template<class TYPE> -void SortedVector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const { - move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); -} - -template<class TYPE> -int SortedVector<TYPE>::do_compare(const void* lhs, const void* rhs) const { - return compare_type( *reinterpret_cast<const TYPE*>(lhs), *reinterpret_cast<const TYPE*>(rhs) ); -} - -} // namespace tinyutils -} // namespace android - - -// --------------------------------------------------------------------------- - -#endif // ANDROID_PIXELFLINGER_SORTED_VECTOR_H diff --git a/libpixelflinger/codeflinger/tinyutils/TypeHelpers.h b/libpixelflinger/codeflinger/tinyutils/TypeHelpers.h deleted file mode 100644 index 7abff072f..000000000 --- a/libpixelflinger/codeflinger/tinyutils/TypeHelpers.h +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright 2005 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. - */ - -#ifndef ANDROID_PIXELFLINGER_TYPE_HELPERS_H -#define ANDROID_PIXELFLINGER_TYPE_HELPERS_H - -#include <new> -#include <stdint.h> -#include <string.h> -#include <sys/types.h> - -// --------------------------------------------------------------------------- - -namespace android { -namespace tinyutils { - -/* - * Types traits - */ - -template <typename T> struct trait_trivial_ctor { enum { value = false }; }; -template <typename T> struct trait_trivial_dtor { enum { value = false }; }; -template <typename T> struct trait_trivial_copy { enum { value = false }; }; -template <typename T> struct trait_trivial_assign{ enum { value = false }; }; - -template <typename T> struct trait_pointer { enum { value = false }; }; -template <typename T> struct trait_pointer<T*> { enum { value = true }; }; - -#define ANDROID_BASIC_TYPES_TRAITS( T ) \ - template<> struct trait_trivial_ctor< T > { enum { value = true }; }; \ - template<> struct trait_trivial_dtor< T > { enum { value = true }; }; \ - template<> struct trait_trivial_copy< T > { enum { value = true }; }; \ - template<> struct trait_trivial_assign< T >{ enum { value = true }; }; - -#define ANDROID_TYPE_TRAITS( T, ctor, dtor, copy, assign ) \ - template<> struct trait_trivial_ctor< T > { enum { value = ctor }; }; \ - template<> struct trait_trivial_dtor< T > { enum { value = dtor }; }; \ - template<> struct trait_trivial_copy< T > { enum { value = copy }; }; \ - template<> struct trait_trivial_assign< T >{ enum { value = assign }; }; - -template <typename TYPE> -struct traits { - enum { - is_pointer = trait_pointer<TYPE>::value, - has_trivial_ctor = is_pointer || trait_trivial_ctor<TYPE>::value, - has_trivial_dtor = is_pointer || trait_trivial_dtor<TYPE>::value, - has_trivial_copy = is_pointer || trait_trivial_copy<TYPE>::value, - has_trivial_assign = is_pointer || trait_trivial_assign<TYPE>::value - }; -}; - -template <typename T, typename U> -struct aggregate_traits { - enum { - is_pointer = false, - has_trivial_ctor = traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor, - has_trivial_dtor = traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor, - has_trivial_copy = traits<T>::has_trivial_copy && traits<U>::has_trivial_copy, - has_trivial_assign = traits<T>::has_trivial_assign && traits<U>::has_trivial_assign - }; -}; - -// --------------------------------------------------------------------------- - -/* - * basic types traits - */ - -ANDROID_BASIC_TYPES_TRAITS( void ); -ANDROID_BASIC_TYPES_TRAITS( bool ); -ANDROID_BASIC_TYPES_TRAITS( char ); -ANDROID_BASIC_TYPES_TRAITS( unsigned char ); -ANDROID_BASIC_TYPES_TRAITS( short ); -ANDROID_BASIC_TYPES_TRAITS( unsigned short ); -ANDROID_BASIC_TYPES_TRAITS( int ); -ANDROID_BASIC_TYPES_TRAITS( unsigned int ); -ANDROID_BASIC_TYPES_TRAITS( long ); -ANDROID_BASIC_TYPES_TRAITS( unsigned long ); -ANDROID_BASIC_TYPES_TRAITS( long long ); -ANDROID_BASIC_TYPES_TRAITS( unsigned long long ); -ANDROID_BASIC_TYPES_TRAITS( float ); -ANDROID_BASIC_TYPES_TRAITS( double ); - -// --------------------------------------------------------------------------- - - -/* - * compare and order types - */ - -template<typename TYPE> inline -int strictly_order_type(const TYPE& lhs, const TYPE& rhs) { - return (lhs < rhs) ? 1 : 0; -} - -template<typename TYPE> inline -int compare_type(const TYPE& lhs, const TYPE& rhs) { - return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs); -} - -/* - * create, destroy, copy and assign types... - */ - -template<typename TYPE> inline -void construct_type(TYPE* p, size_t n) { - if (!traits<TYPE>::has_trivial_ctor) { - while (n--) { - new(p++) TYPE; - } - } -} - -template<typename TYPE> inline -void destroy_type(TYPE* p, size_t n) { - if (!traits<TYPE>::has_trivial_dtor) { - while (n--) { - p->~TYPE(); - p++; - } - } -} - -template<typename TYPE> inline -void copy_type(TYPE* d, const TYPE* s, size_t n) { - if (!traits<TYPE>::has_trivial_copy) { - while (n--) { - new(d) TYPE(*s); - d++, s++; - } - } else { - memcpy(d,s,n*sizeof(TYPE)); - } -} - -template<typename TYPE> inline -void assign_type(TYPE* d, const TYPE* s, size_t n) { - if (!traits<TYPE>::has_trivial_assign) { - while (n--) { - *d++ = *s++; - } - } else { - memcpy(d,s,n*sizeof(TYPE)); - } -} - -template<typename TYPE> inline -void splat_type(TYPE* where, const TYPE* what, size_t n) { - if (!traits<TYPE>::has_trivial_copy) { - while (n--) { - new(where) TYPE(*what); - where++; - } - } else { - while (n--) { - *where++ = *what; - } - } -} - -template<typename TYPE> inline -void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) { - if (!traits<TYPE>::has_trivial_copy || !traits<TYPE>::has_trivial_dtor) { - d += n; - s += n; - while (n--) { - --d, --s; - if (!traits<TYPE>::has_trivial_copy) { - new(d) TYPE(*s); - } else { - *d = *s; - } - if (!traits<TYPE>::has_trivial_dtor) { - s->~TYPE(); - } - } - } else { - memmove(d,s,n*sizeof(TYPE)); - } -} - -template<typename TYPE> inline -void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) { - if (!traits<TYPE>::has_trivial_copy || !traits<TYPE>::has_trivial_dtor) { - while (n--) { - if (!traits<TYPE>::has_trivial_copy) { - new(d) TYPE(*s); - } else { - *d = *s; - } - if (!traits<TYPE>::has_trivial_dtor) { - s->~TYPE(); - } - d++, s++; - } - } else { - memmove(d,s,n*sizeof(TYPE)); - } -} -// --------------------------------------------------------------------------- - -/* - * a key/value pair - */ - -template <typename KEY, typename VALUE> -struct key_value_pair_t { - KEY key; - VALUE value; - key_value_pair_t() { } - key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { } - key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { } - key_value_pair_t(const KEY& k) : key(k) { } - inline bool operator < (const key_value_pair_t& o) const { - return strictly_order_type(key, o.key); - } -}; - -template<> -template <typename K, typename V> -struct trait_trivial_ctor< key_value_pair_t<K, V> > -{ enum { value = aggregate_traits<K,V>::has_trivial_ctor }; }; -template<> -template <typename K, typename V> -struct trait_trivial_dtor< key_value_pair_t<K, V> > -{ enum { value = aggregate_traits<K,V>::has_trivial_dtor }; }; -template<> -template <typename K, typename V> -struct trait_trivial_copy< key_value_pair_t<K, V> > -{ enum { value = aggregate_traits<K,V>::has_trivial_copy }; }; -template<> -template <typename K, typename V> -struct trait_trivial_assign< key_value_pair_t<K, V> > -{ enum { value = aggregate_traits<K,V>::has_trivial_assign};}; - -// --------------------------------------------------------------------------- - -} // namespace tinyutils -} // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_PIXELFLINGER_TYPE_HELPERS_H diff --git a/libpixelflinger/codeflinger/tinyutils/Vector.h b/libpixelflinger/codeflinger/tinyutils/Vector.h deleted file mode 100644 index c07a17aa5..000000000 --- a/libpixelflinger/codeflinger/tinyutils/Vector.h +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright 2005 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. - */ - -#ifndef ANDROID_PIXELFLINGER_VECTOR_H -#define ANDROID_PIXELFLINGER_VECTOR_H - -#include <new> -#include <stdint.h> -#include <sys/types.h> - -#include <cutils/log.h> - -#include "Errors.h" -#include "VectorImpl.h" -#include "TypeHelpers.h" - -// --------------------------------------------------------------------------- - -namespace android { -namespace tinyutils { - -/*! - * The main templated vector class ensuring type safety - * while making use of VectorImpl. - * This is the class users want to use. - */ - -template <class TYPE> -class Vector : private VectorImpl -{ -public: - typedef TYPE value_type; - - /*! - * Constructors and destructors - */ - - Vector(); - Vector(const Vector<TYPE>& rhs); - virtual ~Vector(); - - /*! copy operator */ - const Vector<TYPE>& operator = (const Vector<TYPE>& rhs) const; - Vector<TYPE>& operator = (const Vector<TYPE>& rhs); - - /* - * empty the vector - */ - - inline void clear() { VectorImpl::clear(); } - - /*! - * vector stats - */ - - //! returns number of items in the vector - inline size_t size() const { return VectorImpl::size(); } - //! returns wether or not the vector is empty - inline bool isEmpty() const { return VectorImpl::isEmpty(); } - //! returns how many items can be stored without reallocating the backing store - inline size_t capacity() const { return VectorImpl::capacity(); } - //! setst the capacity. capacity can never be reduced less than size() - inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } - - /*! - * C-style array access - */ - - //! read-only C-style access - inline const TYPE* array() const; - //! read-write C-style access - TYPE* editArray(); - - /*! - * accessors - */ - - //! read-only access to an item at a given index - inline const TYPE& operator [] (size_t index) const; - //! alternate name for operator [] - inline const TYPE& itemAt(size_t index) const; - //! stack-usage of the vector. returns the top of the stack (last element) - const TYPE& top() const; - //! same as operator [], but allows to access the vector backward (from the end) with a negative index - const TYPE& mirrorItemAt(ssize_t index) const; - - /*! - * modifing the array - */ - - //! copy-on write support, grants write access to an item - TYPE& editItemAt(size_t index); - //! grants right acces to the top of the stack (last element) - TYPE& editTop(); - - /*! - * append/insert another vector - */ - - //! insert another vector at a given index - ssize_t insertVectorAt(const Vector<TYPE>& vector, size_t index); - - //! append another vector at the end of this one - ssize_t appendVector(const Vector<TYPE>& vector); - - - /*! - * add/insert/replace items - */ - - //! insert one or several items initialized with their default constructor - inline ssize_t insertAt(size_t index, size_t numItems = 1); - //! insert on onr several items initialized from a prototype item - ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1); - //! pop the top of the stack (removes the last element). No-op if the stack's empty - inline void pop(); - //! pushes an item initialized with its default constructor - inline void push(); - //! pushes an item on the top of the stack - void push(const TYPE& item); - //! same as push() but returns the index the item was added at (or an error) - inline ssize_t add(); - //! same as push() but returns the index the item was added at (or an error) - ssize_t add(const TYPE& item); - //! replace an item with a new one initialized with its default constructor - inline ssize_t replaceAt(size_t index); - //! replace an item with a new one - ssize_t replaceAt(const TYPE& item, size_t index); - - /*! - * remove items - */ - - //! remove several items - inline ssize_t removeItemsAt(size_t index, size_t count = 1); - //! remove one item - inline ssize_t removeAt(size_t index) { return removeItemsAt(index); } - - /*! - * sort (stable) the array - */ - - typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs); - typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state); - - inline status_t sort(compar_t cmp); - inline status_t sort(compar_r_t cmp, void* state); - -protected: - virtual void do_construct(void* storage, size_t num) const; - virtual void do_destroy(void* storage, size_t num) const; - virtual void do_copy(void* dest, const void* from, size_t num) const; - virtual void do_splat(void* dest, const void* item, size_t num) const; - virtual void do_move_forward(void* dest, const void* from, size_t num) const; - virtual void do_move_backward(void* dest, const void* from, size_t num) const; -}; - - -// --------------------------------------------------------------------------- -// No user serviceable parts from here... -// --------------------------------------------------------------------------- - -template<class TYPE> inline -Vector<TYPE>::Vector() - : VectorImpl(sizeof(TYPE), - ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) - |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) - |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0) - |(traits<TYPE>::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0)) - ) -{ -} - -template<class TYPE> inline -Vector<TYPE>::Vector(const Vector<TYPE>& rhs) - : VectorImpl(rhs) { -} - -template<class TYPE> inline -Vector<TYPE>::~Vector() { - finish_vector(); -} - -template<class TYPE> inline -Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) { - VectorImpl::operator = (rhs); - return *this; -} - -template<class TYPE> inline -const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const { - VectorImpl::operator = (rhs); - return *this; -} - -template<class TYPE> inline -const TYPE* Vector<TYPE>::array() const { - return static_cast<const TYPE *>(arrayImpl()); -} - -template<class TYPE> inline -TYPE* Vector<TYPE>::editArray() { - return static_cast<TYPE *>(editArrayImpl()); -} - - -template<class TYPE> inline -const TYPE& Vector<TYPE>::operator[](size_t index) const { - LOG_FATAL_IF( index>=size(), - "itemAt: index %d is past size %d", (int)index, (int)size() ); - return *(array() + index); -} - -template<class TYPE> inline -const TYPE& Vector<TYPE>::itemAt(size_t index) const { - return operator[](index); -} - -template<class TYPE> inline -const TYPE& Vector<TYPE>::mirrorItemAt(ssize_t index) const { - LOG_FATAL_IF( (index>0 ? index : -index)>=size(), - "mirrorItemAt: index %d is past size %d", - (int)index, (int)size() ); - return *(array() + ((index<0) ? (size()-index) : index)); -} - -template<class TYPE> inline -const TYPE& Vector<TYPE>::top() const { - return *(array() + size() - 1); -} - -template<class TYPE> inline -TYPE& Vector<TYPE>::editItemAt(size_t index) { - return *( static_cast<TYPE *>(editItemLocation(index)) ); -} - -template<class TYPE> inline -TYPE& Vector<TYPE>::editTop() { - return *( static_cast<TYPE *>(editItemLocation(size()-1)) ); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::insertVectorAt(const Vector<TYPE>& vector, size_t index) { - return VectorImpl::insertVectorAt(reinterpret_cast<const VectorImpl&>(vector), index); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) { - return VectorImpl::appendVector(reinterpret_cast<const VectorImpl&>(vector)); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) { - return VectorImpl::insertAt(&item, index, numItems); -} - -template<class TYPE> inline -void Vector<TYPE>::push(const TYPE& item) { - return VectorImpl::push(&item); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::add(const TYPE& item) { - return VectorImpl::add(&item); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::replaceAt(const TYPE& item, size_t index) { - return VectorImpl::replaceAt(&item, index); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::insertAt(size_t index, size_t numItems) { - return VectorImpl::insertAt(index, numItems); -} - -template<class TYPE> inline -void Vector<TYPE>::pop() { - VectorImpl::pop(); -} - -template<class TYPE> inline -void Vector<TYPE>::push() { - VectorImpl::push(); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::add() { - return VectorImpl::add(); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::replaceAt(size_t index) { - return VectorImpl::replaceAt(index); -} - -template<class TYPE> inline -ssize_t Vector<TYPE>::removeItemsAt(size_t index, size_t count) { - return VectorImpl::removeItemsAt(index, count); -} - -// --------------------------------------------------------------------------- - -template<class TYPE> -void Vector<TYPE>::do_construct(void* storage, size_t num) const { - construct_type( reinterpret_cast<TYPE*>(storage), num ); -} - -template<class TYPE> -void Vector<TYPE>::do_destroy(void* storage, size_t num) const { - destroy_type( reinterpret_cast<TYPE*>(storage), num ); -} - -template<class TYPE> -void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const { - copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); -} - -template<class TYPE> -void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const { - splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num ); -} - -template<class TYPE> -void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const { - move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); -} - -template<class TYPE> -void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const { - move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); -} - -} // namespace tinyutils -} // namespace android - - -// --------------------------------------------------------------------------- - -#endif // ANDROID_PIXELFLINGER_VECTOR_H diff --git a/libpixelflinger/codeflinger/tinyutils/VectorImpl.cpp b/libpixelflinger/codeflinger/tinyutils/VectorImpl.cpp deleted file mode 100644 index 689129a65..000000000 --- a/libpixelflinger/codeflinger/tinyutils/VectorImpl.cpp +++ /dev/null @@ -1,555 +0,0 @@ -/* - * Copyright 2005 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. - */ - -#define LOG_TAG "Vector" - -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <errno.h> - -#include <cutils/log.h> - -#include "Errors.h" -#include "SharedBuffer.h" -#include "VectorImpl.h" - -/*****************************************************************************/ - - -namespace android { -namespace tinyutils { - -// ---------------------------------------------------------------------------- - -const size_t kMinVectorCapacity = 4; - -static inline size_t max(size_t a, size_t b) { - return a>b ? a : b; -} - -// ---------------------------------------------------------------------------- - -VectorImpl::VectorImpl(size_t itemSize, uint32_t flags) - : mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize) -{ -} - -VectorImpl::VectorImpl(const VectorImpl& rhs) - : mStorage(rhs.mStorage), mCount(rhs.mCount), - mFlags(rhs.mFlags), mItemSize(rhs.mItemSize) -{ - if (mStorage) { - SharedBuffer::sharedBuffer(mStorage)->acquire(); - } -} - -VectorImpl::~VectorImpl() -{ - ALOG_ASSERT(!mCount, - "[%p] " - "subclasses of VectorImpl must call finish_vector()" - " in their destructor. Leaking %d bytes.", - this, (int)(mCount*mItemSize)); - // We can't call _do_destroy() here because the vtable is already gone. -} - -VectorImpl& VectorImpl::operator = (const VectorImpl& rhs) -{ - ALOG_ASSERT(mItemSize == rhs.mItemSize, - "Vector<> have different types (this=%p, rhs=%p)", this, &rhs); - if (this != &rhs) { - release_storage(); - if (rhs.mCount) { - mStorage = rhs.mStorage; - mCount = rhs.mCount; - SharedBuffer::sharedBuffer(mStorage)->acquire(); - } else { - mStorage = 0; - mCount = 0; - } - } - return *this; -} - -void* VectorImpl::editArrayImpl() -{ - if (mStorage) { - SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage)->attemptEdit(); - if (sb == 0) { - sb = SharedBuffer::alloc(capacity() * mItemSize); - if (sb) { - _do_copy(sb->data(), mStorage, mCount); - release_storage(); - mStorage = sb->data(); - } - } - } - return mStorage; -} - -size_t VectorImpl::capacity() const -{ - if (mStorage) { - return SharedBuffer::sharedBuffer(mStorage)->size() / mItemSize; - } - return 0; -} - -ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index) -{ - if (index > size()) - return BAD_INDEX; - void* where = _grow(index, vector.size()); - if (where) { - _do_copy(where, vector.arrayImpl(), vector.size()); - } - return where ? index : (ssize_t)NO_MEMORY; -} - -ssize_t VectorImpl::appendVector(const VectorImpl& vector) -{ - return insertVectorAt(vector, size()); -} - -ssize_t VectorImpl::insertAt(size_t index, size_t numItems) -{ - return insertAt(0, index, numItems); -} - -ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems) -{ - if (index > size()) - return BAD_INDEX; - void* where = _grow(index, numItems); - if (where) { - if (item) { - _do_splat(where, item, numItems); - } else { - _do_construct(where, numItems); - } - } - return where ? index : (ssize_t)NO_MEMORY; -} - -void VectorImpl::pop() -{ - if (size()) - removeItemsAt(size()-1, 1); -} - -void VectorImpl::push() -{ - push(0); -} - -void VectorImpl::push(const void* item) -{ - insertAt(item, size()); -} - -ssize_t VectorImpl::add() -{ - return add(0); -} - -ssize_t VectorImpl::add(const void* item) -{ - return insertAt(item, size()); -} - -ssize_t VectorImpl::replaceAt(size_t index) -{ - return replaceAt(0, index); -} - -ssize_t VectorImpl::replaceAt(const void* prototype, size_t index) -{ - ALOG_ASSERT(index<size(), - "[%p] replace: index=%d, size=%d", this, (int)index, (int)size()); - - void* item = editItemLocation(index); - if (item == 0) - return NO_MEMORY; - _do_destroy(item, 1); - if (prototype == 0) { - _do_construct(item, 1); - } else { - _do_copy(item, prototype, 1); - } - return ssize_t(index); -} - -ssize_t VectorImpl::removeItemsAt(size_t index, size_t count) -{ - ALOG_ASSERT((index+count)<=size(), - "[%p] remove: index=%d, count=%d, size=%d", - this, (int)index, (int)count, (int)size()); - - if ((index+count) > size()) - return BAD_VALUE; - _shrink(index, count); - return index; -} - -void VectorImpl::finish_vector() -{ - release_storage(); - mStorage = 0; - mCount = 0; -} - -void VectorImpl::clear() -{ - _shrink(0, mCount); -} - -void* VectorImpl::editItemLocation(size_t index) -{ - ALOG_ASSERT(index<capacity(), - "[%p] itemLocation: index=%d, capacity=%d, count=%d", - this, (int)index, (int)capacity(), (int)mCount); - - void* buffer = editArrayImpl(); - if (buffer) - return reinterpret_cast<char*>(buffer) + index*mItemSize; - return 0; -} - -const void* VectorImpl::itemLocation(size_t index) const -{ - ALOG_ASSERT(index<capacity(), - "[%p] editItemLocation: index=%d, capacity=%d, count=%d", - this, (int)index, (int)capacity(), (int)mCount); - - const void* buffer = arrayImpl(); - if (buffer) - return reinterpret_cast<const char*>(buffer) + index*mItemSize; - return 0; -} - -ssize_t VectorImpl::setCapacity(size_t new_capacity) -{ - size_t current_capacity = capacity(); - ssize_t amount = new_capacity - size(); - if (amount <= 0) { - // we can't reduce the capacity - return current_capacity; - } - SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); - if (sb) { - void* array = sb->data(); - _do_copy(array, mStorage, size()); - release_storage(); - mStorage = const_cast<void*>(array); - } else { - return NO_MEMORY; - } - return new_capacity; -} - -void VectorImpl::release_storage() -{ - if (mStorage) { - const SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage); - if (sb->release(SharedBuffer::eKeepStorage) == 1) { - _do_destroy(mStorage, mCount); - SharedBuffer::dealloc(sb); - } - } -} - -void* VectorImpl::_grow(size_t where, size_t amount) -{ -// ALOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d", -// this, (int)where, (int)amount, (int)mCount, (int)capacity()); - - if (where > mCount) - where = mCount; - - const size_t new_size = mCount + amount; - if (capacity() < new_size) { - const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2); -// ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity); - if ((mStorage) && - (mCount==where) && - (mFlags & HAS_TRIVIAL_COPY) && - (mFlags & HAS_TRIVIAL_DTOR)) - { - const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); - SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); - mStorage = sb->data(); - } else { - SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); - if (sb) { - void* array = sb->data(); - if (where>0) { - _do_copy(array, mStorage, where); - } - if (mCount>where) { - const void* from = reinterpret_cast<const uint8_t *>(mStorage) + where*mItemSize; - void* dest = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize; - _do_copy(dest, from, mCount-where); - } - release_storage(); - mStorage = const_cast<void*>(array); - } - } - } else { - ssize_t s = mCount-where; - if (s>0) { - void* array = editArrayImpl(); - void* to = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize; - const void* from = reinterpret_cast<const uint8_t *>(array) + where*mItemSize; - _do_move_forward(to, from, s); - } - } - mCount += amount; - void* free_space = const_cast<void*>(itemLocation(where)); - return free_space; -} - -void VectorImpl::_shrink(size_t where, size_t amount) -{ - if (!mStorage) - return; - -// ALOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d", -// this, (int)where, (int)amount, (int)mCount, (int)capacity()); - - if (where >= mCount) - where = mCount - amount; - - const size_t new_size = mCount - amount; - if (new_size*3 < capacity()) { - const size_t new_capacity = max(kMinVectorCapacity, new_size*2); -// ALOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity); - if ((where == mCount-amount) && - (mFlags & HAS_TRIVIAL_COPY) && - (mFlags & HAS_TRIVIAL_DTOR)) - { - const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); - SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); - mStorage = sb->data(); - } else { - SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); - if (sb) { - void* array = sb->data(); - if (where>0) { - _do_copy(array, mStorage, where); - } - if (mCount > where+amount) { - const void* from = reinterpret_cast<const uint8_t *>(mStorage) + (where+amount)*mItemSize; - void* dest = reinterpret_cast<uint8_t *>(array) + where*mItemSize; - _do_copy(dest, from, mCount-(where+amount)); - } - release_storage(); - mStorage = const_cast<void*>(array); - } - } - } else { - void* array = editArrayImpl(); - void* to = reinterpret_cast<uint8_t *>(array) + where*mItemSize; - _do_destroy(to, amount); - ssize_t s = mCount-(where+amount); - if (s>0) { - const void* from = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize; - _do_move_backward(to, from, s); - } - } - - // adjust the number of items... - mCount -= amount; -} - -size_t VectorImpl::itemSize() const { - return mItemSize; -} - -void VectorImpl::_do_construct(void* storage, size_t num) const -{ - if (!(mFlags & HAS_TRIVIAL_CTOR)) { - do_construct(storage, num); - } -} - -void VectorImpl::_do_destroy(void* storage, size_t num) const -{ - if (!(mFlags & HAS_TRIVIAL_DTOR)) { - do_destroy(storage, num); - } -} - -void VectorImpl::_do_copy(void* dest, const void* from, size_t num) const -{ - if (!(mFlags & HAS_TRIVIAL_COPY)) { - do_copy(dest, from, num); - } else { - memcpy(dest, from, num*itemSize()); - } -} - -void VectorImpl::_do_splat(void* dest, const void* item, size_t num) const { - do_splat(dest, item, num); -} - -void VectorImpl::_do_move_forward(void* dest, const void* from, size_t num) const { - do_move_forward(dest, from, num); -} - -void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) const { - do_move_backward(dest, from, num); -} - -void VectorImpl::reservedVectorImpl1() { } -void VectorImpl::reservedVectorImpl2() { } -void VectorImpl::reservedVectorImpl3() { } -void VectorImpl::reservedVectorImpl4() { } -void VectorImpl::reservedVectorImpl5() { } -void VectorImpl::reservedVectorImpl6() { } -void VectorImpl::reservedVectorImpl7() { } -void VectorImpl::reservedVectorImpl8() { } - -/*****************************************************************************/ - -SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags) - : VectorImpl(itemSize, flags) -{ -} - -SortedVectorImpl::SortedVectorImpl(const VectorImpl& rhs) -: VectorImpl(rhs) -{ -} - -SortedVectorImpl::~SortedVectorImpl() -{ -} - -SortedVectorImpl& SortedVectorImpl::operator = (const SortedVectorImpl& rhs) -{ - return static_cast<SortedVectorImpl&>( VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)) ); -} - -ssize_t SortedVectorImpl::indexOf(const void* item) const -{ - return _indexOrderOf(item); -} - -size_t SortedVectorImpl::orderOf(const void* item) const -{ - size_t o; - _indexOrderOf(item, &o); - return o; -} - -ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const -{ - // binary search - ssize_t err = NAME_NOT_FOUND; - ssize_t l = 0; - ssize_t h = size()-1; - ssize_t mid; - const void* a = arrayImpl(); - const size_t s = itemSize(); - while (l <= h) { - mid = l + (h - l)/2; - const void* const curr = reinterpret_cast<const char *>(a) + (mid*s); - const int c = do_compare(curr, item); - if (c == 0) { - err = l = mid; - break; - } else if (c < 0) { - l = mid + 1; - } else { - h = mid - 1; - } - } - if (order) *order = l; - return err; -} - -ssize_t SortedVectorImpl::add(const void* item) -{ - size_t order; - ssize_t index = _indexOrderOf(item, &order); - if (index < 0) { - index = VectorImpl::insertAt(item, order, 1); - } else { - index = VectorImpl::replaceAt(item, index); - } - return index; -} - -ssize_t SortedVectorImpl::merge(const VectorImpl& vector) -{ - // naive merge... - if (!vector.isEmpty()) { - const void* buffer = vector.arrayImpl(); - const size_t is = itemSize(); - size_t s = vector.size(); - for (size_t i=0 ; i<s ; i++) { - ssize_t err = add( reinterpret_cast<const char*>(buffer) + i*is ); - if (err<0) { - return err; - } - } - } - return NO_ERROR; -} - -ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector) -{ - // we've merging a sorted vector... nice! - ssize_t err = NO_ERROR; - if (!vector.isEmpty()) { - // first take care of the case where the vectors are sorted together - if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) { - err = VectorImpl::insertVectorAt(static_cast<const VectorImpl&>(vector), 0); - } else if (do_compare(vector.arrayImpl(), itemLocation(size()-1)) >= 0) { - err = VectorImpl::appendVector(static_cast<const VectorImpl&>(vector)); - } else { - // this could be made a little better - err = merge(static_cast<const VectorImpl&>(vector)); - } - } - return err; -} - -ssize_t SortedVectorImpl::remove(const void* item) -{ - ssize_t i = indexOf(item); - if (i>=0) { - VectorImpl::removeItemsAt(i, 1); - } - return i; -} - -void SortedVectorImpl::reservedSortedVectorImpl1() { }; -void SortedVectorImpl::reservedSortedVectorImpl2() { }; -void SortedVectorImpl::reservedSortedVectorImpl3() { }; -void SortedVectorImpl::reservedSortedVectorImpl4() { }; -void SortedVectorImpl::reservedSortedVectorImpl5() { }; -void SortedVectorImpl::reservedSortedVectorImpl6() { }; -void SortedVectorImpl::reservedSortedVectorImpl7() { }; -void SortedVectorImpl::reservedSortedVectorImpl8() { }; - - -/*****************************************************************************/ - -} // namespace tinyutils -} // namespace android - diff --git a/libpixelflinger/codeflinger/tinyutils/VectorImpl.h b/libpixelflinger/codeflinger/tinyutils/VectorImpl.h deleted file mode 100644 index 56089b325..000000000 --- a/libpixelflinger/codeflinger/tinyutils/VectorImpl.h +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright 2005 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. - */ - -#ifndef ANDROID_PIXELFLINGER_VECTOR_IMPL_H -#define ANDROID_PIXELFLINGER_VECTOR_IMPL_H - -#include <assert.h> -#include <stdint.h> -#include <sys/types.h> - -// --------------------------------------------------------------------------- -// No user serviceable parts in here... -// --------------------------------------------------------------------------- - -namespace android { -namespace tinyutils { - -/*! - * Implementation of the guts of the vector<> class - * this ensures backward binary compatibility and - * reduces code size. - * For performance reasons, we expose mStorage and mCount - * so these fields are set in stone. - * - */ - -class VectorImpl -{ -public: - enum { // flags passed to the ctor - HAS_TRIVIAL_CTOR = 0x00000001, - HAS_TRIVIAL_DTOR = 0x00000002, - HAS_TRIVIAL_COPY = 0x00000004, - HAS_TRIVIAL_ASSIGN = 0x00000008 - }; - - VectorImpl(size_t itemSize, uint32_t flags); - VectorImpl(const VectorImpl& rhs); - virtual ~VectorImpl(); - - /*! must be called from subclasses destructor */ - void finish_vector(); - - VectorImpl& operator = (const VectorImpl& rhs); - - /*! C-style array access */ - inline const void* arrayImpl() const { return mStorage; } - void* editArrayImpl(); - - /*! vector stats */ - inline size_t size() const { return mCount; } - inline bool isEmpty() const { return mCount == 0; } - size_t capacity() const; - ssize_t setCapacity(size_t size); - - /*! append/insert another vector */ - ssize_t insertVectorAt(const VectorImpl& vector, size_t index); - ssize_t appendVector(const VectorImpl& vector); - - /*! add/insert/replace items */ - ssize_t insertAt(size_t where, size_t numItems = 1); - ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); - void pop(); - void push(); - void push(const void* item); - ssize_t add(); - ssize_t add(const void* item); - ssize_t replaceAt(size_t index); - ssize_t replaceAt(const void* item, size_t index); - - /*! remove items */ - ssize_t removeItemsAt(size_t index, size_t count = 1); - void clear(); - - const void* itemLocation(size_t index) const; - void* editItemLocation(size_t index); - -protected: - size_t itemSize() const; - void release_storage(); - - virtual void do_construct(void* storage, size_t num) const = 0; - virtual void do_destroy(void* storage, size_t num) const = 0; - virtual void do_copy(void* dest, const void* from, size_t num) const = 0; - virtual void do_splat(void* dest, const void* item, size_t num) const = 0; - virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0; - virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0; - - // take care of FBC... - virtual void reservedVectorImpl1(); - virtual void reservedVectorImpl2(); - virtual void reservedVectorImpl3(); - virtual void reservedVectorImpl4(); - virtual void reservedVectorImpl5(); - virtual void reservedVectorImpl6(); - virtual void reservedVectorImpl7(); - virtual void reservedVectorImpl8(); - -private: - void* _grow(size_t where, size_t amount); - void _shrink(size_t where, size_t amount); - - inline void _do_construct(void* storage, size_t num) const; - inline void _do_destroy(void* storage, size_t num) const; - inline void _do_copy(void* dest, const void* from, size_t num) const; - inline void _do_splat(void* dest, const void* item, size_t num) const; - inline void _do_move_forward(void* dest, const void* from, size_t num) const; - inline void _do_move_backward(void* dest, const void* from, size_t num) const; - - // These 2 fields are exposed in the inlines below, - // so they're set in stone. - void * mStorage; // base address of the vector - size_t mCount; // number of items - - const uint32_t mFlags; - const size_t mItemSize; -}; - - - -class SortedVectorImpl : public VectorImpl -{ -public: - SortedVectorImpl(size_t itemSize, uint32_t flags); - SortedVectorImpl(const VectorImpl& rhs); - virtual ~SortedVectorImpl(); - - SortedVectorImpl& operator = (const SortedVectorImpl& rhs); - - //! finds the index of an item - ssize_t indexOf(const void* item) const; - - //! finds where this item should be inserted - size_t orderOf(const void* item) const; - - //! add an item in the right place (or replaces it if there is one) - ssize_t add(const void* item); - - //! merges a vector into this one - ssize_t merge(const VectorImpl& vector); - ssize_t merge(const SortedVectorImpl& vector); - - //! removes an item - ssize_t remove(const void* item); - -protected: - virtual int do_compare(const void* lhs, const void* rhs) const = 0; - - // take care of FBC... - virtual void reservedSortedVectorImpl1(); - virtual void reservedSortedVectorImpl2(); - virtual void reservedSortedVectorImpl3(); - virtual void reservedSortedVectorImpl4(); - virtual void reservedSortedVectorImpl5(); - virtual void reservedSortedVectorImpl6(); - virtual void reservedSortedVectorImpl7(); - virtual void reservedSortedVectorImpl8(); - -private: - ssize_t _indexOrderOf(const void* item, size_t* order = 0) const; - - // these are made private, because they can't be used on a SortedVector - // (they don't have an implementation either) - ssize_t add(); - void pop(); - void push(); - void push(const void* item); - ssize_t insertVectorAt(const VectorImpl& vector, size_t index); - ssize_t appendVector(const VectorImpl& vector); - ssize_t insertAt(size_t where, size_t numItems = 1); - ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); - ssize_t replaceAt(size_t index); - ssize_t replaceAt(const void* item, size_t index); -}; - -} // namespace tinyutils -} // namespace android - - -// --------------------------------------------------------------------------- - -#endif // ANDROID_PIXELFLINGER_VECTOR_IMPL_H diff --git a/libpixelflinger/include/private/pixelflinger/ggl_context.h b/libpixelflinger/include/private/pixelflinger/ggl_context.h index a18b2f769..d45dabcd0 100644 --- a/libpixelflinger/include/private/pixelflinger/ggl_context.h +++ b/libpixelflinger/include/private/pixelflinger/ggl_context.h @@ -42,7 +42,7 @@ inline uint32_t GGL_HOST_TO_RGBA(uint32_t v) { #else inline uint32_t GGL_RGBA_TO_HOST(uint32_t v) { -#if defined(__mips__) && __mips==32 && __mips_isa_rev>=2 +#if defined(__mips__) && __mips_isa_rev>=2 uint32_t r; __asm__("wsbh %0, %1;" "rotr %0, %0, 16" @@ -55,7 +55,7 @@ inline uint32_t GGL_RGBA_TO_HOST(uint32_t v) { #endif } inline uint32_t GGL_HOST_TO_RGBA(uint32_t v) { -#if defined(__mips__) && __mips==32 && __mips_isa_rev>=2 +#if defined(__mips__) && __mips_isa_rev>=2 uint32_t r; __asm__("wsbh %0, %1;" "rotr %0, %0, 16" diff --git a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h index 787f6202b..17b85dd58 100644 --- a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h +++ b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h @@ -520,6 +520,252 @@ inline int64_t gglMulii(int32_t x, int32_t y) return res; } +#elif defined(__mips__) && __mips_isa_rev == 6 + +/*inline MIPS implementations*/ +inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) CONST; +inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) { + GGLfixed result,tmp,tmp1,tmp2; + + if (__builtin_constant_p(shift)) { + if (shift == 0) { + asm ("mul %[res], %[a], %[b] \t\n" + : [res]"=&r"(result) + : [a]"r"(a),[b]"r"(b) + ); + } else if (shift == 32) + { + asm ("mul %[res], %[a], %[b] \t\n" + "li %[tmp],1\t\n" + "sll %[tmp],%[tmp],0x1f\t\n" + "addu %[tmp1],%[tmp],%[res] \t\n" + "muh %[res], %[a], %[b] \t\n" + "sltu %[tmp1],%[tmp1],%[tmp]\t\n" /*obit*/ + "sra %[tmp],%[tmp],0x1f \t\n" + "addu %[res],%[res],%[tmp]\t\n" + "addu %[res],%[res],%[tmp1]\t\n" + : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1) + : [a]"r"(a),[b]"r"(b),[shift]"I"(shift) + ); + } else if ((shift >0) && (shift < 32)) + { + asm ("mul %[res], %[a], %[b] \t\n" + "li %[tmp],1 \t\n" + "sll %[tmp],%[tmp],%[shiftm1] \t\n" + "addu %[tmp1],%[tmp],%[res] \t\n" + "sltu %[tmp1],%[tmp1],%[tmp] \t\n" /*obit?*/ + "addu %[res],%[res],%[tmp] \t\n" + "muh %[tmp], %[a], %[b] \t\n" + "addu %[tmp],%[tmp],%[tmp1] \t\n" + "sll %[tmp],%[tmp],%[lshift] \t\n" + "srl %[res],%[res],%[rshift] \t\n" + "or %[res],%[res],%[tmp] \t\n" + : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2) + : [a]"r"(a),[b]"r"(b),[lshift]"I"(32-shift),[rshift]"I"(shift),[shiftm1]"I"(shift-1) + ); + } else { + asm ("mul %[res], %[a], %[b] \t\n" + "li %[tmp],1 \t\n" + "sll %[tmp],%[tmp],%[shiftm1] \t\n" + "addu %[tmp1],%[tmp],%[res] \t\n" + "sltu %[tmp1],%[tmp1],%[tmp] \t\n" /*obit?*/ + "sra %[tmp2],%[tmp],0x1f \t\n" + "addu %[res],%[res],%[tmp] \t\n" + "muh %[tmp], %[a], %[b] \t\n" + "addu %[tmp],%[tmp],%[tmp2] \t\n" + "addu %[tmp],%[tmp],%[tmp1] \t\n" /*tmp=hi*/ + "srl %[tmp2],%[res],%[rshift] \t\n" + "srav %[res], %[tmp],%[rshift]\t\n" + "sll %[tmp],%[tmp],1 \t\n" + "sll %[tmp],%[tmp],%[norbits] \t\n" + "or %[tmp],%[tmp],%[tmp2] \t\n" + "seleqz %[tmp],%[tmp],%[bit5] \t\n" + "selnez %[res],%[res],%[bit5] \t\n" + "or %[res],%[res],%[tmp] \t\n" + : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2) + : [a]"r"(a),[b]"r"(b),[norbits]"I"(~(shift)),[rshift]"I"(shift),[shiftm1] "I"(shift-1),[bit5]"I"(shift & 0x20) + ); + } + } else { + asm ("mul %[res], %[a], %[b] \t\n" + "li %[tmp],1 \t\n" + "sll %[tmp],%[tmp],%[shiftm1] \t\n" + "addu %[tmp1],%[tmp],%[res] \t\n" + "sltu %[tmp1],%[tmp1],%[tmp] \t\n" /*obit?*/ + "sra %[tmp2],%[tmp],0x1f \t\n" + "addu %[res],%[res],%[tmp] \t\n" + "muh %[tmp], %[a], %[b] \t\n" + "addu %[tmp],%[tmp],%[tmp2] \t\n" + "addu %[tmp],%[tmp],%[tmp1] \t\n" /*tmp=hi*/ + "srl %[tmp2],%[res],%[rshift] \t\n" + "srav %[res], %[tmp],%[rshift]\t\n" + "sll %[tmp],%[tmp],1 \t\n" + "sll %[tmp],%[tmp],%[norbits] \t\n" + "or %[tmp],%[tmp],%[tmp2] \t\n" + "seleqz %[tmp],%[tmp],%[bit5] \t\n" + "selnez %[res],%[res],%[bit5] \t\n" + "or %[res],%[res],%[tmp] \t\n" + : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2) + : [a]"r"(a),[b]"r"(b),[norbits]"r"(~(shift)),[rshift] "r"(shift),[shiftm1]"r"(shift-1),[bit5] "r"(shift & 0x20) + ); + } + return result; +} + +inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST; +inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) { + GGLfixed result,t,tmp1,tmp2; + + if (__builtin_constant_p(shift)) { + if (shift == 0) { + asm ("mul %[lo], %[a], %[b] \t\n" + "addu %[lo],%[lo],%[c] \t\n" + : [lo]"=&r"(result) + : [a]"r"(a),[b]"r"(b),[c]"r"(c) + ); + } else if (shift == 32) { + asm ("muh %[lo], %[a], %[b] \t\n" + "addu %[lo],%[lo],%[c] \t\n" + : [lo]"=&r"(result) + : [a]"r"(a),[b]"r"(b),[c]"r"(c) + ); + } else if ((shift>0) && (shift<32)) { + asm ("mul %[res], %[a], %[b] \t\n" + "muh %[t], %[a], %[b] \t\n" + "srl %[res],%[res],%[rshift] \t\n" + "sll %[t],%[t],%[lshift] \t\n" + "or %[res],%[res],%[t] \t\n" + "addu %[res],%[res],%[c] \t\n" + : [res]"=&r"(result),[t]"=&r"(t) + : [a]"r"(a),[b]"r"(b),[c]"r"(c),[lshift]"I"(32-shift),[rshift]"I"(shift) + ); + } else { + asm ("mul %[res], %[a], %[b] \t\n" + "muh %[t], %[a], %[b] \t\n" + "nor %[tmp1],$zero,%[shift]\t\n" + "srl %[res],%[res],%[shift] \t\n" + "sll %[tmp2],%[t],1 \t\n" + "sllv %[tmp2],%[tmp2],%[tmp1] \t\n" + "or %[tmp1],%[tmp2],%[res] \t\n" + "srav %[res],%[t],%[shift] \t\n" + "andi %[tmp2],%[shift],0x20\t\n" + "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n" + "selnez %[res],%[res],%[tmp2]\t\n" + "or %[res],%[res],%[tmp1]\t\n" + "addu %[res],%[res],%[c] \t\n" + : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2) + : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"I"(shift) + ); + } + } else { + asm ("mul %[res], %[a], %[b] \t\n" + "muh %[t], %[a], %[b] \t\n" + "nor %[tmp1],$zero,%[shift]\t\n" + "srl %[res],%[res],%[shift] \t\n" + "sll %[tmp2],%[t],1 \t\n" + "sllv %[tmp2],%[tmp2],%[tmp1] \t\n" + "or %[tmp1],%[tmp2],%[res] \t\n" + "srav %[res],%[t],%[shift] \t\n" + "andi %[tmp2],%[shift],0x20\t\n" + "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n" + "selnez %[res],%[res],%[tmp2]\t\n" + "or %[res],%[res],%[tmp1]\t\n" + "addu %[res],%[res],%[c] \t\n" + : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2) + : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"r"(shift) + ); + } + return result; +} + +inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST; +inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) { + GGLfixed result,t,tmp1,tmp2; + + if (__builtin_constant_p(shift)) { + if (shift == 0) { + asm ("mul %[lo], %[a], %[b] \t\n" + "subu %[lo],%[lo],%[c] \t\n" + : [lo]"=&r"(result) + : [a]"r"(a),[b]"r"(b),[c]"r"(c) + ); + } else if (shift == 32) { + asm ("muh %[lo], %[a], %[b] \t\n" + "subu %[lo],%[lo],%[c] \t\n" + : [lo]"=&r"(result) + : [a]"r"(a),[b]"r"(b),[c]"r"(c) + ); + } else if ((shift>0) && (shift<32)) { + asm ("mul %[res], %[a], %[b] \t\n" + "muh %[t], %[a], %[b] \t\n" + "srl %[res],%[res],%[rshift] \t\n" + "sll %[t],%[t],%[lshift] \t\n" + "or %[res],%[res],%[t] \t\n" + "subu %[res],%[res],%[c] \t\n" + : [res]"=&r"(result),[t]"=&r"(t) + : [a]"r"(a),[b]"r"(b),[c]"r"(c),[lshift]"I"(32-shift),[rshift]"I"(shift) + ); + } else { + asm ("mul %[res], %[a], %[b] \t\n" + "muh %[t], %[a], %[b] \t\n" + "nor %[tmp1],$zero,%[shift]\t\n" + "srl %[res],%[res],%[shift] \t\n" + "sll %[tmp2],%[t],1 \t\n" + "sllv %[tmp2],%[tmp2],%[tmp1] \t\n" + "or %[tmp1],%[tmp2],%[res] \t\n" + "srav %[res],%[t],%[shift] \t\n" + "andi %[tmp2],%[shift],0x20\t\n" + "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n" + "selnez %[res],%[res],%[tmp2]\t\n" + "or %[res],%[res],%[tmp1]\t\n" + "subu %[res],%[res],%[c] \t\n" + : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2) + : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"I"(shift) + ); + } + } else { + asm ("mul %[res], %[a], %[b] \t\n" + "muh %[t], %[a], %[b] \t\n" + "nor %[tmp1],$zero,%[shift]\t\n" + "srl %[res],%[res],%[shift] \t\n" + "sll %[tmp2],%[t],1 \t\n" + "sllv %[tmp2],%[tmp2],%[tmp1] \t\n" + "or %[tmp1],%[tmp2],%[res] \t\n" + "srav %[res],%[t],%[shift] \t\n" + "andi %[tmp2],%[shift],0x20\t\n" + "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n" + "selnez %[res],%[res],%[tmp2]\t\n" + "or %[res],%[res],%[tmp1]\t\n" + "subu %[res],%[res],%[c] \t\n" + : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2) + : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"r"(shift) + ); + } + return result; +} + +inline int64_t gglMulii(int32_t x, int32_t y) CONST; +inline int64_t gglMulii(int32_t x, int32_t y) { + union { + struct { +#if defined(__MIPSEL__) + int32_t lo; + int32_t hi; +#elif defined(__MIPSEB__) + int32_t hi; + int32_t lo; +#endif + } s; + int64_t res; + }u; + asm("mul %0, %2, %3 \t\n" + "muh %1, %2, %3 \t\n" + : "=r"(u.s.lo), "=&r"(u.s.hi) + : "%r"(x), "r"(y) + ); + return u.res; +} + #else // ---------------------------------------------------------------------- inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) CONST; diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp index 3d145319a..a718b02c6 100644 --- a/libpixelflinger/scanline.cpp +++ b/libpixelflinger/scanline.cpp @@ -41,6 +41,8 @@ #include "codeflinger/Arm64Assembler.h" #elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6 #include "codeflinger/MIPSAssembler.h" +#elif defined(__mips__) && defined(__LP64__) +#include "codeflinger/MIPS64Assembler.h" #endif //#include "codeflinger/ARMAssemblerOptimizer.h" @@ -59,7 +61,7 @@ # define ANDROID_CODEGEN ANDROID_CODEGEN_GENERATED #endif -#if defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__) +#if defined(__arm__) || (defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__))) || defined(__aarch64__) # define ANDROID_ARM_CODEGEN 1 #else # define ANDROID_ARM_CODEGEN 0 @@ -73,7 +75,7 @@ */ #define DEBUG_NEEDS 0 -#if defined( __mips__) && !defined(__LP64__) && __mips_isa_rev < 6 +#if defined( __mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__)) #define ASSEMBLY_SCRATCH_SIZE 4096 #elif defined(__aarch64__) #define ASSEMBLY_SCRATCH_SIZE 8192 @@ -136,6 +138,9 @@ extern "C" void scanline_t32cb16blend_arm64(uint16_t*, uint32_t*, size_t); extern "C" void scanline_col32cb16blend_arm64(uint16_t *dst, uint32_t col, size_t ct); #elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6 extern "C" void scanline_t32cb16blend_mips(uint16_t*, uint32_t*, size_t); +#elif defined(__mips__) && defined(__LP64__) +extern "C" void scanline_t32cb16blend_mips64(uint16_t*, uint32_t*, size_t); +extern "C" void scanline_col32cb16blend_mips64(uint16_t *dst, uint32_t col, size_t ct); #endif // ---------------------------------------------------------------------------- @@ -286,7 +291,7 @@ static const needs_filter_t fill16noblend = { #if ANDROID_ARM_CODEGEN -#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6 +#if defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__)) static CodeCache gCodeCache(32 * 1024); #elif defined(__aarch64__) static CodeCache gCodeCache(48 * 1024); @@ -406,8 +411,10 @@ static void pick_scanline(context_t* c) //GGLAssembler assembler( // new ARMAssemblerOptimizer(new ARMAssembler(a)) ); #endif -#if defined(__mips__) +#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6 GGLAssembler assembler( new ArmToMipsAssembler(a) ); +#elif defined(__mips__) && defined(__LP64__) + GGLAssembler assembler( new ArmToMips64Assembler(a) ); #elif defined(__aarch64__) GGLAssembler assembler( new ArmToArm64Assembler(a) ); #endif @@ -2103,6 +2110,8 @@ void scanline_col32cb16blend(context_t* c) #endif // defined(__ARM_HAVE_NEON) && BYTE_ORDER == LITTLE_ENDIAN #elif ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && defined(__aarch64__)) scanline_col32cb16blend_arm64(dst, GGL_RGBA_TO_HOST(c->packed8888), ct); +#elif ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__mips__) && defined(__LP64__))) + scanline_col32cb16blend_mips64(dst, GGL_RGBA_TO_HOST(c->packed8888), ct); #else uint32_t s = GGL_RGBA_TO_HOST(c->packed8888); int sA = (s>>24); @@ -2175,7 +2184,8 @@ last_one: void scanline_t32cb16blend(context_t* c) { -#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__))) +#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__arm__) || defined(__aarch64__) || \ + (defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__))))) int32_t x = c->iterators.xl; size_t ct = c->iterators.xr - x; int32_t y = c->iterators.y; @@ -2191,8 +2201,10 @@ void scanline_t32cb16blend(context_t* c) scanline_t32cb16blend_arm(dst, src, ct); #elif defined(__aarch64__) scanline_t32cb16blend_arm64(dst, src, ct); -#elif defined(__mips__) +#elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6 scanline_t32cb16blend_mips(dst, src, ct); +#elif defined(__mips__) && defined(__LP64__) + scanline_t32cb16blend_mips64(dst, src, ct); #endif #else dst_iterator16 di(c); diff --git a/libpixelflinger/tests/arch-mips/Android.mk b/libpixelflinger/tests/arch-mips/Android.mk new file mode 100644 index 000000000..fe6979ef6 --- /dev/null +++ b/libpixelflinger/tests/arch-mips/Android.mk @@ -0,0 +1,6 @@ +ifeq ($(TARGET_ARCH),mips) +include $(all-subdir-makefiles) +endif +ifeq ($(TARGET_ARCH),mipsel) +include $(all-subdir-makefiles) +endif diff --git a/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk new file mode 100644 index 000000000..40f197f3b --- /dev/null +++ b/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk @@ -0,0 +1,18 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + col32cb16blend_test.c \ + ../../../arch-mips/col32cb16blend.S + +LOCAL_SHARED_LIBRARIES := + +LOCAL_C_INCLUDES := + +LOCAL_MODULE:= test-pixelflinger-mips-col32cb16blend + +LOCAL_MODULE_TAGS := tests + +LOCAL_MULTILIB := 32 + +include $(BUILD_NATIVE_TEST) diff --git a/libpixelflinger/tests/arch-mips/col32cb16blend/col32cb16blend_test.c b/libpixelflinger/tests/arch-mips/col32cb16blend/col32cb16blend_test.c new file mode 100644 index 000000000..dd0e60fef --- /dev/null +++ b/libpixelflinger/tests/arch-mips/col32cb16blend/col32cb16blend_test.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> + + +#define ARGB_8888_MAX 0xFFFFFFFF +#define ARGB_8888_MIN 0x00000000 +#define RGB_565_MAX 0xFFFF +#define RGB_565_MIN 0x0000 + +struct test_t +{ + char name[256]; + uint32_t src_color; + uint16_t dst_color; + size_t count; +}; + +struct test_t tests[] = +{ + {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1}, + {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2}, + {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3}, + {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4}, + {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1}, + {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2}, + {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3}, + {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4}, + {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5}, + {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10} +}; + +void scanline_col32cb16blend_mips(uint16_t *dst, uint32_t src, size_t count); +void scanline_col32cb16blend_c(uint16_t * dst, uint32_t src, size_t count) +{ + uint32_t srcAlpha = (src>>24); + uint32_t f = 0x100 - (srcAlpha + (srcAlpha>>7)); + + while (count--) + { + uint16_t d = *dst; + int dstR = (d>>11)&0x1f; + int dstG = (d>>5)&0x3f; + int dstB = (d)&0x1f; + int srcR = (src >> ( 3))&0x1F; + int srcG = (src >> ( 8+2))&0x3F; + int srcB = (src >> (16+3))&0x1F; + srcR += (f*dstR)>>8; + srcG += (f*dstG)>>8; + srcB += (f*dstB)>>8; + *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB); + } +} + +void scanline_col32cb16blend_test() +{ + uint16_t dst_c[16], dst_asm[16]; + uint32_t i, j; + + for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i) + { + struct test_t test = tests[i]; + + printf("Testing - %s:",test.name); + + memset(dst_c, 0, sizeof(dst_c)); + memset(dst_asm, 0, sizeof(dst_asm)); + + for(j = 0; j < test.count; ++j) + { + dst_c[j] = test.dst_color; + dst_asm[j] = test.dst_color; + } + + + scanline_col32cb16blend_c(dst_c, test.src_color, test.count); + scanline_col32cb16blend_mips(dst_asm, test.src_color, test.count); + + if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0) + printf("Passed\n"); + else + printf("Failed\n"); + + for(j = 0; j < test.count; ++j) + { + printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]); + } + } +} + +int main() +{ + scanline_col32cb16blend_test(); + return 0; +} diff --git a/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk new file mode 100644 index 000000000..d0c0ae4dc --- /dev/null +++ b/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk @@ -0,0 +1,18 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + t32cb16blend_test.c \ + ../../../arch-mips/t32cb16blend.S + +LOCAL_SHARED_LIBRARIES := + +LOCAL_C_INCLUDES := + +LOCAL_MODULE:= test-pixelflinger-mips-t32cb16blend + +LOCAL_MODULE_TAGS := tests + +LOCAL_MULTILIB := 32 + +include $(BUILD_NATIVE_TEST) diff --git a/libpixelflinger/tests/arch-mips/t32cb16blend/t32cb16blend_test.c b/libpixelflinger/tests/arch-mips/t32cb16blend/t32cb16blend_test.c new file mode 100644 index 000000000..c6d6937c6 --- /dev/null +++ b/libpixelflinger/tests/arch-mips/t32cb16blend/t32cb16blend_test.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> + +#define ARGB_8888_MAX 0xFFFFFFFF +#define ARGB_8888_MIN 0x00000000 +#define RGB_565_MAX 0xFFFF +#define RGB_565_MIN 0x0000 + +struct test_t +{ + char name[256]; + uint32_t src_color; + uint16_t dst_color; + size_t count; +}; + +struct test_t tests[] = +{ + {"Count 0", 0, 0, 0}, + {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1}, + {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2}, + {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3}, + {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4}, + {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1}, + {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2}, + {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3}, + {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4}, + {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5}, + {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10} + +}; + +void scanline_t32cb16blend_mips(uint16_t*, uint32_t*, size_t); +void scanline_t32cb16blend_c(uint16_t * dst, uint32_t* src, size_t count) +{ + while (count--) + { + uint16_t d = *dst; + uint32_t s = *src++; + int dstR = (d>>11)&0x1f; + int dstG = (d>>5)&0x3f; + int dstB = (d)&0x1f; + int srcR = (s >> ( 3))&0x1F; + int srcG = (s >> ( 8+2))&0x3F; + int srcB = (s >> (16+3))&0x1F; + int srcAlpha = (s>>24) & 0xFF; + + + int f = 0x100 - (srcAlpha + ((srcAlpha>>7) & 0x1)); + srcR += (f*dstR)>>8; + srcG += (f*dstG)>>8; + srcB += (f*dstB)>>8; + // srcR = srcR > 0x1F? 0x1F: srcR; + // srcG = srcG > 0x3F? 0x3F: srcG; + // srcB = srcB > 0x1F? 0x1F: srcB; + *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB); + } +} + +void scanline_t32cb16blend_test() +{ + uint16_t dst_c[16], dst_asm[16]; + uint32_t src[16]; + uint32_t i; + uint32_t j; + + for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i) + { + struct test_t test = tests[i]; + + printf("Testing - %s:",test.name); + + memset(dst_c, 0, sizeof(dst_c)); + memset(dst_asm, 0, sizeof(dst_asm)); + + for(j = 0; j < test.count; ++j) + { + dst_c[j] = test.dst_color; + dst_asm[j] = test.dst_color; + src[j] = test.src_color; + } + + scanline_t32cb16blend_c(dst_c,src,test.count); + scanline_t32cb16blend_mips(dst_asm,src,test.count); + + + if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0) + printf("Passed\n"); + else + printf("Failed\n"); + + for(j = 0; j < test.count; ++j) + { + printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]); + } + } +} + +int main() +{ + scanline_t32cb16blend_test(); + return 0; +} diff --git a/libpixelflinger/tests/arch-mips64/Android.mk b/libpixelflinger/tests/arch-mips64/Android.mk new file mode 100644 index 000000000..3b1c64ec2 --- /dev/null +++ b/libpixelflinger/tests/arch-mips64/Android.mk @@ -0,0 +1,3 @@ +ifeq ($(TARGET_ARCH),mips64) +include $(all-subdir-makefiles) +endif diff --git a/libpixelflinger/tests/arch-mips64/assembler/Android.mk b/libpixelflinger/tests/arch-mips64/assembler/Android.mk new file mode 100644 index 000000000..469996126 --- /dev/null +++ b/libpixelflinger/tests/arch-mips64/assembler/Android.mk @@ -0,0 +1,21 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + mips64_assembler_test.cpp\ + asm_mips_test_jacket.S + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libpixelflinger + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/../../.. + +LOCAL_MODULE:= test-pixelflinger-mips64-assembler-test + +LOCAL_MODULE_TAGS := tests + +LOCAL_MULTILIB := 64 + +include $(BUILD_NATIVE_TEST) diff --git a/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S b/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S new file mode 100644 index 000000000..8a7f74202 --- /dev/null +++ b/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S @@ -0,0 +1,93 @@ +# /* +# * Copyright (C) 2015 The Android Open Source Project +# * All rights reserved. +# * +# * Redistribution and use in source and binary forms, with or without +# * modification, are permitted provided that the following conditions +# * are met: +# * * Redistributions of source code must retain the above copyright +# * notice, this list of conditions and the following disclaimer. +# * * Redistributions in binary form must reproduce the above copyright +# * notice, this list of conditions and the following disclaimer in +# * the documentation and/or other materials provided with the +# * distribution. +# * +# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# * SUCH DAMAGE. +# */ + + .text + .align 8 + + .global asm_mips_test_jacket + + # // Set the register + # // Calls the asm function + # // Reads the register values to output register + + # // Parameters + # // a0 - Function to jump + # // a1 - register values array + # // a2 - flag values array +asm_mips_test_jacket: + # // Save registers to stack + daddiu $sp, $sp, -96 + sd $s0, 64($sp) + sd $s1, 72($sp) + sd $s2, 80($sp) + sd $ra, 88($sp) + + move $s0, $a0 + move $s1, $a1 + move $s2, $a2 + + ld $v0, 16($s1) + ld $v1, 24($s1) + ld $a0, 32($s1) + ld $a1, 40($s1) + ld $a2, 48($s1) + ld $a3, 56($s1) + ld $a4, 64($s1) + ld $a5, 72($s1) + ld $a6, 80($s1) + ld $a7, 88($s1) + ld $t0, 96($s1) + ld $t1, 104($s1) + ld $t2, 112($s1) + ld $t3, 120($s1) + + jal $s0 + + sd $v0, 16($s1) + sd $v1, 24($s1) + sd $a0, 32($s1) + sd $a1, 40($s1) + sd $a2, 48($s1) + sd $a3, 56($s1) + sd $a4, 64($s1) + sd $a5, 72($s1) + sd $a6, 80($s1) + sd $a7, 88($s1) + sd $t0, 96($s1) + sd $t1, 104($s1) + sd $t2, 112($s1) + sd $t3, 120($s1) + + ld $s0, 64($sp) + ld $s1, 72($sp) + ld $s2, 80($sp) + ld $ra, 88($sp) + + daddiu $sp, $sp, 96 + + j $ra diff --git a/libpixelflinger/tests/arch-mips64/assembler/mips64_assembler_test.cpp b/libpixelflinger/tests/arch-mips64/assembler/mips64_assembler_test.cpp new file mode 100644 index 000000000..2b25223de --- /dev/null +++ b/libpixelflinger/tests/arch-mips64/assembler/mips64_assembler_test.cpp @@ -0,0 +1,644 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include <sys/mman.h> +#include <cutils/ashmem.h> +#include <cutils/atomic.h> +#include <cutils/log.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include "codeflinger/ARMAssemblerInterface.h" +#include "codeflinger/MIPS64Assembler.h" +using namespace android; + +#define TESTS_DATAOP_ENABLE 1 +#define TESTS_DATATRANSFER_ENABLE 1 +#define ASSEMBLY_SCRATCH_SIZE 4096 + +void *instrMem; +uint32_t instrMemSize = 128 * 1024; +char dataMem[8192]; + +typedef void (*asm_function_t)(); +extern "C" void asm_mips_test_jacket(asm_function_t function, + int64_t regs[], int32_t flags[]); + +#define MAX_32BIT (uint32_t)(((uint64_t)1 << 32) - 1) +#define MAX_64BIT ((uint64_t)0xFFFFFFFFFFFFFFFF) +const uint32_t NA = 0; +const uint32_t NUM_REGS = 32; +const uint32_t NUM_FLAGS = 16; + +enum instr_t +{ + INSTR_ADD, + INSTR_SUB, + INSTR_AND, + INSTR_ORR, + INSTR_RSB, + INSTR_BIC, + INSTR_CMP, + INSTR_MOV, + INSTR_MVN, + INSTR_MUL, + INSTR_MLA, + INSTR_SMULBB, + INSTR_SMULBT, + INSTR_SMULTB, + INSTR_SMULTT, + INSTR_SMULWB, + INSTR_SMULWT, + INSTR_SMLABB, + INSTR_UXTB16, + INSTR_UBFX, + INSTR_ADDR_ADD, + INSTR_ADDR_SUB, + INSTR_LDR, + INSTR_LDRB, + INSTR_LDRH, + INSTR_ADDR_LDR, + INSTR_LDM, + INSTR_STR, + INSTR_STRB, + INSTR_STRH, + INSTR_ADDR_STR, + INSTR_STM +}; + +enum shift_t +{ + SHIFT_LSL, + SHIFT_LSR, + SHIFT_ASR, + SHIFT_ROR, + SHIFT_NONE +}; + +enum offset_t +{ + REG_SCALE_OFFSET, + REG_OFFSET, + IMM8_OFFSET, + IMM12_OFFSET, + NO_OFFSET +}; + +enum cond_t +{ + EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV, + HS = CS, + LO = CC +}; + +const char * cc_code[] = +{ + "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC", + "HI", "LS","GE","LT", "GT", "LE", "AL", "NV" +}; + +struct condTest_t +{ + int mode; + int32_t Rcond1; + int32_t Rcond2; + uint64_t Rcond1Value; + uint64_t Rcond2Value; +}; + + +struct dataOpTest_t +{ + uint32_t id; + instr_t op; + condTest_t preCond; + cond_t cond; + bool setFlags; + uint64_t RnValue; + uint64_t RsValue; + bool immediate; + uint32_t immValue; + uint64_t RmValue; + uint32_t shiftMode; + uint32_t shiftAmount; + uint64_t RdValue; + bool checkRd; + uint64_t postRdValue; +}; + +struct dataTransferTest_t +{ + uint32_t id; + instr_t op; + uint32_t preFlag; + cond_t cond; + bool setMem; + uint64_t memOffset; + uint64_t memValue; + uint64_t RnValue; + offset_t offsetType; + uint64_t RmValue; + uint32_t immValue; + bool writeBack; + bool preIndex; + bool postIndex; + uint64_t RdValue; + uint64_t postRdValue; + uint64_t postRnValue; + bool checkMem; + uint64_t postMemOffset; + uint32_t postMemLength; + uint64_t postMemValue; +}; + + +dataOpTest_t dataOpTests [] = +{ + {0xA000,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT,NA,NA,NA,NA,1,0}, + {0xA001,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT-1,NA,NA,NA,NA,1,MAX_64BIT}, + {0xA002,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT,NA,NA,NA,1,0}, + {0xA003,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT-1,NA,NA,NA,1,MAX_64BIT}, + {0xA004,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL, 0,NA,1,0}, + {0xA005,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0xFFFFFFFF80000001}, + {0xA006,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,2}, + {0xA007,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,2}, + {0xA008,INSTR_ADD,{0,0,0,0,0},AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,1}, + {0xA009,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_64BIT,SHIFT_ASR,31,NA,1,0}, + {0xA010,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT,0,0,0,NA,1,1}, + {0xA011,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT-1,0,0,0,NA,1,0}, + {0xA012,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,0,0,NA,1,1}, + {0xA013,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT-1,0,0,NA,1,0}, + {0xA014,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,0,NA,1,1}, + {0xA015,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0}, + {0xA016,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,1}, + {0xA017,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,1}, + {0xA018,INSTR_AND,{0,0,0,0,0},AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,0}, + {0xA019,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_ASR,31,NA,1,1}, + {0xA020,INSTR_ORR,{0,0,0,0,0},AL,0,3,NA,1,MAX_32BIT,0,0,0,NA,1,MAX_64BIT}, + {0xA021,INSTR_ORR,{0,0,0,0,0},AL,0,2,NA,1,MAX_32BIT-1,0,0,0,NA,1,MAX_64BIT-1}, + {0xA022,INSTR_ORR,{0,0,0,0,0},AL,0,3,NA,0,0,MAX_32BIT,0,0,NA,1,MAX_64BIT}, + {0xA023,INSTR_ORR,{0,0,0,0,0},AL,0,2,NA,0,0,MAX_32BIT-1,0,0,NA,1,MAX_64BIT-1}, + {0xA024,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,0,NA,1,MAX_64BIT}, + {0xA025,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0xFFFFFFFF80000001}, + {0xA026,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,1}, + {0xA027,INSTR_ORR,{0,0,0,0,0},AL,0,0,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,1}, + {0xA028,INSTR_ORR,{0,0,0,0,0},AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,1}, + {0xA029,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_64BIT,SHIFT_ASR,31,NA,1,MAX_64BIT}, + {0xA030,INSTR_CMP,{0,0,0,0,0},AL,1,0x10000,NA,1,0x10000,0,0,0,NA,0,0}, + {0xA031,INSTR_MUL,{0,0,0,0,0},AL,0,0,0x10000,0,0,0x10000,0,0,NA,1,0}, + {0xA032,INSTR_MUL,{0,0,0,0,0},AL,0,0,0x1000,0,0,0x10000,0,0,NA,1,0x10000000}, + {0xA033,INSTR_MUL,{0,0,0,0,0},AL,0,0,MAX_32BIT,0,0,1,0,0,NA,1,MAX_64BIT}, + {0xA034,INSTR_MLA,{0,0,0,0,0},AL,0,0x10000,0x10000,0,0,0x10000,0,0,NA,1,0x10000}, + {0xA035,INSTR_MLA,{0,0,0,0,0},AL,0,0x10000,0x1000,0,0,0x10000,0,0,NA,1,0x10010000}, + {0xA036,INSTR_SUB,{1,R_v1,R_a6,2,4},MI,0,2,NA,0,NA,1,NA,NA,2,1,1}, + {0xA037,INSTR_SUB,{2,R_v1,R_a6,2,0},MI,0,2,NA,0,NA,1,NA,NA,2,1,2}, + {0xA038,INSTR_SUB,{1,R_v1,R_a6,4,2},GE,0,2,NA,1,1,NA,NA,NA,2,1,1}, + {0xA039,INSTR_SUB,{1,R_a5,R_a6,2,7},GE,0,2,NA,1,1,NA,NA,NA,2,1,2}, + {0xA040,INSTR_SUB,{1,R_a5,R_a6,1,1},HS,0,2,NA,1,1,NA,NA,NA,2,1,1}, + {0xA041,INSTR_SUB,{1,R_a5,R_a6,0,1},HS,0,2,NA,1,1,NA,NA,NA,2,1,2}, + {0xA042,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,1,1<< 16,0,0,0,NA,1,(uint64_t)(1 -(1<<16))}, + {0xA043,INSTR_SUB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,1,1,0,0,0,NA,1,MAX_64BIT-1}, + {0xA044,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,1,1,0,0,0,NA,1,0}, + {0xA045,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,1<<16,0,0,NA,1,(uint64_t)(1 -(1<<16))}, + {0xA046,INSTR_SUB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,0,NA,1,0,0,NA,1,MAX_64BIT-1}, + {0xA047,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,0,0,NA,1,0}, + {0xA048,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,SHIFT_LSL,16,NA,1,(uint64_t)(1 -(1<<16))}, + {0xA049,INSTR_SUB,{0,0,0,0,0},AL,0,0x80000001,NA,0,NA,MAX_32BIT,SHIFT_LSL,31,NA,1,1}, + {0xA050,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,3,SHIFT_LSR,1,NA,1,0}, + {0xA051,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT,SHIFT_LSR,31,NA,1,0}, + {0xA052,INSTR_RSB,{1,R_a5,R_a6,4,1},GE,0,2,NA,1,0,NA,NA,NA,2,1,(uint64_t)-2}, + {0xA053,INSTR_RSB,{1,R_a5,R_a6,-1,1},GE,0,2,NA,1,0,NA,NA,NA,2,1,2}, + {0xA054,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,1,1<<16,NA,NA,NA,NA,1,(1<<16)-1}, + {0xA055,INSTR_RSB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,1,1,NA,NA,NA,NA,1,(uint64_t)(1-MAX_64BIT)}, + {0xA056,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,1,1,NA,NA,NA,NA,1,0}, + {0xA057,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,1<<16,0,0,NA,1,(1<<16)-1}, + {0xA058,INSTR_RSB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,0,NA,1,0,0,NA,1,(uint64_t)(1-MAX_64BIT)}, + {0xA059,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,0,0,NA,1,0}, + {0xA060,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,SHIFT_LSL,16,NA,1,(1<<16)-1}, + {0xA061,INSTR_RSB,{0,0,0,0,0},AL,0,0x80000001,NA,0,NA,MAX_32BIT ,SHIFT_LSL,31,NA,1,(uint64_t)(-1)}, + {0xA062,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,3,SHIFT_LSR,1,NA,1,0}, + {0xA063,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT,SHIFT_LSR,31,NA,1,0}, + {0xA064,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,1,0x80000001,NA,NA,NA,NA,1,0xFFFFFFFF80000001}, + {0xA065,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,0x80000001,0,0,NA,1,0xFFFFFFFF80000001}, + {0xA066,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,NA,1,MAX_64BIT-1}, + {0xA067,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0xFFFFFFFF80000000}, + {0xA068,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,3,SHIFT_LSR,1,NA,1,1}, + {0xA069,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,1}, + {0xA070,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,3,SHIFT_ASR,1,NA,1,1}, + {0xA071,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_64BIT ,SHIFT_ASR,31,NA,1,MAX_64BIT}, + {0xA072,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,3,SHIFT_ROR,1,NA,1,0xFFFFFFFF80000001}, + {0xA073,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,0x80000001,SHIFT_ROR,31,NA,1,3}, + {0xA074,INSTR_MOV,{0,0,0,0,0},AL,1,NA,NA,0,0,MAX_64BIT -1,SHIFT_ASR,1,NA,1,MAX_64BIT}, + {0xA075,INSTR_MOV,{0,0,0,0,0},AL,1,NA,NA,0,0,3,SHIFT_ASR,1,NA,1,1}, + {0xA076,INSTR_MOV,{2,R_a5,R_a6,6,8},MI,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2}, + {0xA077,INSTR_MOV,{2,R_a5,R_a6,-4,-8},MI,0,NA,NA,0,0,0x80000001,0,0,2,1,0xFFFFFFFF80000001}, + {0xA078,INSTR_MOV,{1,R_a5,R_a6,-1,-1},LT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2}, + {0xA079,INSTR_MOV,{1,R_a5,R_a6,-1,1},LT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001}, + {0xA080,INSTR_MOV,{1,R_a5,R_a6,-1,-5},GE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,MAX_64BIT-1}, + {0xA081,INSTR_MOV,{1,R_a5,R_a6,5,5},GE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,2,1,0xFFFFFFFF80000000}, + {0xA082,INSTR_MOV,{1,R_a5,R_a6,-1,1},GE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,2,1,2}, + {0xA083,INSTR_MOV,{1,R_a5,R_a6,4,1},LE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,2}, + {0xA084,INSTR_MOV,{1,R_a5,R_a6,-1,-1},LE,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001}, + {0xA085,INSTR_MOV,{1,R_a5,R_a6,-1,1},LE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,2,1,0xFFFFFFFF80000000}, + {0xA086,INSTR_MOV,{1,R_a5,R_a6,1,1},GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2}, + {0xA087,INSTR_MOV,{1,R_a5,R_a6,-1,-3},GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001}, + {0xA088,INSTR_MOV,{1,R_a5,R_a6,-1,0},GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2}, + {0xA089,INSTR_MOV,{1,R_a5,R_a6,-1,-1},GT,0,NA,NA,0,0,0x80000001,0,0,2,1,2}, + {0xA090,INSTR_MOV,{1,R_a5,R_a6,6,1},GT,0,NA,NA,0,0,0x80000001,0,0,2,1,0xFFFFFFFF80000001}, + {0xA091,INSTR_MOV,{1,R_a5,R_a6,-1,1},GT,0,NA,NA,0,0,0x80000001,0,0,2,1,2}, + {0xA092,INSTR_MOV,{1,R_a5,R_a6,1,1},GT,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,2}, + {0xA093,INSTR_MOV,{1,R_a5,R_a6,4,1},GT,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,MAX_64BIT-1}, + {0xA094,INSTR_MOV,{1,R_a5,R_a6,-1,1},GT,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,2}, + {0xA095,INSTR_MOV,{1,R_a5,R_a6,1,-1},HS,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2}, + {0xA096,INSTR_MOV,{1,R_a5,R_a6,-1,1},HS,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001}, + {0xA097,INSTR_MVN,{1,R_a5,R_a6,1,4},HS,0,NA,NA,1,MAX_32BIT-1,NA,NA,NA,2,1,2}, + {0xA098,INSTR_MVN,{1,R_a5,R_a6,-1,1},HS,0,NA,NA,1,MAX_32BIT-1,NA,NA,NA,2,1,1}, + {0xA099,INSTR_MVN,{0,0,0,0,0},AL,0,NA,NA,1,0,NA,NA,NA,2,1,MAX_64BIT}, + {0xA100,INSTR_MVN,{0,0,0,0,0},AL,0,NA,NA,0,NA,MAX_32BIT-1,NA,0,2,1,1}, + {0xA101,INSTR_MVN,{0,0,0,0,0},AL,0,NA,NA,0,NA,0x80000001,NA,0,2,1,0x7FFFFFFE}, + {0xA102,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT,NA,NA,NA,NA,1,0}, + {0xA103,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT-1,NA,NA,NA,NA,1,1}, + {0xA104,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,0,0,NA,1,0}, + {0xA105,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT-1,0,0,NA,1,1}, + {0xA106,INSTR_BIC,{0,0,0,0,0},AL,0,0xF0,NA,0,0,3,SHIFT_ASR,1,NA,1,0xF0}, + {0xA107,INSTR_BIC,{0,0,0,0,0},AL,0,0xF0,NA,0,0,MAX_64BIT,SHIFT_ASR,31,NA,1,0}, + {0xA108,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFABCD0001,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF}, + {0xA109,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFABCD0FFF,NA,NA,NA,1,0x00000FFF}, + {0xA110,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF}, + {0xA111,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,1}, + {0xA112,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFABCD0001,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF}, + {0xA113,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFABCD0FFF,NA,NA,NA,1,0x00000FFF}, + {0xA114,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF}, + {0xA115,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,1}, + {0xA116,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF}, + {0xA117,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF}, + {0xA118,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF}, + {0xA119,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,1}, + {0xA120,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF}, + {0xA121,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF}, + {0xA122,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF}, + {0xA123,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,1}, + {0xA124,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFE}, + {0xA125,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF}, + {0xA126,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF}, + {0xA127,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0}, + {0xA128,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFE}, + {0xA129,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF}, + {0xA130,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF}, + {0xA131,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0}, + {0xA132,INSTR_SMLABB,{0,0,0,0,0},AL,0,1,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFABCD0001,NA,NA,NA,1,0}, + {0xA133,INSTR_SMLABB,{0,0,0,0,0},AL,0,1,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFABCD0FFF,NA,NA,NA,1,0x00001000}, + {0xA134,INSTR_SMLABB,{0,0,0,0,0},AL,0,0xFFFFFFFFFFFFFFFF,0xFFFFFFFFABCD0001,0,NA,0xABCDFFFF,NA,NA,NA,1,0xFFFFFFFFFFFFFFFE}, + {0xA135,INSTR_SMLABB,{0,0,0,0,0},AL,0,0xFFFFFFFFFFFFFFFF,0xFFFFFFFFABCDFFFF,0,NA,0xABCDFFFF,NA,NA,NA,1,0}, + {0xA136,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,0,NA,1,0x00CD0001}, + {0xA137,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,1,NA,1,0x00AB00EF}, + {0xA138,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,2,NA,1,0x000100CD}, + {0xA139,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,3,NA,1,0x00EF00AB}, + {0xA140,INSTR_ADDR_ADD,{0,0,0,0,0},AL,0,0xCFFFFFFFF,NA,0,NA,0x1,SHIFT_LSL,1,NA,1,0xD00000001}, + {0xA141,INSTR_ADDR_ADD,{0,0,0,0,0},AL,0,0x01,NA,0,NA,0x1,SHIFT_LSL,2,NA,1,0x5}, + {0xA142,INSTR_ADDR_ADD,{0,0,0,0,0},AL,0,0xCFFFFFFFF,NA,0,NA,0x1,NA,0,NA,1,0xD00000000}, + {0xA143,INSTR_ADDR_SUB,{0,0,0,0,0},AL,0,0xD00000001,NA,0,NA,0x010000,SHIFT_LSR,15,NA,1,0xCFFFFFFFF}, + {0xA144,INSTR_ADDR_SUB,{0,0,0,0,0},AL,0,0xCFFFFFFFF,NA,0,NA,0x020000,SHIFT_LSR,15,NA,1,0xCFFFFFFFB}, + {0xA145,INSTR_ADDR_SUB,{0,0,0,0,0},AL,0,3,NA,0,NA,0x010000,SHIFT_LSR,15,NA,1,1}, +}; + +dataTransferTest_t dataTransferTests [] = +{ + {0xB000,INSTR_LDR,AL,AL,1,24,0xABCDEF0123456789,0,REG_SCALE_OFFSET,24,NA,NA,NA,NA,NA,0x23456789,0,0,NA,NA,NA}, + {0xB001,INSTR_LDR,AL,AL,1,0,0xABCDEF0123456789,0,IMM12_OFFSET,NA,4,1,0,1,NA,0x23456789,4,0,NA,NA,NA}, + {0xB002,INSTR_LDR,AL,AL,1,0,0xABCDEF0123456789,0,NO_OFFSET,NA,NA,0,0,0,NA,0x23456789,0,0,NA,NA,NA}, + {0xB003,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,REG_SCALE_OFFSET,4064,NA,NA,NA,NA,NA,0x89,0,0,NA,NA,NA}, + {0xB004,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,0,0,1,0,NA,0x67,4065,0,NA,NA,NA}, + {0xB005,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,1,0,1,0,NA,0x45,4065,0,NA,NA,NA}, + {0xB006,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,2,0,1,0,NA,0x23,4065,0,NA,NA,NA}, + {0xB007,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,1,1,0,1,NA,0x67,4066,0,NA,NA,NA}, + {0xB008,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,NO_OFFSET,NA,NA,0,0,0,NA,0x89,0,0,NA,NA,NA}, + {0xB009,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,0,IMM8_OFFSET,NA,2,1,0,1,NA,0x6789,2,0,NA,NA,NA}, + {0xB010,INSTR_LDRH,AL,AL,1,4064,0xABCDEF0123456789,0,REG_OFFSET,4064,0,0,1,0,NA,0x6789,0,0,NA,NA,NA}, + {0xB011,INSTR_LDRH,AL,AL,1,4064,0xABCDEF0123456789,0,REG_OFFSET,4066,0,0,1,0,NA,0x2345,0,0,NA,NA,NA}, + {0xB012,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,0,NO_OFFSET,NA,0,0,0,0,NA,0x6789,0,0,NA,NA,NA}, + {0xB013,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,2,NO_OFFSET,NA,0,0,0,0,NA,0x2345,2,0,NA,NA,NA}, + {0xB014,INSTR_STR,AL,AL,1,2,0xDEADBEEFDEADBEEF,4,IMM12_OFFSET,NA,4,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,8,1,2,8,0xDEAD23456789BEEF}, + {0xB015,INSTR_STR,AL,AL,1,2,0xDEADBEEFDEADBEEF,4,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4,1,2,8,0xDEAD23456789BEEF}, + {0xB016,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,0,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDEAD89EF}, + {0xB017,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,1,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDE89BEEF}, + {0xB018,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,2,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEF89ADBEEF}, + {0xB019,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,4,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,5,1,0,8,0xDEADBEEFDEAD89EF}, + {0xB020,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDEAD89EF}, + {0xB021,INSTR_STRH,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4070,IMM8_OFFSET,NA,2,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,4072,1,4066,8,0xDEAD6789DEADBEEF}, + {0xB022,INSTR_STRH,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF}, +}; + + +void flushcache() +{ + const long base = long(instrMem); + const long curr = base + long(instrMemSize); + __builtin___clear_cache((char*)base, (char*)curr); +} + +void dataOpTest(dataOpTest_t test, ArmToMips64Assembler *a64asm, uint32_t Rd = R_v1, + uint32_t Rn = R_t0, uint32_t Rm = R_t1, uint32_t Rs = R_t2) +{ + int64_t regs[NUM_REGS] = {0}; + int32_t flags[NUM_FLAGS] = {0}; + int64_t savedRegs[NUM_REGS] = {0}; + uint32_t i; + uint32_t op2; + + for(i = 0; i < NUM_REGS; ++i) + { + regs[i] = i; + } + + regs[Rd] = test.RdValue; + regs[Rn] = test.RnValue; + regs[Rs] = test.RsValue; + a64asm->reset(); + if (test.preCond.mode) { + a64asm->set_condition(test.preCond.mode, test.preCond.Rcond1, test.preCond.Rcond2); + regs[test.preCond.Rcond1] = test.preCond.Rcond1Value; + regs[test.preCond.Rcond2] = test.preCond.Rcond2Value; + } + a64asm->prolog(); + if(test.immediate == true) + { + op2 = a64asm->imm(test.immValue); + } + else if(test.immediate == false && test.shiftAmount == 0) + { + op2 = Rm; + regs[Rm] = (int64_t)((int32_t)(test.RmValue)); + } + else + { + op2 = a64asm->reg_imm(Rm, test.shiftMode, test.shiftAmount); + regs[Rm] = (int64_t)((int32_t)(test.RmValue)); + } + switch(test.op) + { + case INSTR_ADD: a64asm->ADD(test.cond, test.setFlags, Rd,Rn,op2); break; + case INSTR_SUB: a64asm->SUB(test.cond, test.setFlags, Rd,Rn,op2); break; + case INSTR_RSB: a64asm->RSB(test.cond, test.setFlags, Rd,Rn,op2); break; + case INSTR_AND: a64asm->AND(test.cond, test.setFlags, Rd,Rn,op2); break; + case INSTR_ORR: a64asm->ORR(test.cond, test.setFlags, Rd,Rn,op2); break; + case INSTR_BIC: a64asm->BIC(test.cond, test.setFlags, Rd,Rn,op2); break; + case INSTR_MUL: a64asm->MUL(test.cond, test.setFlags, Rd,Rm,Rs); break; + case INSTR_MLA: a64asm->MLA(test.cond, test.setFlags, Rd,Rm,Rs,Rn); break; + case INSTR_CMP: a64asm->CMP(test.cond, Rn,op2); break; + case INSTR_MOV: a64asm->MOV(test.cond, test.setFlags,Rd,op2); break; + case INSTR_MVN: a64asm->MVN(test.cond, test.setFlags,Rd,op2); break; + case INSTR_SMULBB:a64asm->SMULBB(test.cond, Rd,Rm,Rs); break; + case INSTR_SMULBT:a64asm->SMULBT(test.cond, Rd,Rm,Rs); break; + case INSTR_SMULTB:a64asm->SMULTB(test.cond, Rd,Rm,Rs); break; + case INSTR_SMULTT:a64asm->SMULTT(test.cond, Rd,Rm,Rs); break; + case INSTR_SMULWB:a64asm->SMULWB(test.cond, Rd,Rm,Rs); break; + case INSTR_SMULWT:a64asm->SMULWT(test.cond, Rd,Rm,Rs); break; + case INSTR_SMLABB:a64asm->SMLABB(test.cond, Rd,Rm,Rs,Rn); break; + case INSTR_UXTB16:a64asm->UXTB16(test.cond, Rd,Rm,test.shiftAmount); break; + case INSTR_ADDR_ADD: a64asm->ADDR_ADD(test.cond, test.setFlags, Rd,Rn,op2); break; + case INSTR_ADDR_SUB: a64asm->ADDR_SUB(test.cond, test.setFlags, Rd,Rn,op2); break; + default: printf("Error"); return; + } + a64asm->epilog(0); + a64asm->fix_branches(); + flushcache(); + + asm_function_t asm_function = (asm_function_t)(instrMem); + + for(i = 0; i < NUM_REGS; ++i) + savedRegs[i] = regs[i]; + + asm_mips_test_jacket(asm_function, regs, flags); + + /* Check if all regs except Rd is same */ + for(i = 0; i < NUM_REGS; ++i) + { + if((i == Rd) || i == 2) continue; + if(regs[i] != savedRegs[i]) + { + printf("Test %x failed Reg(%d) tampered Expected(0x%" PRIx64 ")," + "Actual(0x%" PRIx64 ") t\n", test.id, i, savedRegs[i], + regs[i]); + exit(0); + return; + } + } + + if(test.checkRd == 1 && regs[Rd] != test.postRdValue) + { + printf("Test %x failed, Expected(%" PRIx64 "), Actual(%" PRIx64 ")\n", + test.id, test.postRdValue, regs[Rd]); + exit(0); + } + else + { + printf("Test %x passed\n", test.id); + } +} + + +void dataTransferTest(dataTransferTest_t test, ARMAssemblerInterface *a64asm, + uint32_t Rd = R_v1, uint32_t Rn = R_t0,uint32_t Rm = R_t1) +{ + int64_t regs[NUM_REGS] = {0}; + int64_t savedRegs[NUM_REGS] = {0}; + int32_t flags[NUM_FLAGS] = {0}; + uint32_t i; + for(i = 0; i < NUM_REGS; ++i) + { + regs[i] = i; + } + + uint32_t op2; + + regs[Rd] = test.RdValue; + regs[Rn] = (uint64_t)(&dataMem[test.RnValue]); + regs[Rm] = test.RmValue; + flags[test.preFlag] = 1; + + if(test.setMem == true) + { + unsigned char *mem = (unsigned char *)&dataMem[test.memOffset]; + uint64_t value = test.memValue; + for(int j = 0; j < 8; ++j) + { + mem[j] = value & 0x00FF; + value >>= 8; + } + } + a64asm->reset(); + a64asm->prolog(); + if(test.offsetType == REG_SCALE_OFFSET) + { + op2 = a64asm->reg_scale_pre(Rm); + } + else if(test.offsetType == REG_OFFSET) + { + op2 = a64asm->reg_pre(Rm); + } + else if(test.offsetType == IMM12_OFFSET && test.preIndex == true) + { + op2 = a64asm->immed12_pre(test.immValue, test.writeBack); + } + else if(test.offsetType == IMM12_OFFSET && test.postIndex == true) + { + op2 = a64asm->immed12_post(test.immValue); + } + else if(test.offsetType == IMM8_OFFSET && test.preIndex == true) + { + op2 = a64asm->immed8_pre(test.immValue, test.writeBack); + } + else if(test.offsetType == IMM8_OFFSET && test.postIndex == true) + { + op2 = a64asm->immed8_post(test.immValue); + } + else if(test.offsetType == NO_OFFSET) + { + op2 = a64asm->__immed12_pre(0); + } + else + { + printf("Error - Unknown offset\n"); return; + } + + switch(test.op) + { + case INSTR_LDR: a64asm->LDR(test.cond, Rd,Rn,op2); break; + case INSTR_LDRB: a64asm->LDRB(test.cond, Rd,Rn,op2); break; + case INSTR_LDRH: a64asm->LDRH(test.cond, Rd,Rn,op2); break; + case INSTR_ADDR_LDR: a64asm->ADDR_LDR(test.cond, Rd,Rn,op2); break; + case INSTR_STR: a64asm->STR(test.cond, Rd,Rn,op2); break; + case INSTR_STRB: a64asm->STRB(test.cond, Rd,Rn,op2); break; + case INSTR_STRH: a64asm->STRH(test.cond, Rd,Rn,op2); break; + case INSTR_ADDR_STR: a64asm->ADDR_STR(test.cond, Rd,Rn,op2); break; + default: printf("Error"); return; + } + a64asm->epilog(0); + flushcache(); + + asm_function_t asm_function = (asm_function_t)(instrMem); + + for(i = 0; i < NUM_REGS; ++i) + savedRegs[i] = regs[i]; + + asm_mips_test_jacket(asm_function, regs, flags); + + /* Check if all regs except Rd/Rn are same */ + for(i = 0; i < NUM_REGS; ++i) + { + if(i == Rd || i == Rn || i == R_v0) continue; + + if(regs[i] != savedRegs[i]) + { + printf("Test %x failed Reg(%d) tampered" + " Expected(0x%" PRIx64 "), Actual(0x%" PRIx64 ") t\n", + test.id, i, savedRegs[i], regs[i]); + return; + } + } + + if((uint64_t)regs[Rd] != test.postRdValue) + { + printf("Test %x failed, " + "Expected in Rd(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n", + test.id, test.postRdValue, regs[Rd]); + } + else if((uint64_t)regs[Rn] != (uint64_t)(&dataMem[test.postRnValue])) + { + printf("Test %x failed, " + "Expected in Rn(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n", + test.id, test.postRnValue, regs[Rn] - (uint64_t)dataMem); + } + else if(test.checkMem == true) + { + unsigned char *addr = (unsigned char *)&dataMem[test.postMemOffset]; + uint64_t value; + value = 0; + for(uint32_t j = 0; j < test.postMemLength; ++j) + value = (value << 8) | addr[test.postMemLength-j-1]; + if(value != test.postMemValue) + { + printf("Test %x failed, " + "Expected in Mem(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n", + test.id, test.postMemValue, value); + } + else + { + printf("Test %x passed\n", test.id); + } + } + else + { + printf("Test %x passed\n", test.id); + } +} + +int main(void) +{ + uint32_t i; + + /* Allocate memory to store instructions generated by ArmToArm64Assembler */ + { + int fd = ashmem_create_region("code cache", instrMemSize); + if(fd < 0) { + printf("IF < 0\n"); + printf("Creating code cache, ashmem_create_region " + "failed with error '%s'", strerror(errno)); + } + instrMem = mmap(NULL, instrMemSize, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE, fd, 0); + } + + ArmToMips64Assembler a64asm(instrMem); + + if(TESTS_DATAOP_ENABLE) + { + printf("Running data processing tests\n"); + for(i = 0; i < sizeof(dataOpTests)/sizeof(dataOpTest_t); ++i) { + dataOpTest(dataOpTests[i], &a64asm); + } + } + + if(TESTS_DATATRANSFER_ENABLE) + { + printf("Running data transfer tests\n"); + for(i = 0; i < sizeof(dataTransferTests)/sizeof(dataTransferTest_t); ++i) + dataTransferTest(dataTransferTests[i], &a64asm); + } + + return 0; +} diff --git a/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk new file mode 100644 index 000000000..7d4177ecb --- /dev/null +++ b/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk @@ -0,0 +1,18 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + col32cb16blend_test.c \ + ../../../arch-mips64/col32cb16blend.S + +LOCAL_SHARED_LIBRARIES := + +LOCAL_C_INCLUDES := + +LOCAL_MODULE:= test-pixelflinger-mips64-col32cb16blend + +LOCAL_MODULE_TAGS := tests + +LOCAL_MULTILIB := 64 + +include $(BUILD_NATIVE_TEST) diff --git a/libpixelflinger/tests/arch-mips64/col32cb16blend/col32cb16blend_test.c b/libpixelflinger/tests/arch-mips64/col32cb16blend/col32cb16blend_test.c new file mode 100644 index 000000000..066eab679 --- /dev/null +++ b/libpixelflinger/tests/arch-mips64/col32cb16blend/col32cb16blend_test.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> + + +#define ARGB_8888_MAX 0xFFFFFFFF +#define ARGB_8888_MIN 0x00000000 +#define RGB_565_MAX 0xFFFF +#define RGB_565_MIN 0x0000 + +struct test_t +{ + char name[256]; + uint32_t src_color; + uint16_t dst_color; + size_t count; +}; + +struct test_t tests[] = +{ + {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1}, + {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2}, + {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3}, + {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4}, + {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1}, + {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2}, + {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3}, + {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4}, + {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5}, + {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10} +}; + +void scanline_col32cb16blend_mips64(uint16_t *dst, uint32_t src, size_t count); +void scanline_col32cb16blend_c(uint16_t * dst, uint32_t src, size_t count) +{ + uint32_t srcAlpha = (src>>24); + uint32_t f = 0x100 - (srcAlpha + (srcAlpha>>7)); + + while (count--) + { + uint16_t d = *dst; + int dstR = (d>>11)&0x1f; + int dstG = (d>>5)&0x3f; + int dstB = (d)&0x1f; + int srcR = (src >> ( 3))&0x1F; + int srcG = (src >> ( 8+2))&0x3F; + int srcB = (src >> (16+3))&0x1F; + srcR += (f*dstR)>>8; + srcG += (f*dstG)>>8; + srcB += (f*dstB)>>8; + *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB); + } +} + +void scanline_col32cb16blend_test() +{ + uint16_t dst_c[16], dst_asm[16]; + uint32_t i, j; + + for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i) + { + struct test_t test = tests[i]; + + printf("Testing - %s:",test.name); + + memset(dst_c, 0, sizeof(dst_c)); + memset(dst_asm, 0, sizeof(dst_asm)); + + for(j = 0; j < test.count; ++j) + { + dst_c[j] = test.dst_color; + dst_asm[j] = test.dst_color; + } + + + scanline_col32cb16blend_c(dst_c, test.src_color, test.count); + scanline_col32cb16blend_mips64(dst_asm, test.src_color, test.count); + + if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0) + printf("Passed\n"); + else + printf("Failed\n"); + + for(j = 0; j < test.count; ++j) + { + printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]); + } + } +} + +int main() +{ + scanline_col32cb16blend_test(); + return 0; +} diff --git a/libpixelflinger/tests/arch-mips64/disassembler/Android.mk b/libpixelflinger/tests/arch-mips64/disassembler/Android.mk new file mode 100644 index 000000000..4e72b5784 --- /dev/null +++ b/libpixelflinger/tests/arch-mips64/disassembler/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + mips64_disassembler_test.cpp \ + ../../../codeflinger/mips64_disassem.c + +LOCAL_SHARED_LIBRARIES := + +LOCAL_MODULE:= test-pixelflinger-mips64-disassembler-test + +LOCAL_MODULE_TAGS := tests + +LOCAL_MULTILIB := 64 + +include $(BUILD_NATIVE_TEST) diff --git a/libpixelflinger/tests/arch-mips64/disassembler/mips64_disassembler_test.cpp b/libpixelflinger/tests/arch-mips64/disassembler/mips64_disassembler_test.cpp new file mode 100644 index 000000000..41f6f3ef9 --- /dev/null +++ b/libpixelflinger/tests/arch-mips64/disassembler/mips64_disassembler_test.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <stdio.h> +#include <inttypes.h> +#include <string.h> +#include "../../../codeflinger/mips64_disassem.h" + +//typedef uint64_t db_addr_t; +//db_addr_t mips_disassem(db_addr_t loc, char *di_buffer, int alt_format); + +struct test_table_entry_t +{ + uint32_t code; + const char *instr; +}; + +static test_table_entry_t test_table [] = +{ + { 0x00011020, "add\tv0,zero,at" }, + { 0x00832820, "add\ta1,a0,v1" }, + { 0x00c74020, "add\ta4,a2,a3" }, + { 0x012a5820, "add\ta7,a5,a6" }, + { 0x258dffff, "addiu\tt1,t0,-1" }, + { 0x25cf0004, "addiu\tt3,t2,4" }, + { 0x02119021, "addu\ts2,s0,s1" }, + { 0x0274a821, "addu\ts5,s3,s4" }, + { 0x02d7c024, "and\tt8,s6,s7" }, + { 0x333aff00, "andi\tk0,t9,0xff00" }, + { 0x3f7cffff, "aui\tgp,k1,-1" }, + { 0x3c1dffff, "lui\tsp,0xffff" }, + { 0x00e04051, "clo\ta4,a3" }, + { 0x01205050, "clz\ta6,a5" }, + { 0x016c682c, "dadd\tt1,a7,t0" }, + { 0x65cf0008, "daddiu\tt3,t2,8" }, + { 0x0211902d, "daddu\ts2,s0,s1" }, + { 0x7e741403, "dext\ts4,s3,16,3" }, + { 0x7eb6f801, "dextm\ts6,s5,0,64" }, + { 0x7ef87c02, "dextu\tt8,s7,48,16" }, + { 0x7f3a8207, "dins\tk0,t9,8,9" }, + { 0x7f7c0005, "dinsm\tgp,k1,0,33" }, + { 0x7fbe0806, "dinsu\ts8,sp,32,2" }, + { 0x03e1102e, "dsub\tv0,ra,at" }, + { 0x0064282f, "dsubu\ta1,v1,a0" }, + { 0x7cc77a00, "ext\ta3,a2,8,16" }, + { 0x7d09fc04, "ins\ta5,a4,16,16" }, + { 0x00200009, "jr\tat" }, + { 0x00201009, "jalr\tv0,at" }, + { 0x0020f809, "jalr\tat" }, + { 0x8082fff0, "lb\tv0,-16(a0)" }, + { 0x916c0008, "lbu\tt0,8(a7)" }, + { 0xdfa3ffe8, "ld\tv1,-24(sp)" }, + { 0x84850080, "lh\ta1,128(a0)" }, + { 0x94c7ff80, "lhu\ta3,-128(a2)" }, + { 0x8d09000c, "lw\ta5,12(a4)" }, + { 0x9d4bfff4, "lwu\ta7,-12(a6)" }, + { 0x00620898, "mul\tat,v1,v0" }, + { 0x006208d8, "muh\tat,v1,v0" }, + { 0x00620899, "mulu\tat,v1,v0" }, + { 0x006208d9, "muhu\tat,v1,v0" }, + { 0x00000000, "nop" }, + { 0x02329827, "nor\ts3,s1,s2" }, + { 0x0295b025, "or\ts6,s4,s5" }, + { 0x36f0ff00, "ori\ts0,s7,0xff00" }, + { 0x7c03103b, "rdhwr\tv0,v1" }, + { 0x00242a02, "rotr\ta1,a0,8" }, + { 0x00c74046, "rotrv\ta4,a3,a2" }, + { 0xa12afff0, "sb\ta6,-16(a5)" }, + { 0xfd6c0100, "sd\tt0,256(a7)" }, + { 0x7c0d7420, "seb\tt2,t1" }, + { 0x7c0f8620, "seh\ts0,t3" }, + { 0x02329835, "seleqz\ts3,s1,s2" }, + { 0x0295b037, "selnez\ts6,s4,s5" }, + { 0xa6f84000, "sh\tt8,16384(s7)" }, + { 0x0019d100, "sll\tk0,t9,4" }, + { 0x037ce804, "sllv\tsp,gp,k1" }, + { 0x03df082a, "slt\tat,s8,ra" }, + { 0x28430007, "slti\tv1,v0,7" }, + { 0x2c850020, "sltiu\ta1,a0,32" }, + { 0x00c7402b, "sltu\ta4,a2,a3" }, + { 0x00095103, "sra\ta6,a5,4" }, + { 0x016c6807, "srav\tt1,t0,a7" }, + { 0x000e7a02, "srl\tt3,t2,8" }, + { 0x02119006, "srlv\ts2,s1,s0" }, + { 0x0274a822, "sub\ts5,s3,s4" }, + { 0x02d7c023, "subu\tt8,s6,s7" }, + { 0xaf3afffc, "sw\tk0,-4(t9)" }, + { 0x7c1be0a0, "wsbh\tgp,k1" }, + { 0x03bef826, "xor\tra,sp,s8" }, + { 0x3801ffff, "li\tat,0xffff" }, + { 0x3843ffff, "xori\tv1,v0,0xffff" }, +}; + +struct test_branches_table_entry_t +{ + uint32_t code; + const char *instr; + int16_t offset; +}; + +static test_branches_table_entry_t test_branches_table [] = { + { 0x1000ffff, "b\t", 0xffff }, + { 0x13df0008, "beq\ts8,ra,", 0x8 }, + { 0x042100ff, "bgez\tat,", 0xff }, + { 0x1c40ff00, "bgtz\tv0,", 0xff00 }, + { 0x18605555, "blez\tv1,", 0x5555 }, + { 0x0480aaaa, "bltz\ta0,", 0xaaaa }, + { 0x14a68888, "bne\ta1,a2,", 0x8888 }, +}; + +struct test_jump_table_entry_t +{ + uint32_t code; + const char *instr; + int32_t offset; +}; + +static test_jump_table_entry_t test_jump_table [] = { + { 0x0956ae66, "j\t", 0x156ae66 }, + { 0x0d56ae66, "jal\t", 0x156ae66 }, +}; + +int main() +{ + char instr[256]; + uint32_t failed = 0; + + for(uint32_t i = 0; i < sizeof(test_table)/sizeof(test_table_entry_t); ++i) + { + test_table_entry_t *test; + test = &test_table[i]; + mips_disassem(&test->code, instr, 0); + if(strcmp(instr, test->instr) != 0) + { + printf("Test Failed \n" + "Code : 0x%0x\n" + "Expected : %s\n" + "Actual : %s\n", test->code, test->instr, instr); + failed++; + } + } + for(uint32_t i = 0; i < sizeof(test_branches_table)/sizeof(test_branches_table_entry_t); ++i) + { + test_branches_table_entry_t *test; + test = &test_branches_table[i]; + mips_disassem(&test->code, instr, 0); + //printf("DBG code address: %lx\n", (uint64_t)(&test->code)); + uint64_t loc = (uint64_t)test + 4 + (test->offset << 2); + //printf("DBG loc: %lx\n", loc); + char temp[256], address[16]; + strcpy(temp, test->instr); + sprintf(address, "0x%lx", loc); + strcat(temp, address); + if(strcmp(instr, temp) != 0) + { + printf("Test Failed \n" + "Code : 0x%0x\n" + "Expected : %s\n" + "Actual : %s\n", test->code, temp, instr); + failed++; + } + } + for(uint32_t i = 0; i < sizeof(test_jump_table)/sizeof(test_jump_table_entry_t); ++i) + { + test_jump_table_entry_t *test; + test = &test_jump_table[i]; + mips_disassem(&test->code, instr, 0); + //printf("DBG code address: %lx\n", (uint64_t)(&test->code)); + uint64_t loc = ((uint64_t)test & 0xfffffffff0000000) | (test->offset << 2); + //printf("DBG loc: %lx\n", loc); + char temp[256], address[16]; + strcpy(temp, test->instr); + sprintf(address, "0x%08lx", loc); + strcat(temp, address); + if(strcmp(instr, temp) != 0) + { + printf("Test Failed \n" + "Code : 0x%0x\n" + "Expected : '%s'\n" + "Actual : '%s'\n", test->code, temp, instr); + failed++; + } + } + if(failed == 0) + { + printf("All tests PASSED\n"); + return 0; + } + else + { + printf("%d tests FAILED\n", failed); + return -1; + } +} diff --git a/libpixelflinger/tests/codegen/codegen.cpp b/libpixelflinger/tests/codegen/codegen.cpp index 148b6f44d..efa6d87bf 100644 --- a/libpixelflinger/tests/codegen/codegen.cpp +++ b/libpixelflinger/tests/codegen/codegen.cpp @@ -11,16 +11,18 @@ #include "codeflinger/ARMAssembler.h" #if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6 #include "codeflinger/MIPSAssembler.h" +#elif defined(__mips__) && defined(__LP64__) && __mips_isa_rev == 6 +#include "codeflinger/MIPS64Assembler.h" #endif #include "codeflinger/Arm64Assembler.h" -#if defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__) +#if defined(__arm__) || (defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || (defined(__LP64__) && __mips_isa_rev == 6))) || defined(__aarch64__) # define ANDROID_ARM_CODEGEN 1 #else # define ANDROID_ARM_CODEGEN 0 #endif -#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6 +#if defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || (defined(__LP64__) && __mips_isa_rev == 6)) #define ASSEMBLY_SCRATCH_SIZE 4096 #elif defined(__aarch64__) #define ASSEMBLY_SCRATCH_SIZE 8192 @@ -58,6 +60,10 @@ static void ggl_test_codegen(uint32_t n, uint32_t p, uint32_t t0, uint32_t t1) GGLAssembler assembler( new ArmToMipsAssembler(a) ); #endif +#if defined(__mips__) && defined(__LP64__) && __mips_isa_rev == 6 + GGLAssembler assembler( new ArmToMips64Assembler(a) ); +#endif + #if defined(__aarch64__) GGLAssembler assembler( new ArmToArm64Assembler(a) ); #endif diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp index 55cd68773..2c409dc27 100644 --- a/libsync/tests/sync_test.cpp +++ b/libsync/tests/sync_test.cpp @@ -50,7 +50,7 @@ public: bool isValid() const { if (m_fdInitialized) { int status = fcntl(m_fd, F_GETFD, 0); - if (status == 0) + if (status >= 0) return true; else return false; @@ -92,7 +92,7 @@ public: bool isValid() const { if (m_fdInitialized) { int status = fcntl(m_fd, F_GETFD, 0); - if (status == 0) + if (status >= 0) return true; else return false; diff --git a/libutils/Android.mk b/libutils/Android.mk index bae16bba2..631b5a3b4 100644 --- a/libutils/Android.mk +++ b/libutils/Android.mk @@ -53,6 +53,7 @@ LOCAL_CFLAGS += $(host_commonCflags) LOCAL_CFLAGS_windows := -DMB_CUR_MAX=1 LOCAL_MULTILIB := both LOCAL_MODULE_HOST_OS := darwin linux windows +LOCAL_C_INCLUDES += external/safe-iop/include include $(BUILD_HOST_STATIC_LIBRARY) @@ -85,6 +86,7 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE := libutils LOCAL_CLANG := true LOCAL_SANITIZE := integer +LOCAL_C_INCLUDES += external/safe-iop/include include $(BUILD_STATIC_LIBRARY) # For the device, shared @@ -98,6 +100,7 @@ LOCAL_SHARED_LIBRARIES := \ libdl \ liblog LOCAL_CFLAGS := -Werror +LOCAL_C_INCLUDES += external/safe-iop/include LOCAL_CLANG := true LOCAL_SANITIZE := integer diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp index 0bfb520e9..699da7469 100644 --- a/libutils/CallStack.cpp +++ b/libutils/CallStack.cpp @@ -16,11 +16,12 @@ #define LOG_TAG "CallStack" +#include <memory> + #include <utils/CallStack.h> #include <utils/Printer.h> #include <utils/Errors.h> #include <utils/Log.h> -#include <UniquePtr.h> #include <backtrace/Backtrace.h> @@ -40,7 +41,7 @@ CallStack::~CallStack() { void CallStack::update(int32_t ignoreDepth, pid_t tid) { mFrameLines.clear(); - UniquePtr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid)); + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid)); if (!backtrace->Unwind(ignoreDepth)) { ALOGW("%s: Failed to unwind callstack.", __FUNCTION__); } diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp index 2ac158b9f..e8d40ed20 100644 --- a/libutils/VectorImpl.cpp +++ b/libutils/VectorImpl.cpp @@ -21,6 +21,7 @@ #include <stdio.h> #include <cutils/log.h> +#include <safe_iop.h> #include <utils/Errors.h> #include <utils/VectorImpl.h> @@ -86,14 +87,19 @@ VectorImpl& VectorImpl::operator = (const VectorImpl& rhs) void* VectorImpl::editArrayImpl() { if (mStorage) { - SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage)->attemptEdit(); - if (sb == 0) { - sb = SharedBuffer::alloc(capacity() * mItemSize); - if (sb) { - _do_copy(sb->data(), mStorage, mCount); - release_storage(); - mStorage = sb->data(); - } + const SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage); + SharedBuffer* editable = sb->attemptEdit(); + if (editable == 0) { + // If we're here, we're not the only owner of the buffer. + // We must make a copy of it. + editable = SharedBuffer::alloc(sb->size()); + // Fail instead of returning a pointer to storage that's not + // editable. Otherwise we'd be editing the contents of a buffer + // for which we're not the only owner, which is undefined behaviour. + LOG_ALWAYS_FATAL_IF(editable == NULL); + _do_copy(editable->data(), mStorage, mCount); + release_storage(); + mStorage = editable->data(); } } return mStorage; @@ -329,13 +335,15 @@ const void* VectorImpl::itemLocation(size_t index) const ssize_t VectorImpl::setCapacity(size_t new_capacity) { - size_t current_capacity = capacity(); - ssize_t amount = new_capacity - size(); - if (amount <= 0) { - // we can't reduce the capacity - return current_capacity; - } - SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); + // The capacity must always be greater than or equal to the size + // of this vector. + if (new_capacity <= size()) { + return capacity(); + } + + size_t new_allocation_size = 0; + LOG_ALWAYS_FATAL_IF(!safe_mul(&new_allocation_size, new_capacity, mItemSize)); + SharedBuffer* sb = SharedBuffer::alloc(new_allocation_size); if (sb) { void* array = sb->data(); _do_copy(array, mStorage, size()); @@ -377,9 +385,28 @@ void* VectorImpl::_grow(size_t where, size_t amount) "[%p] _grow: where=%d, amount=%d, count=%d", this, (int)where, (int)amount, (int)mCount); // caller already checked - const size_t new_size = mCount + amount; + size_t new_size; + LOG_ALWAYS_FATAL_IF(!safe_add(&new_size, mCount, amount), "new_size overflow"); + if (capacity() < new_size) { - const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2); + // NOTE: This implementation used to resize vectors as per ((3*x + 1) / 2) + // (sigh..). Also note, the " + 1" was necessary to handle the special case + // where x == 1, where the resized_capacity will be equal to the old + // capacity without the +1. The old calculation wouldn't work properly + // if x was zero. + // + // This approximates the old calculation, using (x + (x/2) + 1) instead. + size_t new_capacity = 0; + LOG_ALWAYS_FATAL_IF(!safe_add(&new_capacity, new_size, (new_size / 2)), + "new_capacity overflow"); + LOG_ALWAYS_FATAL_IF(!safe_add(&new_capacity, new_capacity, static_cast<size_t>(1u)), + "new_capacity overflow"); + new_capacity = max(kMinVectorCapacity, new_capacity); + + size_t new_alloc_size = 0; + LOG_ALWAYS_FATAL_IF(!safe_mul(&new_alloc_size, new_capacity, mItemSize), + "new_alloc_size overflow"); + // ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity); if ((mStorage) && (mCount==where) && @@ -387,14 +414,14 @@ void* VectorImpl::_grow(size_t where, size_t amount) (mFlags & HAS_TRIVIAL_DTOR)) { const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage); - SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); + SharedBuffer* sb = cur_sb->editResize(new_alloc_size); if (sb) { mStorage = sb->data(); } else { return NULL; } } else { - SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); + SharedBuffer* sb = SharedBuffer::alloc(new_alloc_size); if (sb) { void* array = sb->data(); if (where != 0) { @@ -436,10 +463,19 @@ void VectorImpl::_shrink(size_t where, size_t amount) "[%p] _shrink: where=%d, amount=%d, count=%d", this, (int)where, (int)amount, (int)mCount); // caller already checked - const size_t new_size = mCount - amount; - if (new_size*3 < capacity()) { - const size_t new_capacity = max(kMinVectorCapacity, new_size*2); -// ALOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity); + size_t new_size; + LOG_ALWAYS_FATAL_IF(!safe_sub(&new_size, mCount, amount)); + + if (new_size < (capacity() / 2)) { + // NOTE: (new_size * 2) is safe because capacity didn't overflow and + // new_size < (capacity / 2)). + const size_t new_capacity = max(kMinVectorCapacity, new_size * 2); + + // NOTE: (new_capacity * mItemSize), (where * mItemSize) and + // ((where + amount) * mItemSize) beyond this point are safe because + // we are always reducing the capacity of the underlying SharedBuffer. + // In other words, (old_capacity * mItemSize) did not overflow, and + // where < (where + amount) < new_capacity < old_capacity. if ((where == new_size) && (mFlags & HAS_TRIVIAL_COPY) && (mFlags & HAS_TRIVIAL_DTOR)) diff --git a/libutils/tests/Android.mk b/libutils/tests/Android.mk index cb9e8a255..8f07f1ad1 100644 --- a/libutils/tests/Android.mk +++ b/libutils/tests/Android.mk @@ -38,3 +38,11 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ include $(BUILD_NATIVE_TEST) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libutils_tests_host +LOCAL_SRC_FILES := Vector_test.cpp +LOCAL_STATIC_LIBRARIES := libutils liblog + +include $(BUILD_HOST_NATIVE_TEST) diff --git a/libutils/tests/Vector_test.cpp b/libutils/tests/Vector_test.cpp index 0ba7161aa..d9b32f9e9 100644 --- a/libutils/tests/Vector_test.cpp +++ b/libutils/tests/Vector_test.cpp @@ -16,6 +16,8 @@ #define LOG_TAG "Vector_test" +#define __STDC_LIMIT_MACROS +#include <stdint.h> #include <utils/Vector.h> #include <cutils/log.h> #include <gtest/gtest.h> @@ -71,5 +73,80 @@ TEST_F(VectorTest, CopyOnWrite_CopyAndAddElements) { EXPECT_EQ(other[3], 5); } +// TODO: gtest isn't capable of parsing Abort messages formatted by +// Android (fails differently on host and target), so we always need to +// use an empty error message for death tests. +TEST_F(VectorTest, SetCapacity_Overflow) { + Vector<int> vector; + EXPECT_DEATH(vector.setCapacity(SIZE_MAX / sizeof(int) + 1), ""); +} + +TEST_F(VectorTest, SetCapacity_ShrinkBelowSize) { + Vector<int> vector; + vector.add(1); + vector.add(2); + vector.add(3); + vector.add(4); + + vector.setCapacity(8); + ASSERT_EQ(8, vector.capacity()); + vector.setCapacity(2); + ASSERT_EQ(8, vector.capacity()); +} + +// NOTE: All of the tests below are useless because of the "TODO" above. +// We have no way of knowing *why* the process crashed. Given that we're +// inserting a NULL array, we'll fail with a SIGSEGV eventually. We need +// the ability to make assertions on the abort message to make sure we're +// failing for the right reasons. +TEST_F(VectorTest, _grow_OverflowSize) { + Vector<int> vector; + vector.add(1); + + // Checks that the size calculation (not the capacity calculation) doesn't + // overflow : the size here will be (1 + SIZE_MAX). + // + // EXPECT_DEATH(vector.insertArrayAt(NULL, 0, SIZE_MAX), "new_size_overflow"); + EXPECT_DEATH(vector.insertArrayAt(NULL, 0, SIZE_MAX), ""); +} + +TEST_F(VectorTest, _grow_OverflowCapacityDoubling) { + Vector<int> vector; + + // This should fail because the calculated capacity will overflow even though + // the size of the vector doesn't. + // + // EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX - 1)), "new_capacity_overflow"); + EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX - 1)), ""); +} + +TEST_F(VectorTest, _grow_OverflowBufferAlloc) { + Vector<int> vector; + // This should fail because the capacity * sizeof(int) overflows, even + // though the capacity itself doesn't. + // + // EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX / 2)), "new_alloc_size overflow"); + EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX / 2)), ""); +} + +TEST_F(VectorTest, editArray_Shared) { + Vector<int> vector1; + vector1.add(1); + vector1.add(2); + vector1.add(3); + vector1.add(4); + + Vector<int> vector2 = vector1; + ASSERT_EQ(vector1.array(), vector2.array()); + // We must make a copy here, since we're not the exclusive owners + // of this array. + ASSERT_NE(vector1.editArray(), vector2.editArray()); + + // Vector doesn't implement operator ==. + ASSERT_EQ(vector1.size(), vector2.size()); + for (size_t i = 0; i < vector1.size(); ++i) { + EXPECT_EQ(vector1[i], vector2[i]); + } +} } // namespace android diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc index 22a7c53b2..f117cc5af 100644 --- a/libziparchive/zip_writer.cc +++ b/libziparchive/zip_writer.cc @@ -267,12 +267,12 @@ int32_t ZipWriter::CompressBytes(FileInfo* file, const void* data, size_t len) { if (z_stream_->avail_out == 0) { // The output is full, let's write it to disk. - size_t dataToWrite = z_stream_->next_out - buffer_.data(); - if (fwrite(buffer_.data(), 1, dataToWrite, file_) != dataToWrite) { + size_t write_bytes = z_stream_->next_out - buffer_.data(); + if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) { return HandleError(kIoError); } - file->compressed_size += dataToWrite; - current_offset_ += dataToWrite; + file->compressed_size += write_bytes; + current_offset_ += write_bytes; // Reset the output buffer for the next input. z_stream_->next_out = buffer_.data(); @@ -288,18 +288,32 @@ int32_t ZipWriter::FlushCompressedBytes(FileInfo* file) { assert(z_stream_->next_out != nullptr); assert(z_stream_->avail_out != 0); - int zerr = deflate(z_stream_.get(), Z_FINISH); + // Keep deflating while there isn't enough space in the buffer to + // to complete the compress. + int zerr; + while ((zerr = deflate(z_stream_.get(), Z_FINISH)) == Z_OK) { + assert(z_stream_->avail_out == 0); + size_t write_bytes = z_stream_->next_out - buffer_.data(); + if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) { + return HandleError(kIoError); + } + file->compressed_size += write_bytes; + current_offset_ += write_bytes; + + z_stream_->next_out = buffer_.data(); + z_stream_->avail_out = buffer_.size(); + } if (zerr != Z_STREAM_END) { return HandleError(kZlibError); } - size_t dataToWrite = z_stream_->next_out - buffer_.data(); - if (dataToWrite != 0) { - if (fwrite(buffer_.data(), 1, dataToWrite, file_) != dataToWrite) { + size_t write_bytes = z_stream_->next_out - buffer_.data(); + if (write_bytes != 0) { + if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) { return HandleError(kIoError); } - file->compressed_size += dataToWrite; - current_offset_ += dataToWrite; + file->compressed_size += write_bytes; + current_offset_ += write_bytes; } z_stream_.reset(); return kNoError; diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc index 046f19572..f752b7e6e 100644 --- a/libziparchive/zip_writer_test.cc +++ b/libziparchive/zip_writer_test.cc @@ -20,6 +20,7 @@ #include <base/test_utils.h> #include <gtest/gtest.h> #include <memory> +#include <vector> struct zipwriter : public ::testing::Test { TemporaryFile* temp_file_; @@ -168,3 +169,40 @@ TEST_F(zipwriter, WriteCompressedZipWithOneFile) { CloseArchive(handle); } + +TEST_F(zipwriter, WriteCompressedZipFlushFull) { + // This exact data will cause the Finish() to require multiple calls + // to deflate() because the ZipWriter buffer isn't big enough to hold + // the entire compressed data buffer. + constexpr size_t kBufSize = 10000000; + std::vector<uint8_t> buffer(kBufSize); + size_t prev = 1; + for (size_t i = 0; i < kBufSize; i++) { + buffer[i] = i + prev; + prev = i; + } + + ZipWriter writer(file_); + ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress)); + ASSERT_EQ(0, writer.WriteBytes(buffer.data(), buffer.size())); + ASSERT_EQ(0, writer.FinishEntry()); + ASSERT_EQ(0, writer.Finish()); + + ASSERT_GE(0, lseek(fd_, 0, SEEK_SET)); + + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false)); + + ZipEntry data; + ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data)); + EXPECT_EQ(kCompressDeflated, data.method); + EXPECT_EQ(kBufSize, data.uncompressed_length); + + std::vector<uint8_t> decompress(kBufSize); + memset(decompress.data(), 0, kBufSize); + ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(), decompress.size())); + EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize)) + << "Input buffer and output buffer are different."; + + CloseArchive(handle); +} diff --git a/lmkd/lmkd.rc b/lmkd/lmkd.rc index 83c5ff0f9..3bb84abf6 100644 --- a/lmkd/lmkd.rc +++ b/lmkd/lmkd.rc @@ -1,4 +1,6 @@ service lmkd /system/bin/lmkd class core + group root readproc critical socket lmkd seqpacket 0660 system system + writepid /dev/cpuset/system-background/tasks diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index cb9a85bff..4d7adf139 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp @@ -399,6 +399,10 @@ static log_time lastLogTime(char *outputFileName) { } log_time now(CLOCK_REALTIME); + bool monotonic = android_log_timestamp() == 'm'; + if (monotonic) { + now = log_time(CLOCK_MONOTONIC); + } std::string directory; char *file = strrchr(outputFileName, '/'); @@ -417,7 +421,11 @@ static log_time lastLogTime(char *outputFileName) { struct dirent *dp; while ((dp = readdir(dir.get())) != NULL) { if ((dp->d_type != DT_REG) - || strncmp(dp->d_name, file, len) + // If we are using realtime, check all files that match the + // basename for latest time. If we are using monotonic time + // then only check the main file because time cycles on + // every reboot. + || strncmp(dp->d_name, file, len + monotonic) || (dp->d_name[len] && ((dp->d_name[len] != '.') || !isdigit(dp->d_name[len+1])))) { diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc index 33d39ac29..cf0e0d299 100644 --- a/logcat/logcatd.rc +++ b/logcat/logcatd.rc @@ -11,3 +11,4 @@ service logcatd /system/bin/logcat -b all -v threadtime -v usec -v printable -D # logd for write to /data/misc/logd, log group for read from log daemon user logd group log + writepid /dev/cpuset/system-background/tasks diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp index 610a6ec70..153a3fd2b 100644 --- a/logcat/tests/logcat_test.cpp +++ b/logcat/tests/logcat_test.cpp @@ -75,6 +75,12 @@ TEST(logcat, buckets) { } TEST(logcat, year) { + + if (android_log_timestamp() == 'm') { + fprintf(stderr, "Skipping test, logd is monotonic time\n"); + return; + } + FILE *fp; char needle[32]; @@ -108,7 +114,44 @@ TEST(logcat, year) { ASSERT_EQ(3, count); } +// Return a pointer to each null terminated -v long time field. +char *fgetLongTime(char *buffer, size_t buflen, FILE *fp) { + while (fgets(buffer, buflen, fp)) { + char *cp = buffer; + if (*cp != '[') { + continue; + } + while (*++cp == ' ') { + ; + } + char *ep = cp; + while (isdigit(*ep)) { + ++ep; + } + if ((*ep != '-') && (*ep != '.')) { + continue; + } + // Find PID field + while (((ep = strchr(ep, ':'))) && (*++ep != ' ')) { + ; + } + if (!ep) { + continue; + } + ep -= 7; + *ep = '\0'; + return cp; + } + return NULL; +} + TEST(logcat, tz) { + + if (android_log_timestamp() == 'm') { + fprintf(stderr, "Skipping test, logd is monotonic time\n"); + return; + } + FILE *fp; ASSERT_TRUE(NULL != (fp = popen( @@ -119,11 +162,8 @@ TEST(logcat, tz) { int count = 0; - while (fgets(buffer, sizeof(buffer), fp)) { - if ((buffer[0] == '[') && (buffer[1] == ' ') - && isdigit(buffer[2]) && isdigit(buffer[3]) - && (buffer[4] == '-') - && (strstr(buffer, " -0700 ") || strstr(buffer, " -0800 "))) { + while (fgetLongTime(buffer, sizeof(buffer), fp)) { + if (strstr(buffer, " -0700") || strstr(buffer, " -0800")) { ++count; } } @@ -144,11 +184,8 @@ TEST(logcat, ntz) { int count = 0; - while (fgets(buffer, sizeof(buffer), fp)) { - if ((buffer[0] == '[') && (buffer[1] == ' ') - && isdigit(buffer[2]) && isdigit(buffer[3]) - && (buffer[4] == '-') - && (strstr(buffer, " -0700 ") || strstr(buffer, " -0800 "))) { + while (fgetLongTime(buffer, sizeof(buffer), fp)) { + if (strstr(buffer, " -0700") || strstr(buffer, " -0800")) { ++count; } } @@ -169,12 +206,8 @@ TEST(logcat, tail_3) { int count = 0; - while (fgets(buffer, sizeof(buffer), fp)) { - if ((buffer[0] == '[') && (buffer[1] == ' ') - && isdigit(buffer[2]) && isdigit(buffer[3]) - && (buffer[4] == '-')) { - ++count; - } + while (fgetLongTime(buffer, sizeof(buffer), fp)) { + ++count; } pclose(fp); @@ -193,12 +226,8 @@ TEST(logcat, tail_10) { int count = 0; - while (fgets(buffer, sizeof(buffer), fp)) { - if ((buffer[0] == '[') && (buffer[1] == ' ') - && isdigit(buffer[2]) && isdigit(buffer[3]) - && (buffer[4] == '-')) { - ++count; - } + while (fgetLongTime(buffer, sizeof(buffer), fp)) { + ++count; } pclose(fp); @@ -217,12 +246,8 @@ TEST(logcat, tail_100) { int count = 0; - while (fgets(buffer, sizeof(buffer), fp)) { - if ((buffer[0] == '[') && (buffer[1] == ' ') - && isdigit(buffer[2]) && isdigit(buffer[3]) - && (buffer[4] == '-')) { - ++count; - } + while (fgetLongTime(buffer, sizeof(buffer), fp)) { + ++count; } pclose(fp); @@ -241,12 +266,8 @@ TEST(logcat, tail_1000) { int count = 0; - while (fgets(buffer, sizeof(buffer), fp)) { - if ((buffer[0] == '[') && (buffer[1] == ' ') - && isdigit(buffer[2]) && isdigit(buffer[3]) - && (buffer[4] == '-')) { - ++count; - } + while (fgetLongTime(buffer, sizeof(buffer), fp)) { + ++count; } pclose(fp); @@ -263,21 +284,15 @@ TEST(logcat, tail_time) { char *last_timestamp = NULL; char *first_timestamp = NULL; int count = 0; - const unsigned int time_length = 18; - const unsigned int time_offset = 2; - while (fgets(buffer, sizeof(buffer), fp)) { - if ((buffer[0] == '[') && (buffer[1] == ' ') - && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1]) - && (buffer[time_offset + 2] == '-')) { - ++count; - buffer[time_length + time_offset] = '\0'; - if (!first_timestamp) { - first_timestamp = strdup(buffer + time_offset); - } - free(last_timestamp); - last_timestamp = strdup(buffer + time_offset); + char *cp; + while ((cp = fgetLongTime(buffer, sizeof(buffer), fp))) { + ++count; + if (!first_timestamp) { + first_timestamp = strdup(cp); } + free(last_timestamp); + last_timestamp = strdup(cp); } pclose(fp); @@ -292,28 +307,24 @@ TEST(logcat, tail_time) { int second_count = 0; int last_timestamp_count = -1; - while (fgets(buffer, sizeof(buffer), fp)) { - if ((buffer[0] == '[') && (buffer[1] == ' ') - && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1]) - && (buffer[time_offset + 2] == '-')) { - ++second_count; - buffer[time_length + time_offset] = '\0'; - if (first_timestamp) { - // we can get a transitory *extremely* rare failure if hidden - // underneath the time is *exactly* XX-XX XX:XX:XX.XXX000000 - EXPECT_STREQ(buffer + time_offset, first_timestamp); - free(first_timestamp); - first_timestamp = NULL; - } - if (!strcmp(buffer + time_offset, last_timestamp)) { - last_timestamp_count = second_count; - } + while ((cp = fgetLongTime(buffer, sizeof(buffer), fp))) { + ++second_count; + if (first_timestamp) { + // we can get a transitory *extremely* rare failure if hidden + // underneath the time is *exactly* XX-XX XX:XX:XX.XXX000000 + EXPECT_STREQ(cp, first_timestamp); + free(first_timestamp); + first_timestamp = NULL; + } + if (!strcmp(cp, last_timestamp)) { + last_timestamp_count = second_count; } } pclose(fp); free(last_timestamp); last_timestamp = NULL; + free(first_timestamp); EXPECT_TRUE(first_timestamp == NULL); EXPECT_LE(count, second_count); @@ -601,6 +612,9 @@ TEST(logcat, logrotate) { } } pclose(fp); + if ((count != 7) && (count != 8)) { + fprintf(stderr, "count=%d\n", count); + } EXPECT_TRUE(count == 7 || count == 8); } } diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp index 2f7cbfb1a..143fb0429 100644 --- a/logd/LogAudit.cpp +++ b/logd/LogAudit.cpp @@ -123,17 +123,19 @@ int LogAudit::logPrint(const char *fmt, ...) { && (*cp == ':')) { memcpy(timeptr + sizeof(audit_str) - 1, "0.0", 3); memmove(timeptr + sizeof(audit_str) - 1 + 3, cp, strlen(cp) + 1); - // - // We are either in 1970ish (MONOTONIC) or 2015+ish (REALTIME) so to - // differentiate without prejudice, we use 1980 to delineate, earlier - // is monotonic, later is real. - // -# define EPOCH_PLUS_10_YEARS (10 * 1461 / 4 * 24 * 60 * 60) - if (now.tv_sec < EPOCH_PLUS_10_YEARS) { - LogKlog::convertMonotonicToReal(now); + if (!isMonotonic()) { + if (android::isMonotonic(now)) { + LogKlog::convertMonotonicToReal(now); + } + } else { + if (!android::isMonotonic(now)) { + LogKlog::convertRealToMonotonic(now); + } } + } else if (isMonotonic()) { + now = log_time(CLOCK_MONOTONIC); } else { - now.strptime("", ""); // side effect of setting CLOCK_REALTIME + now = log_time(CLOCK_REALTIME); } static const char pid_str[] = " pid="; diff --git a/logd/LogAudit.h b/logd/LogAudit.h index 23428228a..8a82630f5 100644 --- a/logd/LogAudit.h +++ b/logd/LogAudit.h @@ -29,6 +29,7 @@ class LogAudit : public SocketListener { public: LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg); int log(char *buf, size_t len); + bool isMonotonic() { return logbuf->isMonotonic(); } protected: virtual bool onDataAvailable(SocketClient *cli); diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp index 1de8e640f..c2f71d149 100644 --- a/logd/LogBuffer.cpp +++ b/logd/LogBuffer.cpp @@ -28,6 +28,7 @@ #include <log/logger.h> #include "LogBuffer.h" +#include "LogKlog.h" #include "LogReader.h" // Default @@ -126,9 +127,48 @@ void LogBuffer::init() { setSize(i, LOG_BUFFER_MIN_SIZE); } } + bool lastMonotonic = monotonic; + monotonic = android_log_timestamp() == 'm'; + if (lastMonotonic == monotonic) { + return; + } + + // + // Fixup all timestamps, may not be 100% accurate, but better than + // throwing what we have away when we get 'surprised' by a change. + // In-place element fixup so no need to check reader-lock. Entries + // should already be in timestamp order, but we could end up with a + // few out-of-order entries if new monotonics come in before we + // are notified of the reinit change in status. A Typical example would + // be: + // --------- beginning of system + // 10.494082 184 201 D Cryptfs : Just triggered post_fs_data + // --------- beginning of kernel + // 0.000000 0 0 I : Initializing cgroup subsys cpuacct + // as the act of mounting /data would trigger persist.logd.timestamp to + // be corrected. 1/30 corner case YMMV. + // + pthread_mutex_lock(&mLogElementsLock); + LogBufferElementCollection::iterator it = mLogElements.begin(); + while((it != mLogElements.end())) { + LogBufferElement *e = *it; + if (monotonic) { + if (!android::isMonotonic(e->mRealTime)) { + LogKlog::convertRealToMonotonic(e->mRealTime); + } + } else { + if (android::isMonotonic(e->mRealTime)) { + LogKlog::convertMonotonicToReal(e->mRealTime); + } + } + ++it; + } + pthread_mutex_unlock(&mLogElementsLock); } -LogBuffer::LogBuffer(LastLogTimes *times) : mTimes(*times) { +LogBuffer::LogBuffer(LastLogTimes *times): + monotonic(android_log_timestamp() == 'm'), + mTimes(*times) { pthread_mutex_init(&mLogElementsLock, NULL); init(); @@ -151,7 +191,9 @@ int LogBuffer::log(log_id_t log_id, log_time realtime, prio = *msg; tag = msg + 1; } - if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) { + if (!__android_log_is_loggable(prio, tag, + ANDROID_LOG_VERBOSE | + ANDROID_LOGGABLE_FLAG_NOT_WITHIN_SIGNAL)) { // Log traffic received to total pthread_mutex_lock(&mLogElementsLock); stats.add(elem); @@ -436,7 +478,10 @@ bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) { worst_sizes = sorted[0]->getSizes(); // Calculate threshold as 12.5% of available storage size_t threshold = log_buffer_size(id) / 8; - if (worst_sizes > threshold) { + if ((worst_sizes > threshold) + // Allow time horizon to extend roughly tenfold, assume + // average entry length is 100 characters. + && (worst_sizes > (10 * sorted[0]->getDropped()))) { worst = sorted[0]->getKey(); second_worst_sizes = sorted[1]->getSizes(); if (second_worst_sizes < threshold) { diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h index 7ed92e999..c1fec73bb 100644 --- a/logd/LogBuffer.h +++ b/logd/LogBuffer.h @@ -32,6 +32,21 @@ #include "LogStatistics.h" #include "LogWhiteBlackList.h" +// +// We are either in 1970ish (MONOTONIC) or 2015+ish (REALTIME) so to +// differentiate without prejudice, we use 1980 to delineate, earlier +// is monotonic, later is real. +// +namespace android { + +static bool isMonotonic(const log_time &mono) { + static const uint32_t EPOCH_PLUS_10_YEARS = 10 * 1461 / 4 * 24 * 60 * 60; + + return mono.tv_sec < EPOCH_PLUS_10_YEARS; +} + +} + typedef std::list<LogBufferElement *> LogBufferElementCollection; class LogBuffer { @@ -49,11 +64,14 @@ class LogBuffer { unsigned long mMaxSize[LOG_ID_MAX]; + bool monotonic; + public: LastLogTimes &mTimes; LogBuffer(LastLogTimes *times); void init(); + bool isMonotonic() { return monotonic; } int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp index c4c302b0a..f10dccfee 100644 --- a/logd/LogBufferElement.cpp +++ b/logd/LogBufferElement.cpp @@ -107,7 +107,9 @@ size_t LogBufferElement::populateDroppedMessage(char *&buffer, LogBuffer *parent) { static const char tag[] = "chatty"; - if (!__android_log_is_loggable(ANDROID_LOG_INFO, tag, ANDROID_LOG_VERBOSE)) { + if (!__android_log_is_loggable(ANDROID_LOG_INFO, tag, + ANDROID_LOG_VERBOSE | + ANDROID_LOGGABLE_FLAG_NOT_WITHIN_SIGNAL)) { return 0; } diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h index 30e43c680..09987ea75 100644 --- a/logd/LogBufferElement.h +++ b/logd/LogBufferElement.h @@ -34,6 +34,9 @@ class LogBuffer; #define EXPIRE_RATELIMIT 10 // maximum rate in seconds to report expiration class LogBufferElement { + + friend LogBuffer; + const log_id_t mLogId; const uid_t mUid; const pid_t mPid; @@ -44,7 +47,7 @@ class LogBufferElement { unsigned short mDropped; // mMsg == NULL }; const uint64_t mSequence; - const log_time mRealTime; + log_time mRealTime; static atomic_int_fast64_t sequence; // assumption: mMsg == NULL diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp index d28161e5a..2a3f52f99 100644 --- a/logd/LogKlog.cpp +++ b/logd/LogKlog.cpp @@ -330,6 +330,10 @@ void LogKlog::sniffTime(log_time &now, } *buf = cp; + if (isMonotonic()) { + return; + } + const char *b; if (((b = strnstr(cp, len, suspendStr))) && ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) { @@ -376,7 +380,11 @@ void LogKlog::sniffTime(log_time &now, convertMonotonicToReal(now); } else { - now = log_time(CLOCK_REALTIME); + if (isMonotonic()) { + now = log_time(CLOCK_MONOTONIC); + } else { + now = log_time(CLOCK_REALTIME); + } } } @@ -574,7 +582,7 @@ int LogKlog::log(const char *buf, size_t len) { // Some may view the following as an ugly heuristic, the desire is to // beautify the kernel logs into an Android Logging format; the goal is // admirable but costly. - while ((isspace(*p) || !*p) && (p < &buf[len])) { + while ((p < &buf[len]) && (isspace(*p) || !*p)) { ++p; } if (p >= &buf[len]) { // timestamp, no content @@ -588,7 +596,7 @@ int LogKlog::log(const char *buf, size_t len) { const char *bt, *et, *cp; bt = p; - if (!fast<strncmp>(p, "[INFO]", 6)) { + if ((taglen >= 6) && !fast<strncmp>(p, "[INFO]", 6)) { // <PRI>[<TIME>] "[INFO]"<tag> ":" message bt = p + 6; taglen -= 6; @@ -612,7 +620,9 @@ int LogKlog::log(const char *buf, size_t len) { p = cp + 1; } else if (taglen) { size = et - bt; - if ((*bt == *cp) && fast<strncmp>(bt + 1, cp + 1, size - 1)) { + if ((taglen > size) && // enough space for match plus trailing : + (*bt == *cp) && // ubber fast<strncmp> pair + fast<strncmp>(bt + 1, cp + 1, size - 1)) { // <PRI>[<TIME>] <tag>_host '<tag>.<num>' : message if (!fast<strncmp>(bt + size - 5, "_host", 5) && !fast<strncmp>(bt + 1, cp + 1, size - 6)) { @@ -686,7 +696,7 @@ int LogKlog::log(const char *buf, size_t len) { p = cp + 1; } } - } + } /* else no tag */ size = etag - tag; if ((size <= 1) // register names like x9 @@ -713,8 +723,12 @@ int LogKlog::log(const char *buf, size_t len) { taglen = mp - tag; } } + // Deal with sloppy and simplistic harmless p = cp + 1 etc above. + if (len < (size_t)(p - buf)) { + p = &buf[len]; + } // skip leading space - while ((isspace(*p) || !*p) && (p < &buf[len])) { + while ((p < &buf[len]) && (isspace(*p) || !*p)) { ++p; } // truncate trailing space or nuls @@ -727,16 +741,26 @@ int LogKlog::log(const char *buf, size_t len) { p = " "; b = 1; } + // paranoid sanity check, can not happen ... if (b > LOGGER_ENTRY_MAX_PAYLOAD) { b = LOGGER_ENTRY_MAX_PAYLOAD; } + if (taglen > LOGGER_ENTRY_MAX_PAYLOAD) { + taglen = LOGGER_ENTRY_MAX_PAYLOAD; + } + // calculate buffer copy requirements size_t n = 1 + taglen + 1 + b + 1; - int rc = n; - if ((taglen > n) || (b > n)) { // Can not happen ... - rc = -EINVAL; - return rc; + // paranoid sanity check, first two just can not happen ... + if ((taglen > n) || (b > n) || (n > USHRT_MAX)) { + return -EINVAL; } + // Careful. + // We are using the stack to house the log buffer for speed reasons. + // If we malloc'd this buffer, we could get away without n's USHRT_MAX + // test above, but we would then required a max(n, USHRT_MAX) as + // truncating length argument to logbuf->log() below. Gain is protection + // of stack sanity and speedup, loss is truncated long-line content. char newstr[n]; char *np = newstr; @@ -755,8 +779,8 @@ int LogKlog::log(const char *buf, size_t len) { np[b] = '\0'; // Log message - rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr, - (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX); + int rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr, + (unsigned short) n); // notify readers if (!rc) { diff --git a/logd/LogKlog.h b/logd/LogKlog.h index 469affdd0..3c8cc8789 100644 --- a/logd/LogKlog.h +++ b/logd/LogKlog.h @@ -43,7 +43,9 @@ public: int log(const char *buf, size_t len); void synchronize(const char *buf, size_t len); + bool isMonotonic() { return logbuf->isMonotonic(); } static void convertMonotonicToReal(log_time &real) { real += correction; } + static void convertRealToMonotonic(log_time &real) { real -= correction; } protected: void sniffTime(log_time &now, const char **buf, size_t len, bool reverse); diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp index 383384389..06135ddc0 100644 --- a/logd/LogReader.cpp +++ b/logd/LogReader.cpp @@ -114,15 +114,17 @@ bool LogReader::onDataAvailable(SocketClient *cli) { log_time &start; uint64_t &sequence; uint64_t last; + bool isMonotonic; public: - LogFindStart(unsigned logMask, pid_t pid, log_time &start, uint64_t &sequence) : + LogFindStart(unsigned logMask, pid_t pid, log_time &start, uint64_t &sequence, bool isMonotonic) : mPid(pid), mLogMask(logMask), startTimeSet(false), start(start), sequence(sequence), - last(sequence) { + last(sequence), + isMonotonic(isMonotonic) { } static int callback(const LogBufferElement *element, void *obj) { @@ -133,20 +135,24 @@ bool LogReader::onDataAvailable(SocketClient *cli) { me->sequence = element->getSequence(); me->startTimeSet = true; return -1; - } else { + } else if (!me->isMonotonic || + android::isMonotonic(element->getRealTime())) { if (me->start < element->getRealTime()) { me->sequence = me->last; me->startTimeSet = true; return -1; } me->last = element->getSequence(); + } else { + me->last = element->getSequence(); } } return false; } bool found() { return startTimeSet; } - } logFindStart(logMask, pid, start, sequence); + } logFindStart(logMask, pid, start, sequence, + logbuf().isMonotonic() && android::isMonotonic(start)); logbuf().flushTo(cli, sequence, FlushCommand::hasReadLogs(cli), logFindStart.callback, &logFindStart); diff --git a/logd/README.property b/logd/README.property index 05ef52852..e4b23a9b9 100644 --- a/logd/README.property +++ b/logd/README.property @@ -25,6 +25,10 @@ persist.logd.filter string Pruning filter to optimize content, "~!" which means to prune the oldest entries of chattiest UID. At runtime use: logcat -P "<string>" +persist.logd.timestamp string The recording timestamp source. Default + is ro.logd.timestamp. "m[onotonic]" is + the only supported key character, + otherwise assumes realtime. NB: - Number support multipliers (K or M) for convenience. Range is limited diff --git a/logd/logd.rc b/logd/logd.rc index da6a0bcd0..10f35536f 100644 --- a/logd/logd.rc +++ b/logd/logd.rc @@ -3,8 +3,10 @@ service logd /system/bin/logd socket logd stream 0666 logd logd socket logdr seqpacket 0666 logd logd socket logdw dgram 0222 logd logd - group root system + group root system readproc + writepid /dev/cpuset/system-background/tasks service logd-reinit /system/bin/logd --reinit oneshot disabled + writepid /dev/cpuset/system-background/tasks diff --git a/logd/main.cpp b/logd/main.cpp index cf8cb8f59..8e75b37a0 100644 --- a/logd/main.cpp +++ b/logd/main.cpp @@ -106,7 +106,9 @@ static int drop_privs() { return -1; } - if (setgroups(0, NULL) == -1) { + gid_t groups[] = { AID_READPROC }; + + if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) == -1) { return -1; } @@ -298,7 +300,7 @@ static void readDmesg(LogAudit *al, LogKlog *kl) { } buf[--len] = '\0'; - if (kl) { + if (kl && kl->isMonotonic()) { kl->synchronize(buf.get(), len); } diff --git a/metricsd/Android.mk b/metricsd/Android.mk index b08c153e4..291c98341 100644 --- a/metricsd/Android.mk +++ b/metricsd/Android.mk @@ -27,6 +27,7 @@ metrics_client_sources := \ metrics_daemon_common := \ collectors/averaged_statistics_collector.cc \ + collectors/cpu_usage_collector.cc \ collectors/disk_usage_collector.cc \ metrics_daemon.cc \ persistent_integer.cc \ @@ -41,6 +42,7 @@ metrics_daemon_common := \ metrics_tests_sources := \ collectors/averaged_statistics_collector_test.cc \ + collectors/cpu_usage_collector_test.cc \ metrics_daemon_test.cc \ metrics_library_test.cc \ persistent_integer_test.cc \ diff --git a/metricsd/collectors/cpu_usage_collector.cc b/metricsd/collectors/cpu_usage_collector.cc new file mode 100644 index 000000000..05934b47c --- /dev/null +++ b/metricsd/collectors/cpu_usage_collector.cc @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2015 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 "collectors/cpu_usage_collector.h" + +#include <base/bind.h> +#include <base/files/file_path.h> +#include <base/files/file_util.h> +#include <base/message_loop/message_loop.h> +#include <base/strings/string_number_conversions.h> +#include <base/strings/string_split.h> +#include <base/strings/string_util.h> +#include <base/sys_info.h> + +#include "metrics/metrics_library.h" + +namespace { + +const char kCpuUsagePercent[] = "Platform.CpuUsage.Percent"; +const char kMetricsProcStatFileName[] = "/proc/stat"; +const int kMetricsProcStatFirstLineItemsCount = 11; + +// Collect every minute. +const int kCollectionIntervalSecs = 60; + +} // namespace + +using base::TimeDelta; + +CpuUsageCollector::CpuUsageCollector(MetricsLibraryInterface* metrics_library) { + CHECK(metrics_library); + metrics_lib_ = metrics_library; + collect_interval_ = TimeDelta::FromSeconds(kCollectionIntervalSecs); +} + +void CpuUsageCollector::Init() { + num_cpu_ = base::SysInfo::NumberOfProcessors(); + + // Get ticks per second (HZ) on this system. + // Sysconf cannot fail, so no sanity checks are needed. + ticks_per_second_ = sysconf(_SC_CLK_TCK); + CHECK_GT(ticks_per_second_, uint64_t(0)) + << "Number of ticks per seconds should be positive."; + + latest_cpu_use_ = GetCumulativeCpuUse(); +} + +void CpuUsageCollector::CollectCallback() { + Collect(); + Schedule(); +} + +void CpuUsageCollector::Schedule() { + base::MessageLoop::current()->PostDelayedTask(FROM_HERE, + base::Bind(&CpuUsageCollector::CollectCallback, base::Unretained(this)), + collect_interval_); +} + +void CpuUsageCollector::Collect() { + TimeDelta cpu_use = GetCumulativeCpuUse(); + TimeDelta diff_per_cpu = (cpu_use - latest_cpu_use_) / num_cpu_; + latest_cpu_use_ = cpu_use; + + // Report the cpu usage as a percentage of the total cpu usage possible. + int percent_use = diff_per_cpu.InMilliseconds() * 100 / + (kCollectionIntervalSecs * 1000); + + metrics_lib_->SendEnumToUMA(kCpuUsagePercent, percent_use, 101); +} + +TimeDelta CpuUsageCollector::GetCumulativeCpuUse() { + base::FilePath proc_stat_path(kMetricsProcStatFileName); + std::string proc_stat_string; + if (!base::ReadFileToString(proc_stat_path, &proc_stat_string)) { + LOG(WARNING) << "cannot open " << kMetricsProcStatFileName; + return TimeDelta(); + } + + uint64_t user_ticks, user_nice_ticks, system_ticks; + if (!ParseProcStat(proc_stat_string, &user_ticks, &user_nice_ticks, + &system_ticks)) { + return TimeDelta(); + } + + uint64_t total = user_ticks + user_nice_ticks + system_ticks; + return TimeDelta::FromMicroseconds( + total * 1000 * 1000 / ticks_per_second_); +} + +bool CpuUsageCollector::ParseProcStat(const std::string& stat_content, + uint64_t *user_ticks, + uint64_t *user_nice_ticks, + uint64_t *system_ticks) { + std::vector<std::string> proc_stat_lines; + base::SplitString(stat_content, '\n', &proc_stat_lines); + if (proc_stat_lines.empty()) { + LOG(WARNING) << "No lines found in " << kMetricsProcStatFileName; + return false; + } + std::vector<std::string> proc_stat_totals; + base::SplitStringAlongWhitespace(proc_stat_lines[0], &proc_stat_totals); + + if (proc_stat_totals.size() != kMetricsProcStatFirstLineItemsCount || + proc_stat_totals[0] != "cpu" || + !base::StringToUint64(proc_stat_totals[1], user_ticks) || + !base::StringToUint64(proc_stat_totals[2], user_nice_ticks) || + !base::StringToUint64(proc_stat_totals[3], system_ticks)) { + LOG(WARNING) << "cannot parse first line: " << proc_stat_lines[0]; + return false; + } + return true; +} diff --git a/metricsd/collectors/cpu_usage_collector.h b/metricsd/collectors/cpu_usage_collector.h new file mode 100644 index 000000000..f81dfcb95 --- /dev/null +++ b/metricsd/collectors/cpu_usage_collector.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef METRICSD_COLLECTORS_CPU_USAGE_COLLECTOR_H_ +#define METRICSD_COLLECTORS_CPU_USAGE_COLLECTOR_H_ + +#include <base/time/time.h> + +#include "metrics/metrics_library.h" + +class CpuUsageCollector { + public: + CpuUsageCollector(MetricsLibraryInterface* metrics_library); + + // Initialize this collector's state. + void Init(); + + // Schedule a collection interval. + void Schedule(); + + // Callback called at the end of the collection interval. + void CollectCallback(); + + // Measure the cpu use and report it. + void Collect(); + + // Gets the current cumulated Cpu usage. + base::TimeDelta GetCumulativeCpuUse(); + + private: + FRIEND_TEST(CpuUsageTest, ParseProcStat); + bool ParseProcStat(const std::string& stat_content, + uint64_t *user_ticks, + uint64_t *user_nice_ticks, + uint64_t *system_ticks); + + int num_cpu_; + uint32_t ticks_per_second_; + + base::TimeDelta collect_interval_; + base::TimeDelta latest_cpu_use_; + + MetricsLibraryInterface* metrics_lib_; +}; + +#endif // METRICSD_COLLECTORS_CPU_USAGE_COLLECTOR_H_ diff --git a/metricsd/collectors/cpu_usage_collector_test.cc b/metricsd/collectors/cpu_usage_collector_test.cc new file mode 100644 index 000000000..ee5c92b29 --- /dev/null +++ b/metricsd/collectors/cpu_usage_collector_test.cc @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 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 <gtest/gtest.h> + +#include "collectors/cpu_usage_collector.h" +#include "metrics/metrics_library_mock.h" + + +TEST(CpuUsageTest, ParseProcStat) { + MetricsLibraryMock metrics_lib_mock; + CpuUsageCollector collector(&metrics_lib_mock); + std::vector<std::string> invalid_contents = { + "", + // First line does not start with cpu. + "spu 17191 11 36579 151118 289 0 2 0 0 0\n" + "cpu0 1564 2 866 48650 68 0 2 0 0 0\n" + "cpu1 14299 0 35116 1844 81 0 0 0 0 0\n", + // One of the field is not a number. + "cpu a17191 11 36579 151118 289 0 2 0 0 0", + // To many numbers in the first line. + "cpu 17191 11 36579 151118 289 0 2 0 0 0 102" + }; + + uint64_t user, nice, system; + for (int i = 0; i < invalid_contents.size(); i++) { + ASSERT_FALSE(collector.ParseProcStat(invalid_contents[i], &user, &nice, + &system)); + } + + ASSERT_TRUE(collector.ParseProcStat( + std::string("cpu 17191 11 36579 151118 289 0 2 0 0 0"), + &user, &nice, &system)); + ASSERT_EQ(17191, user); + ASSERT_EQ(11, nice); + ASSERT_EQ(36579, system); +} diff --git a/metricsd/include/metrics/metrics_library.h b/metricsd/include/metrics/metrics_library.h index b76619456..d2e98c8df 100644 --- a/metricsd/include/metrics/metrics_library.h +++ b/metricsd/include/metrics/metrics_library.h @@ -34,6 +34,7 @@ class MetricsLibraryInterface { virtual bool SendToUMA(const std::string& name, int sample, int min, int max, int nbuckets) = 0; virtual bool SendEnumToUMA(const std::string& name, int sample, int max) = 0; + virtual bool SendBoolToUMA(const std::string& name, bool sample) = 0; virtual bool SendSparseToUMA(const std::string& name, int sample) = 0; virtual bool SendUserActionToUMA(const std::string& action) = 0; virtual ~MetricsLibraryInterface() {} @@ -96,6 +97,9 @@ class MetricsLibrary : public MetricsLibraryInterface { // normal, while 100 is high). bool SendEnumToUMA(const std::string& name, int sample, int max) override; + // Specialization of SendEnumToUMA for boolean values. + bool SendBoolToUMA(const std::string& name, bool sample) override; + // Sends sparse histogram sample to Chrome for transport to UMA. Returns // true on success. // diff --git a/metricsd/include/metrics/metrics_library_mock.h b/metricsd/include/metrics/metrics_library_mock.h index 3de87a9f0..db56f9e50 100644 --- a/metricsd/include/metrics/metrics_library_mock.h +++ b/metricsd/include/metrics/metrics_library_mock.h @@ -32,6 +32,7 @@ class MetricsLibraryMock : public MetricsLibraryInterface { int min, int max, int nbuckets)); MOCK_METHOD3(SendEnumToUMA, bool(const std::string& name, int sample, int max)); + MOCK_METHOD2(SendBoolToUMA, bool(const std::string& name, bool sample)); MOCK_METHOD2(SendSparseToUMA, bool(const std::string& name, int sample)); MOCK_METHOD1(SendUserActionToUMA, bool(const std::string& action)); diff --git a/metricsd/metrics_daemon.cc b/metricsd/metrics_daemon.cc index ed786e1fb..b606fd0ca 100644 --- a/metricsd/metrics_daemon.cc +++ b/metricsd/metrics_daemon.cc @@ -71,10 +71,8 @@ const char kUncleanShutdownDetectedFile[] = const int kMetricMeminfoInterval = 30; // seconds -const char kMetricsProcStatFileName[] = "/proc/stat"; const char kMeminfoFileName[] = "/proc/meminfo"; const char kVmStatFileName[] = "/proc/vmstat"; -const int kMetricsProcStatFirstLineItemsCount = 11; // Thermal CPU throttling. @@ -103,9 +101,7 @@ static const int kMemuseIntervals[] = { MetricsDaemon::MetricsDaemon() : memuse_final_time_(0), - memuse_interval_index_(0), - ticks_per_second_(0), - latest_cpu_use_ticks_(0) {} + memuse_interval_index_(0) {} MetricsDaemon::~MetricsDaemon() { } @@ -188,10 +184,6 @@ void MetricsDaemon::Init(bool testing, upload_interval_ = upload_interval; server_ = server; - // Get ticks per second (HZ) on this system. - // Sysconf cannot fail, so no sanity checks are needed. - ticks_per_second_ = sysconf(_SC_CLK_TCK); - daily_active_use_.reset( new PersistentInteger("Platform.UseTime.PerDay")); version_cumulative_active_use_.reset( @@ -235,6 +227,7 @@ void MetricsDaemon::Init(bool testing, averaged_stats_collector_.reset( new AveragedStatisticsCollector(metrics_lib_, diskstats_path, kVmStatFileName)); + cpu_usage_collector_.reset(new CpuUsageCollector(metrics_lib_)); } int MetricsDaemon::OnInit() { @@ -290,6 +283,7 @@ int MetricsDaemon::OnInit() { base::Bind(&MetricsDaemon::OnDisableMetrics, base::Unretained(this))); } + latest_cpu_use_microseconds_ = cpu_usage_collector_->GetCumulativeCpuUse(); base::MessageLoop::current()->PostDelayedTask(FROM_HERE, base::Bind(&MetricsDaemon::HandleUpdateStatsTimeout, base::Unretained(this)), @@ -404,53 +398,6 @@ DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, return DBUS_HANDLER_RESULT_HANDLED; } -// One might argue that parts of this should go into -// chromium/src/base/sys_info_chromeos.c instead, but put it here for now. - -TimeDelta MetricsDaemon::GetIncrementalCpuUse() { - FilePath proc_stat_path = FilePath(kMetricsProcStatFileName); - std::string proc_stat_string; - if (!base::ReadFileToString(proc_stat_path, &proc_stat_string)) { - LOG(WARNING) << "cannot open " << kMetricsProcStatFileName; - return TimeDelta(); - } - - std::vector<std::string> proc_stat_lines; - base::SplitString(proc_stat_string, '\n', &proc_stat_lines); - if (proc_stat_lines.empty()) { - LOG(WARNING) << "cannot parse " << kMetricsProcStatFileName - << ": " << proc_stat_string; - return TimeDelta(); - } - std::vector<std::string> proc_stat_totals; - base::SplitStringAlongWhitespace(proc_stat_lines[0], &proc_stat_totals); - - uint64_t user_ticks, user_nice_ticks, system_ticks; - if (proc_stat_totals.size() != kMetricsProcStatFirstLineItemsCount || - proc_stat_totals[0] != "cpu" || - !base::StringToUint64(proc_stat_totals[1], &user_ticks) || - !base::StringToUint64(proc_stat_totals[2], &user_nice_ticks) || - !base::StringToUint64(proc_stat_totals[3], &system_ticks)) { - LOG(WARNING) << "cannot parse first line: " << proc_stat_lines[0]; - return TimeDelta(base::TimeDelta::FromSeconds(0)); - } - - uint64_t total_cpu_use_ticks = user_ticks + user_nice_ticks + system_ticks; - - // Sanity check. - if (total_cpu_use_ticks < latest_cpu_use_ticks_) { - LOG(WARNING) << "CPU time decreasing from " << latest_cpu_use_ticks_ - << " to " << total_cpu_use_ticks; - return TimeDelta(); - } - - uint64_t diff = total_cpu_use_ticks - latest_cpu_use_ticks_; - latest_cpu_use_ticks_ = total_cpu_use_ticks; - // Use microseconds to avoid significant truncations. - return base::TimeDelta::FromMicroseconds( - diff * 1000 * 1000 / ticks_per_second_); -} - void MetricsDaemon::ProcessUserCrash() { // Counts the active time up to now. UpdateStats(TimeTicks::Now(), Time::Now()); @@ -506,6 +453,9 @@ bool MetricsDaemon::CheckSystemCrash(const string& crash_file) { void MetricsDaemon::StatsReporterInit() { disk_usage_collector_->Schedule(); + cpu_usage_collector_->Init(); + cpu_usage_collector_->Schedule(); + // Don't start a collection cycle during the first run to avoid delaying the // boot. averaged_stats_collector_->ScheduleWait(); @@ -910,7 +860,10 @@ void MetricsDaemon::UpdateStats(TimeTicks now_ticks, version_cumulative_active_use_->Add(elapsed_seconds); user_crash_interval_->Add(elapsed_seconds); kernel_crash_interval_->Add(elapsed_seconds); - version_cumulative_cpu_use_->Add(GetIncrementalCpuUse().InMilliseconds()); + TimeDelta cpu_use = cpu_usage_collector_->GetCumulativeCpuUse(); + version_cumulative_cpu_use_->Add( + (cpu_use - latest_cpu_use_microseconds_).InMilliseconds()); + latest_cpu_use_microseconds_ = cpu_use; last_update_stats_time_ = now_ticks; const TimeDelta since_epoch = now_wall_time - Time::UnixEpoch(); diff --git a/metricsd/metrics_daemon.h b/metricsd/metrics_daemon.h index 3d691c5ee..f12b02eee 100644 --- a/metricsd/metrics_daemon.h +++ b/metricsd/metrics_daemon.h @@ -32,6 +32,7 @@ #include <gtest/gtest_prod.h> // for FRIEND_TEST #include "collectors/averaged_statistics_collector.h" +#include "collectors/cpu_usage_collector.h" #include "collectors/disk_usage_collector.h" #include "metrics/metrics_library.h" #include "persistent_integer.h" @@ -164,10 +165,6 @@ class MetricsDaemon : public brillo::DBusDaemon { // total number of kernel crashes since the last version update. void SendKernelCrashesCumulativeCountStats(); - // Returns the total (system-wide) CPU usage between the time of the most - // recent call to this function and now. - base::TimeDelta GetIncrementalCpuUse(); - // Sends a sample representing the number of seconds of active use // for a 24-hour period and reset |use|. void SendAndResetDailyUseSample( @@ -268,12 +265,9 @@ class MetricsDaemon : public brillo::DBusDaemon { // Selects the wait time for the next memory use callback. unsigned int memuse_interval_index_; - // The system "HZ", or frequency of ticks. Some system data uses ticks as a - // unit, and this is used to convert to standard time units. - uint32_t ticks_per_second_; // Used internally by GetIncrementalCpuUse() to return the CPU utilization // between calls. - uint64_t latest_cpu_use_ticks_; + base::TimeDelta latest_cpu_use_microseconds_; // Persistent values and accumulators for crash statistics. scoped_ptr<PersistentInteger> daily_cycle_; @@ -302,6 +296,8 @@ class MetricsDaemon : public brillo::DBusDaemon { scoped_ptr<PersistentInteger> kernel_crashes_version_count_; scoped_ptr<PersistentInteger> unclean_shutdowns_daily_count_; scoped_ptr<PersistentInteger> unclean_shutdowns_weekly_count_; + + scoped_ptr<CpuUsageCollector> cpu_usage_collector_; scoped_ptr<DiskUsageCollector> disk_usage_collector_; scoped_ptr<AveragedStatisticsCollector> averaged_stats_collector_; diff --git a/metricsd/metrics_daemon.rc b/metricsd/metrics_daemon.rc index 0ee577e4f..8b24749c9 100644 --- a/metricsd/metrics_daemon.rc +++ b/metricsd/metrics_daemon.rc @@ -1,7 +1,7 @@ on post-fs-data mkdir /data/misc/metrics 0770 system system -service metrics_daemon /system/bin/metrics_daemon --uploader -nodaemon +service metrics_daemon /system/bin/metrics_daemon --uploader --foreground --logtosyslog class late_start user system group system dbus inet diff --git a/metricsd/metrics_daemon_main.cc b/metricsd/metrics_daemon_main.cc index 50c279dfa..8573f6866 100644 --- a/metricsd/metrics_daemon_main.cc +++ b/metricsd/metrics_daemon_main.cc @@ -53,7 +53,7 @@ const std::string MetricsMainDiskStatsPath() { } int main(int argc, char** argv) { - DEFINE_bool(daemon, true, "run as daemon (use -nodaemon for debugging)"); + DEFINE_bool(foreground, false, "Don't daemonize"); // The uploader is disabled by default on ChromeOS as Chrome is responsible // for sending the metrics. @@ -79,13 +79,28 @@ int main(int argc, char** argv) { metrics::kMetricsDirectory, "Root of the configuration files (testing only)"); + DEFINE_bool(logtostderr, false, "Log to standard error"); + DEFINE_bool(logtosyslog, false, "Log to syslog"); + brillo::FlagHelper::Init(argc, argv, "Chromium OS Metrics Daemon"); + int logging_location = (FLAGS_foreground ? brillo::kLogToStderr + : brillo::kLogToSyslog); + if (FLAGS_logtosyslog) + logging_location = brillo::kLogToSyslog; + + if (FLAGS_logtostderr) + logging_location = brillo::kLogToStderr; + // Also log to stderr when not running as daemon. - brillo::InitLog(brillo::kLogToSyslog | brillo::kLogHeader | - (FLAGS_daemon ? 0 : brillo::kLogToStderr)); + brillo::InitLog(logging_location | brillo::kLogHeader); + + if (FLAGS_logtostderr && FLAGS_logtosyslog) { + LOG(ERROR) << "only one of --logtosyslog and --logtostderr can be set"; + return 1; + } - if (FLAGS_daemon && daemon(0, 0) != 0) { + if (!FLAGS_foreground && daemon(0, 0) != 0) { return errno; } diff --git a/metricsd/metrics_library.cc b/metricsd/metrics_library.cc index a651b7676..735d39ff8 100644 --- a/metricsd/metrics_library.cc +++ b/metricsd/metrics_library.cc @@ -173,6 +173,13 @@ bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample, uma_events_file_.value()); } +bool MetricsLibrary::SendBoolToUMA(const std::string& name, bool sample) { + return metrics::SerializationUtils::WriteMetricToFile( + *metrics::MetricSample::LinearHistogramSample(name, + sample ? 1 : 0, 2).get(), + uma_events_file_.value()); +} + bool MetricsLibrary::SendSparseToUMA(const std::string& name, int sample) { return metrics::SerializationUtils::WriteMetricToFile( *metrics::MetricSample::SparseHistogramSample(name, sample).get(), diff --git a/metricsd/uploader/system_profile_cache.cc b/metricsd/uploader/system_profile_cache.cc index f7060a2fa..637cf9e68 100644 --- a/metricsd/uploader/system_profile_cache.cc +++ b/metricsd/uploader/system_profile_cache.cc @@ -179,13 +179,13 @@ std::string SystemProfileCache::GetPersistentGUID( metrics::SystemProfileProto_Channel SystemProfileCache::ProtoChannelFromString( const std::string& channel) { - if (channel == "stable") { + if (channel == "stable-channel") { return metrics::SystemProfileProto::CHANNEL_STABLE; - } else if (channel == "dev") { + } else if (channel == "dev-channel") { return metrics::SystemProfileProto::CHANNEL_DEV; - } else if (channel == "beta") { + } else if (channel == "beta-channel") { return metrics::SystemProfileProto::CHANNEL_BETA; - } else if (channel == "canary") { + } else if (channel == "canary-channel") { return metrics::SystemProfileProto::CHANNEL_CANARY; } diff --git a/metricsd/uploader/upload_service_test.cc b/metricsd/uploader/upload_service_test.cc index 236376a50..47e7b917c 100644 --- a/metricsd/uploader/upload_service_test.cc +++ b/metricsd/uploader/upload_service_test.cc @@ -214,10 +214,10 @@ TEST_F(UploadServiceTest, ExtractChannelFromString) { metrics::SystemProfileProto::CHANNEL_UNKNOWN); EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_DEV, - SystemProfileCache::ProtoChannelFromString("dev")); + SystemProfileCache::ProtoChannelFromString("dev-channel")); EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_STABLE, - SystemProfileCache::ProtoChannelFromString("stable")); + SystemProfileCache::ProtoChannelFromString("stable-channel")); EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_UNKNOWN, SystemProfileCache::ProtoChannelFromString("this is a test")); diff --git a/rootdir/init.rc b/rootdir/init.rc index a9a18bd84..a0b1acf01 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -7,6 +7,7 @@ import /init.environ.rc import /init.usb.rc import /init.${ro.hardware}.rc +import /init.usb.configfs.rc import /init.${ro.zygote}.rc on early-init @@ -86,10 +87,17 @@ on init write /proc/sys/kernel/panic_on_oops 1 write /proc/sys/kernel/hung_task_timeout_secs 0 write /proc/cpu/alignment 4 + + # scheduler tunables + # Disable auto-scaling of scheduler tunables with hotplug. The tunables + # will vary across devices in unpredictable ways if allowed to scale with + # cpu cores. + write /proc/sys/kernel/sched_tunable_scaling 0 write /proc/sys/kernel/sched_latency_ns 10000000 write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000 write /proc/sys/kernel/sched_compat_yield 1 write /proc/sys/kernel/sched_child_runs_first 0 + write /proc/sys/kernel/randomize_va_space 2 write /proc/sys/kernel/kptr_restrict 2 write /proc/sys/vm/mmap_min_addr 32768 @@ -131,20 +139,32 @@ on init mkdir /dev/cpuset mount cpuset none /dev/cpuset mkdir /dev/cpuset/foreground + mkdir /dev/cpuset/foreground/boost mkdir /dev/cpuset/background + # system-background is for system tasks that should only run on + # little cores, not on bigs + # to be used only by init, so don't change the permissions + mkdir /dev/cpuset/system-background # this ensures that the cpusets are present and usable, but the device's # init.rc must actually set the correct cpus write /dev/cpuset/foreground/cpus 0 + write /dev/cpuset/foreground/boost/cpus 0 write /dev/cpuset/background/cpus 0 + write /dev/cpuset/system-background/cpus 0 write /dev/cpuset/foreground/mems 0 + write /dev/cpuset/foreground/boost/mems 0 write /dev/cpuset/background/mems 0 + write /dev/cpuset/system-background/mems 0 chown system system /dev/cpuset chown system system /dev/cpuset/foreground + chown system system /dev/cpuset/foreground/boost chown system system /dev/cpuset/background chown system system /dev/cpuset/tasks chown system system /dev/cpuset/foreground/tasks + chown system system /dev/cpuset/foreground/boost/tasks chown system system /dev/cpuset/background/tasks chmod 0664 /dev/cpuset/foreground/tasks + chmod 0664 /dev/cpuset/foreground/boost/tasks chmod 0664 /dev/cpuset/background/tasks chmod 0664 /dev/cpuset/tasks @@ -180,8 +200,11 @@ on property:sys.boot_from_charger_mode=1 trigger late-init # Load properties from /system/ + /factory after fs mount. -on load_all_props_action - load_all_props +on load_system_props_action + load_system_props + +on load_persist_props_action + load_persist_props start logd start logd-reinit @@ -194,12 +217,16 @@ on late-init trigger early-fs trigger fs trigger post-fs - trigger post-fs-data # Load properties from /system/ + /factory after fs mount. Place # this in another action so that the load will be scheduled after the prior # issued fs triggers have completed. - trigger load_all_props_action + trigger load_system_props_action + + # Now we can mount /data. File encryption requires keymaster to decrypt + # /data, which in turn can only be loaded when system properties are present + trigger post-fs-data + trigger load_persist_props_action # Remove a file to wake up anything waiting for firmware. trigger firmware_mounts_complete @@ -300,6 +327,7 @@ on post-fs-data mkdir /data/misc/media 0700 media media mkdir /data/misc/boottrace 0771 system shell mkdir /data/misc/update_engine 0700 root root + mkdir /data/misc/trace 0700 root root # For security reasons, /data/local/tmp should always be empty. # Do not place files or directories in /data/local/tmp @@ -348,6 +376,8 @@ on post-fs-data mkdir /data/system/heapdump 0700 system system mkdir /data/user 0711 system system + setusercryptopolicies /data/user + # Reload policy from /data/security if present. setprop selinux.reload_policy 1 @@ -526,10 +556,13 @@ service console /system/bin/sh console disabled user shell - group shell log + group shell log readproc seclabel u:r:shell:s0 on property:ro.debuggable=1 + # Give writes to anyone for the trace folder on debug builds. + # The folder is used to store method traces. + chmod 0773 /data/misc/trace start console service flash_recovery /system/bin/install-recovery.sh diff --git a/rootdir/init.usb.configfs.rc b/rootdir/init.usb.configfs.rc new file mode 100644 index 000000000..186384bdd --- /dev/null +++ b/rootdir/init.usb.configfs.rc @@ -0,0 +1,175 @@ +on property:sys.usb.config=none && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/UDC "none" + stop adbd + write /config/usb_gadget/g1/bDeviceClass 0 + write /config/usb_gadget/g1/bDeviceSubClass 0 + write /config/usb_gadget/g1/bDeviceProtocol 0 + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=adb && property:sys.usb.configfs=1 + start adbd + +on property:sys.usb.ffs.ready=1 && property:sys.usb.config=adb && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "adb" + rm /config/usb_gadget/g1/configs/b.1/f1 + rm /config/usb_gadget/g1/configs/b.1/f2 + rm /config/usb_gadget/g1/configs/b.1/f3 + symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f1 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=mtp && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp" + rm /config/usb_gadget/g1/configs/b.1/f1 + rm /config/usb_gadget/g1/configs/b.1/f2 + rm /config/usb_gadget/g1/configs/b.1/f3 + symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1 + start adbd + +on property:sys.usb.ffs.ready=1 && property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp_adb" + rm /config/usb_gadget/g1/configs/b.1/f1 + rm /config/usb_gadget/g1/configs/b.1/f2 + rm /config/usb_gadget/g1/configs/b.1/f3 + symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1 + symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=ptp && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ptp" + rm /config/usb_gadget/g1/configs/b.1/f1 + rm /config/usb_gadget/g1/configs/b.1/f2 + rm /config/usb_gadget/g1/configs/b.1/f3 + symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1 + start adbd + +on property:sys.usb.ffs.ready=1 && property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ptp_adb" + rm /config/usb_gadget/g1/configs/b.1/f1 + rm /config/usb_gadget/g1/configs/b.1/f2 + rm /config/usb_gadget/g1/configs/b.1/f3 + symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1 + symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=accessory && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "accessory" + rm /config/usb_gadget/g1/configs/b.1/f1 + rm /config/usb_gadget/g1/configs/b.1/f2 + rm /config/usb_gadget/g1/configs/b.1/f3 + symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=accessory,adb && property:sys.usb.configfs=1 + start adbd + +on property:sys.usb.ffs.ready=1 && property:sys.usb.config=accessory,adb && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "accessory_adb" + rm /config/usb_gadget/g1/configs/b.1/f1 + rm /config/usb_gadget/g1/configs/b.1/f2 + rm /config/usb_gadget/g1/configs/b.1/f3 + symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1 + symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=audio_source && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "audiosource" + rm /config/usb_gadget/g1/configs/b.1/f1 + rm /config/usb_gadget/g1/configs/b.1/f2 + rm /config/usb_gadget/g1/configs/b.1/f3 + symlink /config/usb_gadget/g1/functions/audio_source.gs2 /config/usb_gadget/g1/configs/b.1/f1 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=audio_source,adb && property:sys.usb.configfs=1 + start adbd + +on property:sys.usb.ffs.ready=1 && property:sys.usb.config=audio_source,adb && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "audiosource_adb" + rm /config/usb_gadget/g1/configs/b.1/f1 + rm /config/usb_gadget/g1/configs/b.1/f2 + rm /config/usb_gadget/g1/configs/b.1/f3 + symlink /config/usb_gadget/g1/functions/audio_source.gs2 /config/usb_gadget/g1/configs/b.1/f1 + symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=accessory,audio_source && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "accessory_audiosource" + rm /config/usb_gadget/g1/configs/b.1/f1 + rm /config/usb_gadget/g1/configs/b.1/f2 + rm /config/usb_gadget/g1/configs/b.1/f3 + symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1 + symlink /config/usb_gadget/g1/functions/audio_source.gs3 /config/usb_gadget/g1/configs/b.1/f2 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=accessory,audio_source,adb && property:sys.usb.configfs=1 + start adbd + +on property:sys.usb.ffs.ready=1 && property:sys.usb.config=accessory,audio_source,adb && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "accessory_audiosource_adb" + rm /config/usb_gadget/g1/configs/b.1/f1 + rm /config/usb_gadget/g1/configs/b.1/f2 + rm /config/usb_gadget/g1/configs/b.1/f3 + symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1 + symlink /config/usb_gadget/g1/functions/audio_source.gs3 /config/usb_gadget/g1/configs/b.1/f2 + symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f3 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=midi && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "midi" + rm /config/usb_gadget/g1/configs/b.1/f1 + rm /config/usb_gadget/g1/configs/b.1/f2 + rm /config/usb_gadget/g1/configs/b.1/f3 + symlink /config/usb_gadget/g1/functions/midi.gs5 /config/usb_gadget/g1/configs/b.1/f1 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=midi,adb && property:sys.usb.configfs=1 + start adbd + +on property:sys.usb.ffs.ready=1 && property:sys.usb.config=midi,adb && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "midi_adb" + rm /config/usb_gadget/g1/configs/b.1/f1 + rm /config/usb_gadget/g1/configs/b.1/f2 + rm /config/usb_gadget/g1/configs/b.1/f3 + symlink /config/usb_gadget/g1/functions/midi.gs5 /config/usb_gadget/g1/configs/b.1/f1 + symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=rndis && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "rndis" + rm /config/usb_gadget/g1/configs/b.1/f1 + rm /config/usb_gadget/g1/configs/b.1/f2 + rm /config/usb_gadget/g1/configs/b.1/f3 + symlink /config/usb_gadget/g1/functions/rndis.gs4 /config/usb_gadget/g1/configs/b.1/f1 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=rndis,adb && property:sys.usb.configfs=1 + start adbd + +on property:sys.usb.ffs.ready=1 && property:sys.usb.config=rndis,adb && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "rndis_adb" + rm /config/usb_gadget/g1/configs/b.1/f1 + rm /config/usb_gadget/g1/configs/b.1/f2 + rm /config/usb_gadget/g1/configs/b.1/f3 + symlink /config/usb_gadget/g1/functions/rndis.gs4 /config/usb_gadget/g1/configs/b.1/f1 + symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc index 4e6f2a8c9..1fd1e2a7a 100644 --- a/rootdir/init.usb.rc +++ b/rootdir/init.usb.rc @@ -22,8 +22,11 @@ service adbd /sbin/adbd --root_seclabel=u:r:su:s0 on property:ro.kernel.qemu=1 start adbd +on boot + setprop sys.usb.configfs 0 + # Used to disable USB when switching states -on property:sys.usb.config=none +on property:sys.usb.config=none && property:sys.usb.configfs=0 stop adbd write /sys/class/android_usb/android0/enable 0 write /sys/class/android_usb/android0/bDeviceClass 0 @@ -32,7 +35,7 @@ on property:sys.usb.config=none # adb only USB configuration # This is the fallback configuration if the # USB manager fails to set a standard configuration -on property:sys.usb.config=adb +on property:sys.usb.config=adb && property:sys.usb.configfs=0 write /sys/class/android_usb/android0/enable 0 write /sys/class/android_usb/android0/idVendor 18d1 write /sys/class/android_usb/android0/idProduct 4EE7 @@ -42,7 +45,7 @@ on property:sys.usb.config=adb setprop sys.usb.state ${sys.usb.config} # USB accessory configuration -on property:sys.usb.config=accessory +on property:sys.usb.config=accessory && property:sys.usb.configfs=0 write /sys/class/android_usb/android0/enable 0 write /sys/class/android_usb/android0/idVendor 18d1 write /sys/class/android_usb/android0/idProduct 2d00 @@ -51,7 +54,7 @@ on property:sys.usb.config=accessory setprop sys.usb.state ${sys.usb.config} # USB accessory configuration, with adb -on property:sys.usb.config=accessory,adb +on property:sys.usb.config=accessory,adb && property:sys.usb.configfs=0 write /sys/class/android_usb/android0/enable 0 write /sys/class/android_usb/android0/idVendor 18d1 write /sys/class/android_usb/android0/idProduct 2d01 @@ -61,7 +64,7 @@ on property:sys.usb.config=accessory,adb setprop sys.usb.state ${sys.usb.config} # audio accessory configuration -on property:sys.usb.config=audio_source +on property:sys.usb.config=audio_source && property:sys.usb.configfs=0 write /sys/class/android_usb/android0/enable 0 write /sys/class/android_usb/android0/idVendor 18d1 write /sys/class/android_usb/android0/idProduct 2d02 @@ -70,7 +73,7 @@ on property:sys.usb.config=audio_source setprop sys.usb.state ${sys.usb.config} # audio accessory configuration, with adb -on property:sys.usb.config=audio_source,adb +on property:sys.usb.config=audio_source,adb && property:sys.usb.configfs=0 write /sys/class/android_usb/android0/enable 0 write /sys/class/android_usb/android0/idVendor 18d1 write /sys/class/android_usb/android0/idProduct 2d03 @@ -80,7 +83,7 @@ on property:sys.usb.config=audio_source,adb setprop sys.usb.state ${sys.usb.config} # USB and audio accessory configuration -on property:sys.usb.config=accessory,audio_source +on property:sys.usb.config=accessory,audio_source && property:sys.usb.configfs=0 write /sys/class/android_usb/android0/enable 0 write /sys/class/android_usb/android0/idVendor 18d1 write /sys/class/android_usb/android0/idProduct 2d04 @@ -89,7 +92,7 @@ on property:sys.usb.config=accessory,audio_source setprop sys.usb.state ${sys.usb.config} # USB and audio accessory configuration, with adb -on property:sys.usb.config=accessory,audio_source,adb +on property:sys.usb.config=accessory,audio_source,adb && property:sys.usb.configfs=0 write /sys/class/android_usb/android0/enable 0 write /sys/class/android_usb/android0/idVendor 18d1 write /sys/class/android_usb/android0/idProduct 2d05 @@ -102,3 +105,34 @@ on property:sys.usb.config=accessory,audio_source,adb # when changing the default configuration on property:persist.sys.usb.config=* setprop sys.usb.config ${persist.sys.usb.config} + +# +# USB type C +# + +# USB mode changes +on property:sys.usb.typec.mode=dfp + write /sys/class/dual_role_usb/otg_default/mode ${sys.usb.typec.mode} + setprop sys.usb.typec.state ${sys.usb.typec.mode} + +on property:sys.usb.typec.mode=ufp + write /sys/class/dual_role_usb/otg_default/mode ${sys.usb.typec.mode} + setprop sys.usb.typec.state ${sys.usb.typec.mode} + +# USB data role changes +on property:sys.usb.typec.data_role=device + write /sys/class/dual_role_usb/otg_default/data_role ${sys.usb.typec.data_role} + setprop sys.usb.typec.state ${sys.usb.typec.data_role} + +on property:sys.usb.typec.data_role=host + write /sys/class/dual_role_usb/otg_default/data_role ${sys.usb.typec.data_role} + setprop sys.usb.typec.state ${sys.usb.typec.data_role} + +# USB power role changes +on property:sys.usb.typec.power_role=source + write /sys/class/dual_role_usb/otg_default/power_role ${sys.usb.typec.power_role} + setprop sys.usb.typec.state ${sys.usb.typec.power_role} + +on property:sys.usb.typec.power_role=sink + write /sys/class/dual_role_usb/otg_default/power_role ${sys.usb.typec.power_role} + setprop sys.usb.typec.state ${sys.usb.typec.power_role} diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc index 75961e6e8..ff25ac227 100644 --- a/rootdir/init.zygote32.rc +++ b/rootdir/init.zygote32.rc @@ -5,4 +5,4 @@ service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-sys onrestart write /sys/power/state on onrestart restart media onrestart restart netd - + writepid /dev/cpuset/foreground/tasks diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc index 68c0668cd..29bb1cf2e 100644 --- a/rootdir/init.zygote32_64.rc +++ b/rootdir/init.zygote32_64.rc @@ -5,8 +5,10 @@ service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-s onrestart write /sys/power/state on onrestart restart media onrestart restart netd + writepid /dev/cpuset/foreground/tasks service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary class main socket zygote_secondary stream 660 root system onrestart restart zygote + writepid /dev/cpuset/foreground/tasks
\ No newline at end of file diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc index afb6d63ce..5497524e4 100644 --- a/rootdir/init.zygote64.rc +++ b/rootdir/init.zygote64.rc @@ -5,4 +5,4 @@ service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-s onrestart write /sys/power/state on onrestart restart media onrestart restart netd - + writepid /dev/cpuset/foreground/tasks diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc index 979ab3b53..8ed5e9ee8 100644 --- a/rootdir/init.zygote64_32.rc +++ b/rootdir/init.zygote64_32.rc @@ -5,8 +5,10 @@ service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-s onrestart write /sys/power/state on onrestart restart media onrestart restart netd + writepid /dev/cpuset/foreground/tasks service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary class main socket zygote_secondary stream 660 root system onrestart restart zygote + writepid /dev/cpuset/foreground/tasks
\ No newline at end of file diff --git a/run-as/run-as.c b/run-as/run-as.c index 3f32e7d74..f0fd2fe7c 100644 --- a/run-as/run-as.c +++ b/run-as/run-as.c @@ -20,6 +20,8 @@ #include <dirent.h> #include <errno.h> +#include <paths.h> +#include <pwd.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> @@ -193,10 +195,21 @@ int main(int argc, char **argv) panic("Could not set SELinux security context: %s\n", strerror(errno)); } - /* cd into the data directory */ + // cd into the data directory, and set $HOME correspondingly. if (TEMP_FAILURE_RETRY(chdir(info.dataDir)) < 0) { panic("Could not cd to package's data directory: %s\n", strerror(errno)); } + setenv("HOME", info.dataDir, 1); + + // Reset parts of the environment, like su would. + setenv("PATH", _PATH_DEFPATH, 1); + unsetenv("IFS"); + + // Set the user-specific parts for this user. + struct passwd* pw = getpwuid(uid); + setenv("LOGNAME", pw->pw_name, 1); + setenv("SHELL", pw->pw_shell, 1); + setenv("USER", pw->pw_name, 1); /* User specified command for exec. */ if ((argc >= commandArgvOfs + 1) && diff --git a/toolbox/top.c b/toolbox/top.c index 1e99d4cc0..0ea5a5e75 100644 --- a/toolbox/top.c +++ b/toolbox/top.c @@ -158,7 +158,7 @@ int top_main(int argc, char *argv[]) { fprintf(stderr, "Invalid argument \"%s\" for option -s.\n", argv[i]); exit(EXIT_FAILURE); } - if (!strcmp(argv[i], "-t")) { threads = 1; continue; } + if (!strcmp(argv[i], "-H") || !strcmp(argv[i], "-t")) { threads = 1; continue; } if (!strcmp(argv[i], "-h")) { usage(argv[0]); exit(EXIT_SUCCESS); @@ -187,6 +187,7 @@ int top_main(int argc, char *argv[]) { read_procs(); print_procs(); free_old_procs(); + fflush(stdout); } return 0; @@ -566,7 +567,7 @@ static void usage(char *cmd) { " -n num Updates to show before exiting.\n" " -d num Seconds to wait between updates.\n" " -s col Column to sort by (cpu,vss,rss,thr).\n" - " -t Show threads instead of processes.\n" + " -H Show threads instead of processes.\n" " -h Display this help screen.\n", cmd); } |