diff options
124 files changed, 18475 insertions, 2107 deletions
diff --git a/adb/adb.cpp b/adb/adb.cpp index f64b19f..c09aee3 100644 --- a/adb/adb.cpp +++ b/adb/adb.cpp @@ -919,7 +919,7 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r if(!strncmp(service,"get-state",strlen("get-state"))) { transport = acquire_one_transport(CS_ANY, ttype, serial, NULL); SendOkay(reply_fd); - SendProtocolString(reply_fd, transport->connection_state_name()); + SendProtocolString(reply_fd, transport ? transport->connection_state_name() : "unknown"); return 0; } #endif // ADB_HOST @@ -339,6 +339,8 @@ int adb_commandline(int argc, const char **argv); int connection_state(atransport *t); +extern int recovery_mode; + #define CS_ANY -1 #define CS_OFFLINE 0 #define CS_BOOTLOADER 1 @@ -350,6 +352,9 @@ int connection_state(atransport *t); #define CS_UNAUTHORIZED 7 extern const char *adb_device_banner; + +#define CS_ONLINE 10 /* recovery or device */ + extern int HOST; extern int SHELL_EXIT_NOTIFY_FD; diff --git a/adb/adb_main.cpp b/adb/adb_main.cpp index 45a2158..4025fbe 100644 --- a/adb/adb_main.cpp +++ b/adb/adb_main.cpp @@ -239,7 +239,9 @@ int adb_main(int is_daemon, int server_port) // descriptor will always be open. adbd_cloexec_auth_socket(); - if (ALLOW_ADBD_NO_AUTH && property_get_bool("ro.adb.secure", 0) == 0) { + // Override auth in factory test mode + if ((ALLOW_ADBD_NO_AUTH && property_get_bool("ro.adb.secure", 0) == 0) || + (property_get_bool("ro.boot.ftm", 0) == 1)) { auth_required = false; } @@ -359,6 +361,10 @@ void close_stdin() { } #endif +#if !ADB_HOST +int recovery_mode = 0; +#endif + // TODO(danalbert): Split this file up into adb_main.cpp and adbd_main.cpp. int main(int argc, char **argv) { #if ADB_HOST @@ -397,6 +403,8 @@ int main(int argc, char **argv) { } } + recovery_mode = (strcmp(adb_device_banner, "recovery") == 0); + close_stdin(); adb_trace_init(); diff --git a/adb/commandline.cpp b/adb/commandline.cpp index fd9953c..26eea2f 100644 --- a/adb/commandline.cpp +++ b/adb/commandline.cpp @@ -497,6 +497,8 @@ static int adb_download_buffer(const char *service, const char *fn, const void* #define SIDELOAD_HOST_BLOCK_SIZE (CHUNK_SIZE) +#define MB (1024*1024) + /* * The sideload-host protocol serves the data in a file (given on the * command line) to the client, using a simple protocol: @@ -517,10 +519,13 @@ static int adb_download_buffer(const char *service, const char *fn, const void* * we hang up. */ static int adb_sideload_host(const char* fn) { + static const char spinner[] = "/-\\|"; + static const int spinlen = sizeof(spinner)-1; + size_t last_xfer = 0; + int spin_index = 0; unsigned sz; size_t xfer = 0; int status; - int last_percent = -1; int opt = SIDELOAD_HOST_BLOCK_SIZE; printf("loading: '%s'", fn); @@ -531,6 +536,11 @@ static int adb_sideload_host(const char* fn) { fprintf(stderr, "* cannot read '%s' *\n", fn); return -1; } + if (sz == 0) { + printf("\n"); + fprintf(stderr, "* '%s' is empty *\n", fn); + return -1; + } std::string service = android::base::StringPrintf("sideload-host:%d:%d", sz, SIDELOAD_HOST_BLOCK_SIZE); @@ -547,6 +557,24 @@ static int adb_sideload_host(const char* fn) { opt = adb_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void *) &opt, sizeof(opt)); while (true) { + fd_set fds; + struct timeval tv; + FD_ZERO(&fds); + FD_SET(fd, &fds); + tv.tv_sec = 1; + tv.tv_usec = 0; + int rc = select(fd+1, &fds, NULL, NULL, &tv); + size_t diff = xfer - last_xfer; + if (rc == 0 || diff >= (1*MB)) { + spin_index = (spin_index+1) % spinlen; + printf("\rserving: '%s' %4umb %.2fx %c", fn, + (unsigned)xfer/(1*MB), (double)xfer/sz, spinner[spin_index]); + fflush(stdout); + last_xfer = xfer; + } + if (rc == 0) { + continue; + } char buf[9]; if (!ReadFdExactly(fd, buf, 8)) { fprintf(stderr, "* failed to read command: %s\n", strerror(errno)); @@ -582,22 +610,9 @@ static int adb_sideload_host(const char* fn) { goto done; } xfer += to_write; - - // For normal OTA packages, we expect to transfer every byte - // twice, plus a bit of overhead (one read during - // verification, one read of each byte for installation, plus - // extra access to things like the zip central directory). - // This estimate of the completion becomes 100% when we've - // transferred ~2.13 (=100/47) times the package size. - int percent = (int)(xfer * 47LL / (sz ? sz : 1)); - if (percent != last_percent) { - printf("\rserving: '%s' (~%d%%) ", fn, percent); - fflush(stdout); - last_percent = percent; - } } - printf("\rTotal xfer: %.2fx%*s\n", (double)xfer / (sz ? sz : 1), (int)strlen(fn)+10, ""); + printf("\ntotal xfer: %4umb %.2fx\n", (unsigned)xfer/(1*MB), (double)xfer/sz); done: if (fd >= 0) adb_close(fd); @@ -741,8 +756,10 @@ static int logcat(transport_type transport, const char* serial, int argc, const static int mkdirs(const char *path) { + std::string holder(path); + int ret; - char *x = (char *)path + 1; + char *x = &holder[1]; for(;;) { x = adb_dirstart(x); @@ -759,7 +776,7 @@ static int mkdirs(const char *path) } 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++) { diff --git a/adb/services.cpp b/adb/services.cpp index 1847447..ef2fa56 100644 --- a/adb/services.cpp +++ b/adb/services.cpp @@ -76,6 +76,18 @@ void restart_root_service(int fd, void *cookie) { return; } + char build_type[PROPERTY_VALUE_MAX]; + char cm_version[PROPERTY_VALUE_MAX]; + property_get("persist.sys.root_access", value, "0"); + property_get("ro.build.type", build_type, ""); + property_get("ro.cm.version", cm_version, ""); + + if (strlen(cm_version) > 0 && strcmp(build_type, "eng") != 0 && (atoi(value) & 2) != 2) { + WriteFdExactly(fd, "root access is disabled by system setting - enable in settings -> development options\n"); + adb_close(fd); + return; + } + property_set("service.adb.root", "1"); WriteFdExactly(fd, "restarting adbd as root\n"); adb_close(fd); @@ -342,8 +354,10 @@ static int create_subproc_raw(const char *cmd, const char *arg0, const char *arg #if ADB_HOST #define SHELL_COMMAND "/bin/sh" +#define ALTERNATE_SHELL_COMMAND "" #else #define SHELL_COMMAND "/system/bin/sh" +#define ALTERNATE_SHELL_COMMAND "/sbin/sh" #endif #if !ADB_HOST @@ -384,6 +398,9 @@ static int create_subproc_thread(const char *name, const subproc_mode mode) int ret_fd; pid_t pid = -1; + const char* shell_command; + struct stat st; + const char *arg0, *arg1; if (name == 0 || *name == 0) { arg0 = "-"; arg1 = 0; @@ -391,12 +408,24 @@ static int create_subproc_thread(const char *name, const subproc_mode mode) arg0 = "-c"; arg1 = name; } + char value[PROPERTY_VALUE_MAX]; + property_get("persist.sys.adb.shell", value, ""); + if (value[0] != '\0' && stat(value, &st) == 0) { + shell_command = value; + } + else if (stat(ALTERNATE_SHELL_COMMAND, &st) == 0) { + shell_command = ALTERNATE_SHELL_COMMAND; + } + else { + shell_command = SHELL_COMMAND; + } + switch (mode) { case SUBPROC_PTY: - ret_fd = create_subproc_pty(SHELL_COMMAND, arg0, arg1, &pid); + ret_fd = create_subproc_pty(shell_command, arg0, arg1, &pid); break; case SUBPROC_RAW: - ret_fd = create_subproc_raw(SHELL_COMMAND, arg0, arg1, &pid); + ret_fd = create_subproc_raw(shell_command, arg0, arg1, &pid); break; default: fprintf(stderr, "invalid subproc_mode %d\n", mode); @@ -422,6 +451,13 @@ static int create_subproc_thread(const char *name, const subproc_mode mode) } #endif +#if !ADB_HOST +static const char* bu_path() +{ + return (recovery_mode ? "/sbin/bu" : "/system/bin/bu"); +} +#endif + int service_to_fd(const char *name) { int ret = -1; @@ -478,10 +514,14 @@ int service_to_fd(const char *name) } else if(!strncmp(name, "unroot:", 7)) { ret = create_service_thread(restart_unroot_service, NULL); } else if(!strncmp(name, "backup:", 7)) { - ret = create_subproc_thread(android::base::StringPrintf("/system/bin/bu backup %s", + ret = create_subproc_thread(android::base::StringPrintf("%s backup %s", bu_path(), (name + 7)).c_str(), SUBPROC_RAW); } else if(!strncmp(name, "restore:", 8)) { - ret = create_subproc_thread("/system/bin/bu restore", SUBPROC_RAW); + char* cmd; + if (asprintf(&cmd, "%s restore", bu_path()) != -1) { + ret = create_subproc_thread(cmd, SUBPROC_RAW); + free(cmd); + } } else if(!strncmp(name, "tcpip:", 6)) { int port; if (sscanf(name + 6, "%d", &port) != 1) { @@ -672,6 +712,15 @@ asocket* host_service_to_socket(const char* name, const char *serial) } else if (!strncmp(name, "any", strlen("any"))) { sinfo->transport = kTransportAny; sinfo->state = CS_DEVICE; + } else if (!strncmp(name, "sideload", strlen("sideload"))) { + sinfo->transport = kTransportAny; + sinfo->state = CS_SIDELOAD; + } else if (!strncmp(name, "recovery", strlen("recovery"))) { + sinfo->transport = kTransportAny; + sinfo->state = CS_RECOVERY; + } else if (!strncmp(name, "online", strlen("online"))) { + sinfo->transport = kTransportAny; + sinfo->state = CS_ONLINE; } else { free(sinfo); return NULL; diff --git a/adb/transport.cpp b/adb/transport.cpp index 2cd6ac2..5ae49d3 100644 --- a/adb/transport.cpp +++ b/adb/transport.cpp @@ -821,8 +821,10 @@ retry: result = NULL; } - /* check for required connection state */ - if (result && state != CS_ANY && result->connection_state != state) { + /* check for required connection state */ + if (result && state != CS_ANY && ((state != CS_ONLINE && result->connection_state != state) + || (state == CS_ONLINE && !(result->connection_state == CS_DEVICE + || result->connection_state == CS_RECOVERY)))) { if (error_out) *error_out = "invalid device state"; result = NULL; } diff --git a/adb/usb_linux_client.cpp b/adb/usb_linux_client.cpp index f3db346..44df695 100644 --- a/adb/usb_linux_client.cpp +++ b/adb/usb_linux_client.cpp @@ -333,37 +333,35 @@ static void init_functionfs(struct usb_handle *h) v2_descriptor.hs_descs = hs_descriptors; v2_descriptor.ss_descs = ss_descriptors; - if (h->control < 0) { // might have already done this before - D("OPENING %s\n", USB_FFS_ADB_EP0); - h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR); - if (h->control < 0) { - D("[ %s: cannot open control endpoint: errno=%d]\n", USB_FFS_ADB_EP0, errno); - goto err; - } - - ret = adb_write(h->control, &v2_descriptor, sizeof(v2_descriptor)); - if (ret < 0) { - v1_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC); - v1_descriptor.header.length = cpu_to_le32(sizeof(v1_descriptor)); - v1_descriptor.header.fs_count = 3; - v1_descriptor.header.hs_count = 3; - v1_descriptor.fs_descs = fs_descriptors; - v1_descriptor.hs_descs = hs_descriptors; - D("[ %s: Switching to V1_descriptor format errno=%d ]\n", USB_FFS_ADB_EP0, errno); - ret = adb_write(h->control, &v1_descriptor, sizeof(v1_descriptor)); - if (ret < 0) { - D("[ %s: write descriptors failed: errno=%d ]\n", USB_FFS_ADB_EP0, errno); - goto err; - } - } + D("OPENING %s\n", USB_FFS_ADB_EP0); + h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR); + if (h->control < 0) { + D("[ %s: cannot open control endpoint: errno=%d]\n", USB_FFS_ADB_EP0, errno); + goto err; + } - ret = adb_write(h->control, &strings, sizeof(strings)); + ret = adb_write(h->control, &v2_descriptor, sizeof(v2_descriptor)); + if (ret < 0) { + v1_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC); + v1_descriptor.header.length = cpu_to_le32(sizeof(v1_descriptor)); + v1_descriptor.header.fs_count = 3; + v1_descriptor.header.hs_count = 3; + v1_descriptor.fs_descs = fs_descriptors; + v1_descriptor.hs_descs = hs_descriptors; + D("[ %s: Switching to V1_descriptor format errno=%d ]\n", USB_FFS_ADB_EP0, errno); + ret = adb_write(h->control, &v1_descriptor, sizeof(v1_descriptor)); if (ret < 0) { - D("[ %s: writing strings failed: errno=%d]\n", USB_FFS_ADB_EP0, errno); + D("[ %s: write descriptors failed: errno=%d ]\n", USB_FFS_ADB_EP0, errno); goto err; } } + ret = adb_write(h->control, &strings, sizeof(strings)); + if (ret < 0) { + D("[ %s: writing strings failed: errno=%d]\n", USB_FFS_ADB_EP0, errno); + goto err; + } + h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDWR); if (h->bulk_out < 0) { D("[ %s: cannot open bulk-out ep: errno=%d ]\n", USB_FFS_ADB_OUT, errno); @@ -401,14 +399,14 @@ static void *usb_ffs_open_thread(void *x) while (true) { // wait until the USB device needs opening adb_mutex_lock(&usb->lock); - while (usb->control != -1 && usb->bulk_in != -1 && usb->bulk_out != -1) + while (usb->control != -1) adb_cond_wait(&usb->notify, &usb->lock); adb_mutex_unlock(&usb->lock); while (true) { init_functionfs(usb); - if (usb->control >= 0 && usb->bulk_in >= 0 && usb->bulk_out >= 0) + if (usb->control >= 0) break; adb_sleep_ms(1000); @@ -500,13 +498,10 @@ static void usb_ffs_kick(usb_handle *h) D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out, errno); adb_mutex_lock(&h->lock); - - // don't close ep0 here, since we may not need to reinitialize it with - // the same descriptors again. if however ep1/ep2 fail to re-open in - // init_functionfs, only then would we close and open ep0 again. + adb_close(h->control); adb_close(h->bulk_out); adb_close(h->bulk_in); - h->bulk_out = h->bulk_in = -1; + h->control = h->bulk_out = h->bulk_in = -1; // notify usb_ffs_open_thread that we are disconnected adb_cond_signal(&h->notify); diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp index 9c8a41e..984b931 100644 --- a/debuggerd/debuggerd.cpp +++ b/debuggerd/debuggerd.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#define LOG_TAG "DEBUG" + #include <stdio.h> #include <errno.h> #include <signal.h> @@ -84,11 +86,13 @@ static void wait_for_user_action(const debugger_request_t &request) { "* and start gdbclient:\n" "*\n" "* gdbclient %s :5039 %d\n" + "* or\n" + "* dddclient %s :5039 %d\n" "*\n" "* Wait for gdb to start, then press the VOLUME DOWN key\n" "* to let the process continue crashing.\n" "********************************************************", - request.pid, exe, request.tid); + request.pid, exe, request.tid, exe, request.tid); // Wait for VOLUME DOWN. if (init_getevent() == 0) { @@ -258,7 +262,19 @@ static bool should_attach_gdb(debugger_request_t* request) { char value[PROPERTY_VALUE_MAX]; property_get("debug.db.uid", value, "-1"); int debug_uid = atoi(value); - return debug_uid >= 0 && request->uid <= (uid_t)debug_uid; + if (debug_uid >= 0 && request->uid <= (uid_t)debug_uid) { + return true; + } else { + /* External docs say to use 10,000 but more is likely needed; be helpful. */ + if (request->uid > (uid_t)debug_uid) { + ALOGI("request->uid:%d > property debug.db.uid:%d; NOT waiting for gdb.", + request->uid, debug_uid); + } else { + ALOGI("property debug.db.uid not set; NOT waiting for gdb."); + ALOGI("HINT: adb shell setprop debug.db.uid 100000"); + ALOGI("HINT: adb forward tcp:5039 tcp:5039"); + } + } } return false; } diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp index aeffc66..0c9fc49 100644 --- a/debuggerd/tombstone.cpp +++ b/debuggerd/tombstone.cpp @@ -167,10 +167,13 @@ static const char* get_sigcode(int signo, int code) { static void dump_header_info(log_t* log) { char fingerprint[PROPERTY_VALUE_MAX]; char revision[PROPERTY_VALUE_MAX]; + char cm_version[PROPERTY_VALUE_MAX]; + property_get("ro.cm.version", cm_version, "unknown"); property_get("ro.build.fingerprint", fingerprint, "unknown"); property_get("ro.revision", revision, "unknown"); + _LOG(log, logtype::HEADER, "CM Version: '%s'\n", cm_version); _LOG(log, logtype::HEADER, "Build fingerprint: '%s'\n", fingerprint); _LOG(log, logtype::HEADER, "Revision: '%s'\n", revision); _LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING); diff --git a/fastboot/Android.mk b/fastboot/Android.mk index 66a470a..b5212ba 100644 --- a/fastboot/Android.mk +++ b/fastboot/Android.mk @@ -68,14 +68,14 @@ ifneq ($(HOST_OS),windows) LOCAL_STATIC_LIBRARIES += libselinux endif # HOST_OS != windows -ifeq ($(HOST_OS),linux) +ifneq (,$(filter linux darwin,$(HOST_OS))) # libf2fs_dlutils_host will dlopen("libf2fs_fmt_host_dyn") LOCAL_CFLAGS += -DUSE_F2FS LOCAL_LDFLAGS += -ldl -rdynamic -Wl,-rpath,. LOCAL_REQUIRED_MODULES := libf2fs_fmt_host_dyn # The following libf2fs_* are from system/extras/f2fs_utils, # and do not use code in external/f2fs-tools. -LOCAL_STATIC_LIBRARIES += libf2fs_utils_host libf2fs_ioutils_host libf2fs_dlutils_host +LOCAL_STATIC_LIBRARIES += libf2fs_utils_host libf2fs_dlutils_host endif # libc++ not available on windows yet @@ -91,7 +91,7 @@ LOCAL_SHARED_LIBRARIES := include $(BUILD_HOST_EXECUTABLE) my_dist_files := $(LOCAL_BUILT_MODULE) -ifeq ($(HOST_OS),linux) +ifneq (,$(filter linux darwin,$(HOST_OS))) my_dist_files += $(HOST_LIBRARY_PATH)/libf2fs_fmt_host_dyn$(HOST_SHLIB_SUFFIX) endif $(call dist-for-goals,dist_files sdk,$(my_dist_files)) diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp index de4c0ea..6a2f10d 100644 --- a/fastboot/fastboot.cpp +++ b/fastboot/fastboot.cpp @@ -203,7 +203,13 @@ int match_fastboot_with_serial(usb_ifc_info *info, const char *local_serial) (info->dev_vendor != 0x413c) && // DELL (info->dev_vendor != 0x2314) && // INQ Mobile (info->dev_vendor != 0x0b05) && // Asus - (info->dev_vendor != 0x0bb4)) // HTC + (info->dev_vendor != 0x0bb4) && // HTC + (info->dev_vendor != 0x0421) && // Nokia + (info->dev_vendor != 0x1ebf) && // Coolpad + (info->dev_vendor != 0x2b4c) && // Zuk + (info->dev_vendor != 0x2a96) && // MMX + (info->dev_vendor != 0x19d2) && // ZTE + (info->dev_vendor != 0x2c3f)) // Nextbit return -1; if(info->ifc_class != 0xff) return -1; if(info->ifc_subclass != 0x42) return -1; @@ -275,7 +281,7 @@ void usage(void) "usage: fastboot [ <option> ] <command>\n" "\n" "commands:\n" - " update <filename> reflash device from update.zip\n" + " update <filename> [ -a <boot.img> ] reflash device from update.zip\n" " flashall flash boot, system, vendor and if found,\n" " recovery\n" " flash <partition> [ <filename> ] write a file to a flash partition\n" @@ -325,6 +331,9 @@ void usage(void) " default: 2048\n" " -S <size>[K|M|G] automatically sparse files greater\n" " than size. 0 to disable\n" + " -R reboot device (e.g. after flash)\n" + " -a <boot.img> use alternate <boot.img> instead of\n" + " boot.img in update.zip file\n" ); } @@ -736,7 +745,7 @@ void do_update_signature(ZipArchiveHandle zip, char *fn) fb_queue_command("signature", "installing signature"); } -void do_update(usb_handle *usb, const char *filename, int erase_first) +void do_update(usb_handle *usb, const char *filename, int erase_first, const char *alt_boot_fname) { queue_info_dump(); @@ -759,18 +768,27 @@ void do_update(usb_handle *usb, const char *filename, int erase_first) setup_requirements(reinterpret_cast<char*>(data), sz); for (size_t i = 0; i < ARRAY_SIZE(images); ++i) { - int fd = unzip_to_file(zip, images[i].img_name); - if (fd == -1) { - if (images[i].is_optional) { - continue; + fastboot_buffer buf; + // support alt images only for boot partition + bool from_zip = alt_boot_fname == NULL || + strncmp(images[i].part_name, "boot", sizeof(images[i].part_name)); + if (from_zip) { + int fd = unzip_to_file(zip, images[i].img_name); + if (fd == -1) { + if (images[i].is_optional) { + continue; + } + CloseArchive(zip); + exit(1); // unzip_to_file already explained why. } - CloseArchive(zip); - exit(1); // unzip_to_file already explained why. + 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); + } else { + int rc = load_buf(usb, alt_boot_fname, &buf); + if (rc) die("cannot load %s", alt_boot_fname); } - 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); } @@ -1013,8 +1031,10 @@ int main(int argc, char **argv) int status; int c; int longindex; + const char *alt_boot_fname = NULL; const struct option longopts[] = { + {"alt-boot", required_argument, 0, 'a'}, {"base", required_argument, 0, 'b'}, {"kernel_offset", required_argument, 0, 'k'}, {"page_size", required_argument, 0, 'n'}, @@ -1023,18 +1043,22 @@ int main(int argc, char **argv) {"help", no_argument, 0, 'h'}, {"unbuffered", no_argument, 0, 0}, {"version", no_argument, 0, 0}, + {"reboot", no_argument, 0, 'R'}, {0, 0, 0, 0} }; serial = getenv("ANDROID_SERIAL"); while (1) { - c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:h", longopts, &longindex); + c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:hRa:", longopts, &longindex); if (c < 0) { break; } /* Alphabetical cases */ switch (c) { + case 'a': + alt_boot_fname = optarg; + break; case 'b': base_addr = strtoul(optarg, 0, 16); break; @@ -1070,6 +1094,9 @@ int main(int argc, char **argv) case 'r': ramdisk_offset = strtoul(optarg, 0, 16); break; + case 'R': + wants_reboot = 1; + break; case 't': tags_offset = strtoul(optarg, 0, 16); break; @@ -1247,10 +1274,10 @@ int main(int argc, char **argv) wants_reboot = 1; } else if(!strcmp(*argv, "update")) { if (argc > 1) { - do_update(usb, argv[1], erase_first); + do_update(usb, argv[1], erase_first, alt_boot_fname); skip(2); } else { - do_update(usb, "update.zip", erase_first); + do_update(usb, "update.zip", erase_first, alt_boot_fname); skip(1); } wants_reboot = 1; diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk index 8ed5cc9..43c64fb 100644 --- a/fs_mgr/Android.mk +++ b/fs_mgr/Android.mk @@ -12,8 +12,8 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \ external/openssl/include LOCAL_MODULE:= libfs_mgr -LOCAL_STATIC_LIBRARIES := liblogwrap libmincrypt libext4_utils_static libsquashfs_utils -LOCAL_C_INCLUDES += system/extras/ext4_utils system/extras/squashfs_utils +LOCAL_STATIC_LIBRARIES := liblogwrap libmincrypt libext4_utils_static libsquashfs_utils libext2_blkid libext2_uuid_static +LOCAL_C_INCLUDES += system/extras/ext4_utils system/extras/squashfs_utils external/e2fsprogs/lib LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_CFLAGS := -Werror @@ -23,6 +23,30 @@ endif include $(BUILD_STATIC_LIBRARY) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.c fs_mgr_fstab.c +LOCAL_SRC_FILES += fs_mgr_format.c + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \ + system/vold \ + system/extras/ext4_utils \ + external/openssl/include \ + system/core/logwrapper/include + +LOCAL_MODULE:= libfs_mgr +LOCAL_WHOLE_STATIC_LIBRARIES := liblogwrap libmincrypt libext4_utils_static libsquashfs_utils libsparse_static libext2_blkid libext2_uuid_static +LOCAL_STATIC_LIBRARIES := libcutils liblog libsparse_static libz libselinux +LOCAL_C_INCLUDES += system/extras/ext4_utils system/extras/squashfs_utils external/e2fsprogs/lib +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_CFLAGS := -Werror + +ifneq (,$(filter userdebug,$(TARGET_BUILD_VARIANT))) +LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1 +endif + +include $(BUILD_SHARED_LIBRARY) + include $(CLEAR_VARS) @@ -38,7 +62,7 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/sbin LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED) -LOCAL_STATIC_LIBRARIES := libfs_mgr liblogwrap libcutils liblog libc libmincrypt libext4_utils_static libsquashfs_utils +LOCAL_STATIC_LIBRARIES := libfs_mgr liblogwrap libcutils liblog libc libmincrypt libext4_utils_static libsquashfs_utils libext2_blkid libext2_uuid_static LOCAL_STATIC_LIBRARIES += libsparse_static libz libselinux LOCAL_CXX_STL := libc++_static diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index 15d44ef..e78b581 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c @@ -39,6 +39,7 @@ #include <cutils/partition_utils.h> #include <cutils/properties.h> #include <logwrap/logwrap.h> +#include <blkid/blkid.h> #include "mincrypt/rsa.h" #include "mincrypt/sha.h" @@ -60,6 +61,7 @@ #define FSCK_LOG_FILE "/dev/fscklogs/log" #define ZRAM_CONF_DEV "/sys/block/zram0/disksize" +#define ZRAM_STREAMS "/sys/block/zram0/max_comp_streams" #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) @@ -161,10 +163,10 @@ static void check_fs(char *blk_device, char *fs_type, char *target) } else if (!strcmp(fs_type, "f2fs")) { char *f2fs_fsck_argv[] = { F2FS_FSCK_BIN, - "-f", + "-a", blk_device }; - INFO("Running %s -f %s\n", F2FS_FSCK_BIN, blk_device); + INFO("Running %s -a %s\n", F2FS_FSCK_BIN, blk_device); ret = android_fork_execvp_ext(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv, &status, true, LOG_KLOG | LOG_FILE, @@ -304,6 +306,8 @@ static int mount_with_alternatives(struct fstab *fstab, int start_idx, int *end_ int i; int mount_errno = 0; int mounted = 0; + int cmp_len; + char *detected_fs_type; if (!end_idx || !attempted_idx || start_idx >= fstab->num_entries) { errno = EINVAL; @@ -329,8 +333,16 @@ static int mount_with_alternatives(struct fstab *fstab, int start_idx, int *end_ } if (fstab->recs[i].fs_mgr_flags & MF_CHECK) { - check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type, - fstab->recs[i].mount_point); + /* Skip file system check unless we are sure we are the right type */ + detected_fs_type = blkid_get_tag_value(NULL, "TYPE", fstab->recs[i].blk_device); + if (detected_fs_type) { + cmp_len = (!strncmp(detected_fs_type, "ext", 3) && + strlen(detected_fs_type) == 4) ? 3 : strlen(detected_fs_type); + if (!strncmp(fstab->recs[i].fs_type, detected_fs_type, cmp_len)) { + check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type, + fstab->recs[i].mount_point); + } + } } if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, &fstab->recs[i])) { *attempted_idx = i; @@ -492,6 +504,52 @@ static int handle_encryptable(struct fstab *fstab, const struct fstab_rec* rec) return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED; } +/* + * Reads the kernel cmdline to check if MDTP is activated. + * When MDTP is activated, kernel cmdline will have the word 'mdtp'. + */ +int fs_mgr_is_mdtp_activated() +{ + char cmdline[2048]; + char *ptr; + int fd; + static int mdtp_activated = 0; + static int mdtp_activated_set = 0; + + if (mdtp_activated_set) { + return mdtp_activated; + } + + fd = open("/proc/cmdline", O_RDONLY); + if (fd >= 0) { + int n = read(fd, cmdline, sizeof(cmdline) - 1); + if (n < 0) n = 0; + + /* get rid of trailing newline, it happens */ + if (n > 0 && cmdline[n-1] == '\n') n--; + + cmdline[n] = 0; + close(fd); + } else { + cmdline[0] = 0; + } + + ptr = cmdline; + while (ptr && *ptr) { + char *x = strchr(ptr, ' '); + if (x != 0) *x++ = 0; + if (!strcmp(ptr,"mdtp")) { + mdtp_activated = 1; + break; + } + ptr = x; + } + + mdtp_activated_set = 1; + + return mdtp_activated; +} + /* When multiple fstab records share the same mount_point, it will * try to mount each one in turn, and ignore any duplicates after a * first successful mount. @@ -547,91 +605,106 @@ int fs_mgr_mount_all(struct fstab *fstab) continue; } } - int last_idx_inspected; - int top_idx = i; - mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx); - i = last_idx_inspected; - mount_errno = errno; + if (fs_mgr_is_mdtp_activated() && ((fstab->recs[i].fs_mgr_flags & MF_FORCECRYPT) || + device_is_force_encrypted())) { + INFO("%s(): mdtp activated, blkdev %s for mount %s type %s expected to be encrypted)\n", + __func__, fstab->recs[i].blk_device, fstab->recs[i].mount_point, + fstab->recs[i].fs_type); + if (fs_mgr_do_tmpfs_mount(fstab->recs[i].mount_point) < 0) { + ++error_count; + continue; + } - /* Deal with encryptability. */ - if (!mret) { - int status = handle_encryptable(fstab, &fstab->recs[attempted_idx]); + encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED; - if (status == FS_MGR_MNTALL_FAIL) { - /* Fatal error - no point continuing */ - return status; - } + } else { + int last_idx_inspected; + int top_idx = i; - if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) { - if (encryptable != FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) { - // Log and continue - ERROR("Only one encryptable/encrypted partition supported\n"); - } - encryptable = status; - } + mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx); + i = last_idx_inspected; + mount_errno = errno; - /* Success! Go get the next one */ - continue; - } + /* Deal with encryptability. */ + if (!mret) { + int status = handle_encryptable(fstab, &fstab->recs[attempted_idx]); - /* mount(2) returned an error, handle the encryptable/formattable case */ - bool wiped = partition_wiped(fstab->recs[top_idx].blk_device); - if (mret && mount_errno != EBUSY && mount_errno != EACCES && - fs_mgr_is_formattable(&fstab->recs[top_idx]) && wiped) { - /* top_idx and attempted_idx point at the same partition, but sometimes - * at two different lines in the fstab. Use the top one for formatting - * as that is the preferred one. - */ - ERROR("%s(): %s is wiped and %s %s is formattable. Format it.\n", __func__, - fstab->recs[top_idx].blk_device, fstab->recs[top_idx].mount_point, - fstab->recs[top_idx].fs_type); - if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) && - strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) { - int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY, 0644); - if (fd >= 0) { - INFO("%s(): also wipe %s\n", __func__, fstab->recs[top_idx].key_loc); - wipe_block_device(fd, get_file_size(fd)); - close(fd); - } else { - ERROR("%s(): %s wouldn't open (%s)\n", __func__, - fstab->recs[top_idx].key_loc, strerror(errno)); + if (status == FS_MGR_MNTALL_FAIL) { + /* Fatal error - no point continuing */ + return status; } - } - if (fs_mgr_do_format(&fstab->recs[top_idx]) == 0) { - /* Let's replay the mount actions. */ - i = top_idx - 1; + + if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) { + if (encryptable != FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) { + // Log and continue + ERROR("Only one encryptable/encrypted partition supported\n"); + } + encryptable = status; + } + + /* Success! Go get the next one */ continue; } - } - if (mret && mount_errno != EBUSY && mount_errno != EACCES && - fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) { - if (wiped) { - ERROR("%s(): %s is wiped and %s %s is encryptable. Suggest recovery...\n", __func__, - fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point, - fstab->recs[attempted_idx].fs_type); - encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY; - continue; - } else { - /* Need to mount a tmpfs at this mountpoint for now, and set - * properties that vold will query later for decrypting + + /* mount(2) returned an error, handle the encryptable/formattable case */ + bool wiped = partition_wiped(fstab->recs[top_idx].blk_device); + if (mret && mount_errno != EBUSY && mount_errno != EACCES && + fs_mgr_is_formattable(&fstab->recs[top_idx]) && wiped) { + /* top_idx and attempted_idx point at the same partition, but sometimes + * at two different lines in the fstab. Use the top one for formatting + * as that is the preferred one. */ - ERROR("%s(): possibly an encryptable blkdev %s for mount %s type %s )\n", __func__, - fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point, - fstab->recs[attempted_idx].fs_type); - if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) { - ++error_count; + ERROR("%s(): %s is wiped and %s %s is formattable. Format it.\n", __func__, + fstab->recs[top_idx].blk_device, fstab->recs[top_idx].mount_point, + fstab->recs[top_idx].fs_type); + if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) && + strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) { + int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY, 0644); + if (fd >= 0) { + INFO("%s(): also wipe %s\n", __func__, fstab->recs[top_idx].key_loc); + wipe_block_device(fd, get_file_size(fd)); + close(fd); + } else { + ERROR("%s(): %s wouldn't open (%s)\n", __func__, + fstab->recs[top_idx].key_loc, strerror(errno)); + } + } + if (fs_mgr_do_format(&fstab->recs[top_idx]) == 0) { + /* Let's replay the mount actions. */ + i = top_idx - 1; continue; } } - encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED; - } else { - ERROR("Failed to mount an un-encryptable or wiped partition on" - "%s at %s options: %s error: %s\n", - fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point, - fstab->recs[attempted_idx].fs_options, strerror(mount_errno)); - ++error_count; - continue; + if (mret && mount_errno != EBUSY && mount_errno != EACCES && + fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) { + if (wiped) { + ERROR("%s(): %s is wiped and %s %s is encryptable. Suggest recovery...\n", __func__, + fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point, + fstab->recs[attempted_idx].fs_type); + encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY; + continue; + } else { + /* Need to mount a tmpfs at this mountpoint for now, and set + * properties that vold will query later for decrypting + */ + ERROR("%s(): possibly an encryptable blkdev %s for mount %s type %s )\n", __func__, + fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point, + fstab->recs[attempted_idx].fs_type); + if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) { + ++error_count; + continue; + } + } + encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED; + } else { + ERROR("Failed to mount an un-encryptable or wiped partition on" + "%s at %s options: %s error: %s\n", + fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point, + fstab->recs[attempted_idx].fs_options, strerror(mount_errno)); + ++error_count; + continue; + } } } @@ -799,6 +872,14 @@ int fs_mgr_swapon_all(struct fstab *fstab) */ FILE *zram_fp; + /* The stream count parameter is only available on new kernels. + * It must be set before the disk size. */ + zram_fp = fopen(ZRAM_STREAMS, "r+"); + if (zram_fp) { + fprintf(zram_fp, "%d\n", fstab->recs[i].zram_streams); + fclose(zram_fp); + } + zram_fp = fopen(ZRAM_CONF_DEV, "r+"); if (zram_fp == NULL) { ERROR("Unable to open zram conf device %s\n", ZRAM_CONF_DEV); diff --git a/fs_mgr/fs_mgr_format.c b/fs_mgr/fs_mgr_format.c index c73045d..8bda19c 100644 --- a/fs_mgr/fs_mgr_format.c +++ b/fs_mgr/fs_mgr_format.c @@ -31,9 +31,9 @@ extern struct fs_info info; /* magic global from ext4_utils */ extern void reset_ext4fs_info(); -static int format_ext4(char *fs_blkdev, char *fs_mnt_point) +static int format_ext4(char *fs_blkdev, char *fs_mnt_point, long long fs_length) { - unsigned int nr_sec; + uint64_t dev_sz; int fd, rc = 0; if ((fd = open(fs_blkdev, O_WRONLY, 0644)) < 0) { @@ -41,7 +41,7 @@ static int format_ext4(char *fs_blkdev, char *fs_mnt_point) return -1; } - if ((ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) { + if ((ioctl(fd, BLKGETSIZE64, &dev_sz)) == -1) { ERROR("Cannot get block device size. %s\n", strerror(errno)); close(fd); return -1; @@ -49,7 +49,13 @@ static int format_ext4(char *fs_blkdev, char *fs_mnt_point) /* Format the partition using the calculated length */ reset_ext4fs_info(); - info.len = ((off64_t)nr_sec * 512); + info.len = (off64_t)dev_sz; + + if (fs_length > 0) { + info.len = fs_length; + } else if (fs_length < 0) { + info.len += fs_length; + } /* Use make_ext4fs_internal to avoid wiping an already-wiped partition. */ rc = make_ext4fs_internal(fd, NULL, NULL, fs_mnt_point, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL); @@ -61,15 +67,27 @@ static int format_ext4(char *fs_blkdev, char *fs_mnt_point) return rc; } -static int format_f2fs(char *fs_blkdev) +static int format_f2fs(char *fs_blkdev, long long fs_length) { - char * args[3]; + char * args[5]; int pid; int rc = 0; - - args[0] = (char *)"/sbin/mkfs.f2fs"; - args[1] = fs_blkdev; - args[2] = (char *)0; + char buff[65]; + + args[0] = (char *)"/system/bin/mkfs.f2fs"; + + if (fs_length >= 0) { + snprintf(buff, sizeof(buff), "%lld", fs_length / 512); + args[1] = fs_blkdev; + args[2] = buff; + args[3] = (char *)0; + } else if (fs_length < 0) { + snprintf(buff, sizeof(buff), "%lld", -fs_length); + args[1] = "-r"; + args[2] = buff; + args[3] = fs_blkdev; + args[4] = (char *)0; + } pid = fork(); if (pid < 0) { @@ -77,7 +95,7 @@ static int format_f2fs(char *fs_blkdev) } if (!pid) { /* This doesn't return */ - execv("/sbin/mkfs.f2fs", args); + execv("/system/bin/mkfs.f2fs", args); exit(1); } for(;;) { @@ -108,9 +126,9 @@ int fs_mgr_do_format(struct fstab_rec *fstab) ERROR("%s: Format %s as '%s'.\n", __func__, fstab->blk_device, fstab->fs_type); if (!strncmp(fstab->fs_type, "f2fs", 4)) { - rc = format_f2fs(fstab->blk_device); + rc = format_f2fs(fstab->blk_device, fstab->length); } else if (!strncmp(fstab->fs_type, "ext4", 4)) { - rc = format_ext4(fstab->blk_device, fstab->mount_point); + rc = format_ext4(fstab->blk_device, fstab->mount_point, fstab->length); } else { ERROR("File system type '%s' is not supported\n", fstab->fs_type); } diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c index f24af1f..b1b0325 100644 --- a/fs_mgr/fs_mgr_fstab.c +++ b/fs_mgr/fs_mgr_fstab.c @@ -32,6 +32,7 @@ struct fs_mgr_flag_values { int partnum; int swap_prio; unsigned int zram_size; + unsigned int zram_streams; }; struct flag_list { @@ -74,6 +75,7 @@ static struct flag_list fs_mgr_flags[] = { { "noemulatedsd", MF_NOEMULATEDSD }, { "notrim", MF_NOTRIM }, { "formattable", MF_FORMATTABLE }, + { "zramstreams=",MF_ZRAMSTREAMS }, { "defaults", 0 }, { 0, 0 }, }; @@ -106,6 +108,7 @@ static int parse_flags(char *flags, struct flag_list *fl, memset(flag_vals, 0, sizeof(*flag_vals)); flag_vals->partnum = -1; flag_vals->swap_prio = -1; /* negative means it wasn't specified. */ + flag_vals->zram_streams = 1; } /* initialize fs_options to the null string */ @@ -178,6 +181,8 @@ static int parse_flags(char *flags, struct flag_list *fl, flag_vals->zram_size = calculate_zram_size(val); else flag_vals->zram_size = val; + } else if ((fl[i].flag == MF_ZRAMSTREAMS) && flag_vals) { + flag_vals->zram_streams = strtoll(strchr(p, '=') + 1, NULL, 0); } break; } @@ -329,6 +334,7 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path) fstab->recs[cnt].partnum = flag_vals.partnum; fstab->recs[cnt].swap_prio = flag_vals.swap_prio; fstab->recs[cnt].zram_size = flag_vals.zram_size; + fstab->recs[cnt].zram_streams = flag_vals.zram_streams; cnt++; } fclose(fstab_file); diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h index 682fd11..b72e200 100644 --- a/fs_mgr/fs_mgr_priv.h +++ b/fs_mgr/fs_mgr_priv.h @@ -79,6 +79,7 @@ #define MF_NOTRIM 0x1000 #define MF_FILEENCRYPTION 0x2000 #define MF_FORMATTABLE 0x4000 +#define MF_ZRAMSTREAMS 0x8000 #define DM_BUF_SIZE 4096 diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h index 27fccf7..5e05d9a 100644 --- a/fs_mgr/include/fs_mgr.h +++ b/fs_mgr/include/fs_mgr.h @@ -65,6 +65,7 @@ struct fstab_rec { int partnum; int swap_prio; unsigned int zram_size; + unsigned int zram_streams; }; // Callback function for verity status @@ -97,6 +98,7 @@ int fs_mgr_add_entry(struct fstab *fstab, const char *mount_point, const char *fs_type, const char *blk_device); struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path); +struct fstab_rec *fs_mgr_get_entry_for_mount_point_after(struct fstab_rec *start_rec, struct fstab *fstab, const char *path); int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab); int fs_mgr_is_nonremovable(const struct fstab_rec *fstab); int fs_mgr_is_verified(const struct fstab_rec *fstab); @@ -106,6 +108,7 @@ int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab); int fs_mgr_is_notrim(struct fstab_rec *fstab); int fs_mgr_is_formattable(struct fstab_rec *fstab); int fs_mgr_swapon_all(struct fstab *fstab); +int fs_mgr_is_mdtp_activated(void); int fs_mgr_do_format(struct fstab_rec *fstab); diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h index 75fe11d..c8010ca 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 6fc4ac0..a62b1d4 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/Android.mk b/healthd/Android.mk index 07e1d73..7db4ad5 100644 --- a/healthd/Android.mk +++ b/healthd/Android.mk @@ -9,6 +9,12 @@ LOCAL_CFLAGS := -Werror include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) +LOCAL_SRC_FILES := healthd_board_msm.cpp +LOCAL_MODULE := libhealthd.qcom +LOCAL_CFLAGS := -Werror +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) LOCAL_SRC_FILES := \ healthd.cpp \ @@ -25,6 +31,18 @@ LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) LOCAL_CFLAGS := -D__STDC_LIMIT_MACROS -Werror +HEALTHD_CHARGER_DEFINES := RED_LED_PATH \ + GREEN_LED_PATH \ + BLUE_LED_PATH \ + BACKLIGHT_PATH \ + CHARGING_ENABLED_PATH + +$(foreach healthd_charger_define,$(HEALTHD_CHARGER_DEFINES), \ + $(if $($(healthd_charger_define)), \ + $(eval LOCAL_CFLAGS += -D$(healthd_charger_define)=\"$($(healthd_charger_define))\") \ + ) \ +) + ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true) LOCAL_CFLAGS += -DCHARGER_DISABLE_INIT_BLANK endif @@ -33,6 +51,10 @@ ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true) LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND endif +ifeq ($(strip $(BOARD_NO_CHARGER_LED)),true) +LOCAL_CFLAGS += -DNO_CHARGER_LED +endif + LOCAL_C_INCLUDES := bootable/recovery LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libminui libpng libz libutils libstdc++ libcutils liblog libm libc @@ -43,9 +65,13 @@ endif LOCAL_HAL_STATIC_LIBRARIES := libhealthd +ifeq ($(BOARD_USES_QCOM_HARDWARE),true) +BOARD_HAL_STATIC_LIBRARIES ?= libhealthd.qcom +endif + # Symlink /charger to /sbin/healthd LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \ - && ln -sf /sbin/healthd $(TARGET_ROOT_OUT)/charger + && rm -f $(TARGET_ROOT_OUT)/charger && ln -sf /sbin/healthd $(TARGET_ROOT_OUT)/charger include $(BUILD_EXECUTABLE) @@ -63,8 +89,13 @@ include $$(BUILD_PREBUILT) endef _img_modules := +ifeq ($(strip $(BOARD_HEALTHD_CUSTOM_CHARGER_RES)),) +IMAGES_DIR := images +else +IMAGES_DIR := ../../../$(BOARD_HEALTHD_CUSTOM_CHARGER_RES) +endif _images := -$(foreach _img, $(call find-subdir-subdir-files, "images", "*.png"), \ +$(foreach _img, $(call find-subdir-subdir-files, "$(IMAGES_DIR)", "*.png"), \ $(eval $(call _add-charger-image,$(_img)))) include $(CLEAR_VARS) diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp index 396dfef..67a8ad0 100644 --- a/healthd/BatteryMonitor.cpp +++ b/healthd/BatteryMonitor.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2015 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -137,7 +138,12 @@ 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_HVDCP", ANDROID_POWER_SUPPLY_TYPE_AC }, + { "USB_HVDCP_3", ANDROID_POWER_SUPPLY_TYPE_AC }, { "Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS }, + { "Wipower", ANDROID_POWER_SUPPLY_TYPE_WIRELESS }, + { "DockBattery", ANDROID_POWER_SUPPLY_TYPE_DOCK_BATTERY }, + { "DockAC", ANDROID_POWER_SUPPLY_TYPE_DOCK_AC }, { NULL, 0 }, }; @@ -182,9 +188,12 @@ bool BatteryMonitor::update(void) { props.chargerAcOnline = false; props.chargerUsbOnline = false; props.chargerWirelessOnline = false; + props.chargerDockAcOnline = false; props.batteryStatus = BATTERY_STATUS_UNKNOWN; props.batteryHealth = BATTERY_HEALTH_UNKNOWN; props.maxChargingCurrent = 0; + props.dockBatteryStatus = BATTERY_STATUS_UNKNOWN; + props.dockBatteryHealth = BATTERY_HEALTH_UNKNOWN; if (!mHealthdConfig->batteryPresentPath.isEmpty()) props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath); @@ -222,53 +231,109 @@ bool BatteryMonitor::update(void) { if (readFromFile(mHealthdConfig->batteryTechnologyPath, buf, SIZE) > 0) props.batteryTechnology = String8(buf); - unsigned int i; + props.dockBatterySupported = mHealthdConfig->dockBatterySupported; + if (props.dockBatterySupported) { + if (!mHealthdConfig->dockBatteryPresentPath.isEmpty()) + props.dockBatteryPresent = getBooleanField(mHealthdConfig->dockBatteryPresentPath); + else + props.dockBatteryPresent = mDockBatteryDevicePresent; - for (i = 0; i < mChargerNames.size(); i++) { - String8 path; - path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, - mChargerNames[i].string()); + props.dockBatteryLevel = mBatteryFixedCapacity ? + mBatteryFixedCapacity : + getIntField(mHealthdConfig->dockBatteryCapacityPath); + props.dockBatteryVoltage = getIntField(mHealthdConfig->dockBatteryVoltagePath) / 1000; - if (readFromFile(path, buf, SIZE) > 0) { - if (buf[0] != '0') { - path.clear(); - path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, - mChargerNames[i].string()); - switch(readPowerSupplyType(path)) { - case ANDROID_POWER_SUPPLY_TYPE_AC: - props.chargerAcOnline = true; - break; - case ANDROID_POWER_SUPPLY_TYPE_USB: - props.chargerUsbOnline = true; - break; - case ANDROID_POWER_SUPPLY_TYPE_WIRELESS: - props.chargerWirelessOnline = true; - break; - default: - KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n", - mChargerNames[i].string()); - } + props.dockBatteryTemperature = mBatteryFixedTemperature ? + mBatteryFixedTemperature : + getIntField(mHealthdConfig->dockBatteryTemperaturePath); + + if (readFromFile(mHealthdConfig->dockBatteryStatusPath, buf, SIZE) > 0) + props.dockBatteryStatus = getBatteryStatus(buf); + + if (readFromFile(mHealthdConfig->dockBatteryHealthPath, buf, SIZE) > 0) + props.dockBatteryHealth = getBatteryHealth(buf); + + if (readFromFile(mHealthdConfig->dockBatteryTechnologyPath, buf, SIZE) > 0) + props.dockBatteryTechnology = String8(buf); + } + + // reinitialize the mChargerNames vector everytime there is an update + String8 path; + DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH); + if (dir == NULL) { + KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH); + } else { + struct dirent* entry; + // reconstruct the charger strings + mChargerNames.clear(); + while ((entry = readdir(dir))) { + const char* name = entry->d_name; + + if (!strcmp(name, ".") || !strcmp(name, "..")) + continue; + + // Look for "type" file in each subdirectory + path.clear(); + path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name); + switch(readPowerSupplyType(path)) { + case ANDROID_POWER_SUPPLY_TYPE_BATTERY: + case ANDROID_POWER_SUPPLY_TYPE_DOCK_BATTERY: + break; + default: path.clear(); - path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH, - mChargerNames[i].string()); + path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name); if (access(path.string(), R_OK) == 0) { - int maxChargingCurrent = getIntField(path); - if (props.maxChargingCurrent < maxChargingCurrent) { - props.maxChargingCurrent = maxChargingCurrent; + mChargerNames.add(String8(name)); + if (readFromFile(path, buf, SIZE) > 0) { + if (buf[0] != '0') { + path.clear(); + path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, + name); + switch(readPowerSupplyType(path)) { + case ANDROID_POWER_SUPPLY_TYPE_AC: + props.chargerAcOnline = true; + break; + case ANDROID_POWER_SUPPLY_TYPE_USB: + props.chargerUsbOnline = true; + break; + case ANDROID_POWER_SUPPLY_TYPE_WIRELESS: + props.chargerWirelessOnline = true; + break; + case ANDROID_POWER_SUPPLY_TYPE_DOCK_AC: + if (mHealthdConfig->dockBatterySupported) { + props.chargerDockAcOnline = true; + } + default: + KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n", + name); + } + path.clear(); + path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH, + name); + if (access(path.string(), R_OK) == 0) { + int maxChargingCurrent = getIntField(path); + if (props.maxChargingCurrent < maxChargingCurrent) { + props.maxChargingCurrent = maxChargingCurrent; + } + } + } } } - } - } - } + break; + } //switch + } //while + closedir(dir); + }//else logthis = !healthd_board_battery_update(&props); if (logthis) { char dmesgline[256]; + char dmesglinedock[256]; if (props.batteryPresent) { snprintf(dmesgline, sizeof(dmesgline), - "battery l=%d v=%d t=%s%d.%d h=%d st=%d", + "battery [l=%d v=%d t=%s%d.%d h=%d st=%d]", props.batteryLevel, props.batteryVoltage, props.batteryTemperature < 0 ? "-" : "", abs(props.batteryTemperature / 10), @@ -287,6 +352,27 @@ bool BatteryMonitor::update(void) { "battery none"); } + if (props.dockBatteryPresent) { + snprintf(dmesglinedock, sizeof(dmesglinedock), + "dock-battery [l=%d v=%d t=%s%d.%d h=%d st=%d]", + props.dockBatteryLevel, props.dockBatteryVoltage, + props.dockBatteryTemperature < 0 ? "-" : "", + abs(props.dockBatteryTemperature / 10), + abs(props.dockBatteryTemperature % 10), props.dockBatteryHealth, + props.dockBatteryStatus); + + if (!mHealthdConfig->dockBatteryCurrentNowPath.isEmpty()) { + int c = getIntField(mHealthdConfig->dockBatteryCurrentNowPath); + char b[20]; + + snprintf(b, sizeof(b), " c=%d", c / 1000); + strlcat(dmesglinedock, b, sizeof(dmesglinedock)); + } + } else { + snprintf(dmesglinedock, sizeof(dmesglinedock), + "dock-battery none"); + } + size_t len = strlen(dmesgline); snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s", props.chargerAcOnline ? "a" : "", @@ -317,7 +403,7 @@ bool BatteryMonitor::update(void) { healthd_mode_ops->battery_update(&props); return props.chargerAcOnline | props.chargerUsbOnline | - props.chargerWirelessOnline; + props.chargerWirelessOnline | props.chargerDockAcOnline; } status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) { @@ -381,13 +467,77 @@ status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) { return ret; } +status_t BatteryMonitor::getDockProperty(int id, struct BatteryProperty *val) { + status_t ret = BAD_VALUE; + if (!mHealthdConfig->dockBatterySupported) { + return ret; + } + + val->valueInt64 = LONG_MIN; + + switch(id) { + case BATTERY_PROP_CHARGE_COUNTER: + if (!mHealthdConfig->dockBatteryChargeCounterPath.isEmpty()) { + val->valueInt64 = + getIntField(mHealthdConfig->dockBatteryChargeCounterPath); + ret = NO_ERROR; + } else { + ret = NAME_NOT_FOUND; + } + break; + + case BATTERY_PROP_CURRENT_NOW: + if (!mHealthdConfig->dockBatteryCurrentNowPath.isEmpty()) { + val->valueInt64 = + getIntField(mHealthdConfig->dockBatteryCurrentNowPath); + ret = NO_ERROR; + } else { + ret = NAME_NOT_FOUND; + } + break; + + case BATTERY_PROP_CURRENT_AVG: + if (!mHealthdConfig->dockBatteryCurrentAvgPath.isEmpty()) { + val->valueInt64 = + getIntField(mHealthdConfig->dockBatteryCurrentAvgPath); + ret = NO_ERROR; + } else { + ret = NAME_NOT_FOUND; + } + break; + + case BATTERY_PROP_CAPACITY: + if (!mHealthdConfig->dockBatteryCapacityPath.isEmpty()) { + val->valueInt64 = + getIntField(mHealthdConfig->dockBatteryCapacityPath); + ret = NO_ERROR; + } else { + ret = NAME_NOT_FOUND; + } + break; + + case BATTERY_PROP_ENERGY_COUNTER: + if (mHealthdConfig->dockEnergyCounter) { + ret = mHealthdConfig->dockEnergyCounter(&val->valueInt64); + } else { + ret = NAME_NOT_FOUND; + } + break; + + default: + break; + } + + return ret; +} + void BatteryMonitor::dumpState(int fd) { int v; char vs[128]; - snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d\n", + snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d dock-ac: %d current_max: %d\n", props.chargerAcOnline, props.chargerUsbOnline, - props.chargerWirelessOnline, props.maxChargingCurrent); + props.chargerWirelessOnline, props.chargerDockAcOnline, props.maxChargingCurrent); write(fd, vs, strlen(vs)); snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n", props.batteryStatus, props.batteryHealth, props.batteryPresent); @@ -414,6 +564,34 @@ void BatteryMonitor::dumpState(int fd) { snprintf(vs, sizeof(vs), "charge counter: %d\n", v); write(fd, vs, strlen(vs)); } + + if (mHealthdConfig->dockBatterySupported) { + snprintf(vs, sizeof(vs), "dock-status: %d dock-health: %d dock-present: %d\n", + props.dockBatteryStatus, props.dockBatteryHealth, props.dockBatteryPresent); + write(fd, vs, strlen(vs)); + snprintf(vs, sizeof(vs), "dock-level: %d dock-voltage: %d dock-temp: %d\n", + props.dockBatteryLevel, props.dockBatteryVoltage, + props.dockBatteryTemperature); + write(fd, vs, strlen(vs)); + + if (!mHealthdConfig->dockBatteryCurrentNowPath.isEmpty()) { + v = getIntField(mHealthdConfig->dockBatteryCurrentNowPath); + snprintf(vs, sizeof(vs), "dock-current now: %d\n", v); + write(fd, vs, strlen(vs)); + } + + if (!mHealthdConfig->dockBatteryCurrentAvgPath.isEmpty()) { + v = getIntField(mHealthdConfig->dockBatteryCurrentAvgPath); + snprintf(vs, sizeof(vs), "dock-current avg: %d\n", v); + write(fd, vs, strlen(vs)); + } + + if (!mHealthdConfig->dockBatteryChargeCounterPath.isEmpty()) { + v = getIntField(mHealthdConfig->dockBatteryChargeCounterPath); + snprintf(vs, sizeof(vs), "dock-charge counter: %d\n", v); + write(fd, vs, strlen(vs)); + } + } } void BatteryMonitor::init(struct healthd_config *hc) { @@ -440,6 +618,7 @@ void BatteryMonitor::init(struct healthd_config *hc) { case ANDROID_POWER_SUPPLY_TYPE_AC: case ANDROID_POWER_SUPPLY_TYPE_USB: case ANDROID_POWER_SUPPLY_TYPE_WIRELESS: + case ANDROID_POWER_SUPPLY_TYPE_DOCK_AC: path.clear(); path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name); if (access(path.string(), R_OK) == 0) @@ -545,6 +724,107 @@ void BatteryMonitor::init(struct healthd_config *hc) { break; + case ANDROID_POWER_SUPPLY_TYPE_DOCK_BATTERY: + if (mHealthdConfig->dockBatterySupported) { + mDockBatteryDevicePresent = true; + + if (mHealthdConfig->dockBatteryStatusPath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH, + name); + if (access(path, R_OK) == 0) + mHealthdConfig->dockBatteryStatusPath = path; + } + + if (mHealthdConfig->dockBatteryHealthPath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH, + name); + if (access(path, R_OK) == 0) + mHealthdConfig->dockBatteryHealthPath = path; + } + + if (mHealthdConfig->dockBatteryPresentPath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH, + name); + if (access(path, R_OK) == 0) + mHealthdConfig->dockBatteryPresentPath = path; + } + + if (mHealthdConfig->dockBatteryCapacityPath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH, + name); + if (access(path, R_OK) == 0) + mHealthdConfig->dockBatteryCapacityPath = path; + } + + if (mHealthdConfig->dockBatteryVoltagePath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/voltage_now", + POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) { + mHealthdConfig->dockBatteryVoltagePath = path; + } else { + path.clear(); + path.appendFormat("%s/%s/batt_vol", + POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mHealthdConfig->dockBatteryVoltagePath = path; + } + } + + if (mHealthdConfig->dockBatteryCurrentNowPath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/current_now", + POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mHealthdConfig->dockBatteryCurrentNowPath = path; + } + + if (mHealthdConfig->dockBatteryCurrentAvgPath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/current_avg", + POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mHealthdConfig->dockBatteryCurrentAvgPath = path; + } + + if (mHealthdConfig->dockBatteryChargeCounterPath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/charge_counter", + POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mHealthdConfig->dockBatteryChargeCounterPath = path; + } + + if (mHealthdConfig->dockBatteryTemperaturePath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH, + name); + if (access(path, R_OK) == 0) { + mHealthdConfig->dockBatteryTemperaturePath = path; + } else { + path.clear(); + path.appendFormat("%s/%s/batt_temp", + POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mHealthdConfig->dockBatteryTemperaturePath = path; + } + } + + if (mHealthdConfig->dockBatteryTechnologyPath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/technology", + POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mHealthdConfig->dockBatteryTechnologyPath = path; + } + } + + break; + case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN: break; } @@ -561,7 +841,7 @@ void BatteryMonitor::init(struct healthd_config *hc) { mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE; mAlwaysPluggedDevice = true; } - if (!mBatteryDevicePresent) { + if (!mBatteryDevicePresent && !mDockBatteryDevicePresent) { KLOG_WARNING(LOG_TAG, "No battery devices found\n"); hc->periodic_chores_interval_fast = -1; hc->periodic_chores_interval_slow = -1; @@ -580,6 +860,23 @@ void BatteryMonitor::init(struct healthd_config *hc) { KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n"); if (mHealthdConfig->batteryTechnologyPath.isEmpty()) KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n"); + + if (mHealthdConfig->dockBatterySupported) { + if (mHealthdConfig->dockBatteryStatusPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "DockBatteryStatusPath not found\n"); + if (mHealthdConfig->dockBatteryHealthPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "DockBatteryHealthPath not found\n"); + if (mHealthdConfig->dockBatteryPresentPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "DockBatteryPresentPath not found\n"); + if (mHealthdConfig->dockBatteryCapacityPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "DockBatteryCapacityPath not found\n"); + if (mHealthdConfig->dockBatteryVoltagePath.isEmpty()) + KLOG_WARNING(LOG_TAG, "DockBatteryVoltagePath not found\n"); + if (mHealthdConfig->dockBatteryTemperaturePath.isEmpty()) + KLOG_WARNING(LOG_TAG, "DockBatteryTemperaturePath not found\n"); + if (mHealthdConfig->dockBatteryTechnologyPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "DockBatteryTechnologyPath not found\n"); + } } if (property_get("ro.boot.fake_battery", pval, NULL) > 0 diff --git a/healthd/BatteryMonitor.h b/healthd/BatteryMonitor.h index a61171f..4320348 100644 --- a/healthd/BatteryMonitor.h +++ b/healthd/BatteryMonitor.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2015 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,18 +35,22 @@ class BatteryMonitor { ANDROID_POWER_SUPPLY_TYPE_AC, ANDROID_POWER_SUPPLY_TYPE_USB, ANDROID_POWER_SUPPLY_TYPE_WIRELESS, - ANDROID_POWER_SUPPLY_TYPE_BATTERY + ANDROID_POWER_SUPPLY_TYPE_BATTERY, + ANDROID_POWER_SUPPLY_TYPE_DOCK_AC, + ANDROID_POWER_SUPPLY_TYPE_DOCK_BATTERY }; void init(struct healthd_config *hc); bool update(void); status_t getProperty(int id, struct BatteryProperty *val); + status_t getDockProperty(int id, struct BatteryProperty *val); void dumpState(int fd); private: struct healthd_config *mHealthdConfig; Vector<String8> mChargerNames; bool mBatteryDevicePresent; + bool mDockBatteryDevicePresent; bool mAlwaysPluggedDevice; int mBatteryFixedCapacity; int mBatteryFixedTemperature; diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp index 09667a1..7a28ead 100644 --- a/healthd/BatteryPropertiesRegistrar.cpp +++ b/healthd/BatteryPropertiesRegistrar.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2015 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -76,6 +77,10 @@ status_t BatteryPropertiesRegistrar::getProperty(int id, struct BatteryProperty return healthd_get_property(id, val); } +status_t BatteryPropertiesRegistrar::getDockProperty(int id, struct BatteryProperty *val) { + return healthd_get_dock_property(id, val); +} + status_t BatteryPropertiesRegistrar::dump(int fd, const Vector<String16>& /*args*/) { IPCThreadState* self = IPCThreadState::self(); const int pid = self->getCallingPid(); diff --git a/healthd/BatteryPropertiesRegistrar.h b/healthd/BatteryPropertiesRegistrar.h index 8853874..5ca4fc1 100644 --- a/healthd/BatteryPropertiesRegistrar.h +++ b/healthd/BatteryPropertiesRegistrar.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2015 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,6 +41,7 @@ private: void registerListener(const sp<IBatteryPropertiesListener>& listener); void unregisterListener(const sp<IBatteryPropertiesListener>& listener); status_t getProperty(int id, struct BatteryProperty *val); + status_t getDockProperty(int id, struct BatteryProperty *val); status_t dump(int fd, const Vector<String16>& args); void binderDied(const wp<IBinder>& who); }; diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp index b0002cc..1489912 100644 --- a/healthd/healthd.cpp +++ b/healthd/healthd.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2015 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,8 +37,15 @@ using namespace android; // Periodic chores intervals in seconds +#ifdef QCOM_HARDWARE +#define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 10) +//For the designs without low battery detection,need to enable +//the default 60*10s wakeup timer to periodic check. +#define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW -1 +#else #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1) #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10) +#endif static struct healthd_config healthd_config = { .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST, @@ -55,6 +63,18 @@ static struct healthd_config healthd_config = { .energyCounter = NULL, .boot_min_cap = 0, .screen_on = NULL, + .dockBatterySupported = false, + .dockBatteryStatusPath = String8(String8::kEmptyString), + .dockBatteryHealthPath = String8(String8::kEmptyString), + .dockBatteryPresentPath = String8(String8::kEmptyString), + .dockBatteryCapacityPath = String8(String8::kEmptyString), + .dockBatteryVoltagePath = String8(String8::kEmptyString), + .dockBatteryTemperaturePath = String8(String8::kEmptyString), + .dockBatteryTechnologyPath = String8(String8::kEmptyString), + .dockBatteryCurrentNowPath = String8(String8::kEmptyString), + .dockBatteryCurrentAvgPath = String8(String8::kEmptyString), + .dockBatteryChargeCounterPath = String8(String8::kEmptyString), + .dockEnergyCounter = NULL, }; static int eventct; @@ -173,6 +193,10 @@ status_t healthd_get_property(int id, struct BatteryProperty *val) { return gBatteryMonitor->getProperty(id, val); } +status_t healthd_get_dock_property(int id, struct BatteryProperty *val) { + return gBatteryMonitor->getDockProperty(id, val); +} + void healthd_battery_update(void) { // Fast wake interval when on charger (watch for overheat); // slow wake interval when on battery (watch for drained battery). @@ -357,6 +381,9 @@ int main(int argc, char **argv) { exit(2); } + periodic_chores(); + healthd_mode_ops->heartbeat(); + healthd_mainloop(); KLOG_ERROR("Main loop terminated, exiting\n"); return 3; diff --git a/healthd/healthd.h b/healthd/healthd.h index 84b6d76..0b2a16e 100644 --- a/healthd/healthd.h +++ b/healthd/healthd.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2015 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,8 +49,25 @@ // batteryTemperaturePath: battery temperature (POWER_SUPPLY_PROP_TEMP) // batteryTechnologyPath: battery technology (POWER_SUPPLY_PROP_TECHNOLOGY) // batteryCurrentNowPath: battery current (POWER_SUPPLY_PROP_CURRENT_NOW) +// batteryCurrentAvgPath: battery average (POWER_SUPPLY_PROP_CURRENT_AVG) // batteryChargeCounterPath: battery accumulated charge // (POWER_SUPPLY_PROP_CHARGE_COUNTER) +// +// dockBatteryStatusPath: dock charging status (POWER_SUPPLY_PROP_STATUS) +// dockBatteryHealthPath: dock battery health (POWER_SUPPLY_PROP_HEALTH) +// dockBatteryPresentPath: dock battery present (POWER_SUPPLY_PROP_PRESENT) +// dockBatteryCapacityPath: remaining dock capacity (POWER_SUPPLY_PROP_CAPACITY) +// dockBatteryVoltagePath: dock battery voltage (POWER_SUPPLY_PROP_VOLTAGE_NOW) +// dockBatteryTemperaturePath: dock battery temperature (POWER_SUPPLY_PROP_TEMP) +// dockBatteryTechnologyPath: dock battery technology (POWER_SUPPLY_PROP_TECHNOLOGY) +// dockBatteryCurrentNowPath: dock battery current (POWER_SUPPLY_PROP_CURRENT_NOW) +// dockBatteryCurrentAvgPath: dock battery average (POWER_SUPPLY_PROP_CURRENT_AVG) +// dockBatteryChargeCounterPath: dock battery accumulated charge +// (POWER_SUPPLY_PROP_CHARGE_COUNTER) +// +// The dockBatterySupported property indicates whether a dock battery is supported +// by the device, and whether this module should fetch dock battery values. +// Defaults is to false. struct healthd_config { int periodic_chores_interval_fast; @@ -69,6 +87,20 @@ struct healthd_config { int (*energyCounter)(int64_t *); int boot_min_cap; bool (*screen_on)(android::BatteryProperties *props); + + bool dockBatterySupported; + android::String8 dockBatteryStatusPath; + android::String8 dockBatteryHealthPath; + android::String8 dockBatteryPresentPath; + android::String8 dockBatteryCapacityPath; + android::String8 dockBatteryVoltagePath; + android::String8 dockBatteryTemperaturePath; + android::String8 dockBatteryTechnologyPath; + android::String8 dockBatteryCurrentNowPath; + android::String8 dockBatteryCurrentAvgPath; + android::String8 dockBatteryChargeCounterPath; + + int (*dockEnergyCounter)(int64_t *); }; // Global helper functions @@ -77,6 +109,8 @@ int healthd_register_event(int fd, void (*handler)(uint32_t)); void healthd_battery_update(); android::status_t healthd_get_property(int id, struct android::BatteryProperty *val); +android::status_t healthd_get_dock_property(int id, + struct android::BatteryProperty *val); void healthd_dump_battery_state(int fd); struct healthd_mode_ops { @@ -121,4 +155,20 @@ void healthd_board_init(struct healthd_config *config); int healthd_board_battery_update(struct android::BatteryProperties *props); +//This API is called to turn on/off the display backlight incase it can't be +//handle correcty in the display driver by calling gr_fg_blank(); +void healthd_board_mode_charger_set_backlight(bool en); + +//This API is called to update the battery/charging status by using the user +//noticeable method other then the animation, such as: LEDs +void healthd_board_mode_charger_battery_update(struct android::BatteryProperties *batt_prop); + +//This API is used to handle some board specific charger mode initialization, +//such as: checking the charging is enabled or not. +void healthd_board_mode_charger_init(void); + +//This API is called in the draw_battery() function to draw more infomation +//about the battery on the animation, such as the SoC (State of Charge). +void healthd_board_mode_charger_draw_battery(struct android::BatteryProperties *batt_prop); + #endif /* _HEALTHD_H_ */ diff --git a/healthd/healthd_board_default.cpp b/healthd/healthd_board_default.cpp index ed4ddb4..c511c9c 100644 --- a/healthd/healthd_board_default.cpp +++ b/healthd/healthd_board_default.cpp @@ -25,5 +25,25 @@ void healthd_board_init(struct healthd_config*) int healthd_board_battery_update(struct android::BatteryProperties*) { // return 0 to log periodic polled battery status to kernel log - return 0; + return 1; +} + +void healthd_board_mode_charger_draw_battery(struct android::BatteryProperties*) +{ + +} + +void healthd_board_mode_charger_battery_update(struct android::BatteryProperties*) +{ + +} + +void healthd_board_mode_charger_set_backlight(bool) +{ + +} + +void healthd_board_mode_charger_init() +{ + } diff --git a/healthd/healthd_board_msm.cpp b/healthd/healthd_board_msm.cpp new file mode 100644 index 0000000..d17095f --- /dev/null +++ b/healthd/healthd_board_msm.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2014 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 <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> + +#include <cutils/android_reboot.h> +#include <cutils/klog.h> +#include <cutils/misc.h> +#include <cutils/uevent.h> +#include <cutils/properties.h> + +#include <pthread.h> +#include <linux/android_alarm.h> +#include <sys/timerfd.h> +#include <linux/rtc.h> + +#include <healthd.h> + +#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0) +#define LOGI(x...) do { KLOG_INFO("charger", x); } while (0) +#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0) + +enum alarm_time_type { + ALARM_TIME, + RTC_TIME, +}; + +/* + * shouldn't be changed after + * reading from alarm register + */ +static time_t alm_secs; + +static int alarm_get_time(enum alarm_time_type time_type, + time_t *secs) +{ + struct tm tm; + unsigned int cmd; + int rc, fd = -1; + + if (!secs) + return -1; + + fd = open("/dev/rtc0", O_RDONLY); + if (fd < 0) { + LOGE("Can't open rtc devfs node\n"); + return -1; + } + + switch (time_type) { + case ALARM_TIME: + cmd = RTC_ALM_READ; + break; + case RTC_TIME: + cmd = RTC_RD_TIME; + break; + default: + LOGE("Invalid time type\n"); + goto err; + } + + rc = ioctl(fd, cmd, &tm); + if (rc < 0) { + LOGE("Unable to get time\n"); + goto err; + } + + *secs = mktime(&tm) + tm.tm_gmtoff; + if (*secs < 0) { + LOGE("Invalid seconds = %ld\n", *secs); + goto err; + } + + close(fd); + return 0; + +err: + close(fd); + return -1; +} + +#define ERR_SECS 2 +static int alarm_is_alm_expired() +{ + int rc; + time_t rtc_secs; + + rc = alarm_get_time(RTC_TIME, &rtc_secs); + if (rc < 0) + return 0; + + return (alm_secs >= rtc_secs - ERR_SECS && + alm_secs <= rtc_secs + ERR_SECS) ? 1 : 0; +} + +static int timerfd_set_reboot_time_and_wait(time_t secs) +{ + int fd; + int ret = -1; + fd = timerfd_create(CLOCK_REALTIME_ALARM, 0); + if (fd < 0) { + LOGE("Can't open timerfd alarm node\n"); + goto err_return; + } + + struct itimerspec spec; + memset(&spec, 0, sizeof(spec)); + spec.it_value.tv_sec = secs; + + if (timerfd_settime(fd, 0 /* relative */, &spec, NULL)) { + LOGE("Can't set timerfd alarm\n"); + goto err_close; + } + + uint64_t unused; + if (read(fd, &unused, sizeof(unused)) < 0) { + LOGE("Wait alarm error\n"); + goto err_close; + } + + ret = 0; +err_close: + close(fd); +err_return: + return ret; +} + +static int alarm_set_reboot_time_and_wait(time_t secs) +{ + int rc, fd; + struct timespec ts; + + fd = open("/dev/alarm", O_RDWR); + if (fd < 0) { + LOGE("Can't open alarm devfs node, trying timerfd\n"); + return timerfd_set_reboot_time_and_wait(secs); + } + + /* get the elapsed realtime from boot time to now */ + rc = ioctl(fd, ANDROID_ALARM_GET_TIME( + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP), &ts); + if (rc < 0) { + LOGE("Unable to get elapsed realtime\n"); + goto err; + } + + /* calculate the elapsed time from boot time to reboot time */ + ts.tv_sec += secs; + ts.tv_nsec = 0; + + rc = ioctl(fd, ANDROID_ALARM_SET( + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP), &ts); + if (rc < 0) { + LOGE("Unable to set reboot time to %ld\n", secs); + goto err; + } + + do { + rc = ioctl(fd, ANDROID_ALARM_WAIT); + } while ((rc < 0 && errno == EINTR) || !alarm_is_alm_expired()); + + if (rc <= 0) { + LOGE("Unable to wait on alarm\n"); + goto err; + } + + close(fd); + return 0; + +err: + if (fd >= 0) + close(fd); + return -1; +} + +static void *alarm_thread(void *) +{ + time_t rtc_secs, rb_secs; + int rc; + + /* + * to support power off alarm, the time + * stored in alarm register at latest + * shutdown time should be some time + * earlier than the actual alarm time + * set by user + */ + rc = alarm_get_time(ALARM_TIME, &alm_secs); + if (rc < 0 || !alm_secs) + goto err; + + rc = alarm_get_time(RTC_TIME, &rtc_secs); + if (rc < 0) + goto err; + + /* + * calculate the reboot time after which + * the phone will reboot + */ + rb_secs = alm_secs - rtc_secs; + if (rb_secs <= 0) + goto err; + + rc = alarm_set_reboot_time_and_wait(rb_secs); + if (rc < 0) + goto err; + + LOGI("Exit from power off charging, reboot the phone!\n"); + android_reboot(ANDROID_RB_RESTART, 0, 0); + +err: + LOGE("Exit from alarm thread\n"); + return NULL; +} + +void healthd_board_init(struct healthd_config*) +{ + pthread_t tid; + int rc; + char value[PROP_VALUE_MAX]; + + property_get("ro.bootmode", value, ""); + if (!strcmp("charger", value)) { + rc = pthread_create(&tid, NULL, alarm_thread, NULL); + if (rc < 0) + LOGE("Create alarm thread failed\n"); + } +} + +int healthd_board_battery_update(struct android::BatteryProperties*) +{ + // return 0 to log periodic polled battery status to kernel log + return 1; +} + +void healthd_board_mode_charger_draw_battery(struct android::BatteryProperties*) +{ + +} + +void healthd_board_mode_charger_battery_update(struct android::BatteryProperties*) +{ + +} + +void healthd_board_mode_charger_set_backlight(bool) +{ + +} + +void healthd_board_mode_charger_init() +{ + +} diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp index 6800ad2..d6c31cf 100644 --- a/healthd/healthd_mode_charger.cpp +++ b/healthd/healthd_mode_charger.cpp @@ -65,13 +65,22 @@ char *locale; #define BATTERY_UNKNOWN_TIME (2 * MSEC_PER_SEC) #define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC) -#define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC) +#define UNPLUGGED_SHUTDOWN_TIME (5 * MSEC_PER_SEC) #define BATTERY_FULL_THRESH 95 #define LAST_KMSG_PATH "/proc/last_kmsg" #define LAST_KMSG_PSTORE_PATH "/sys/fs/pstore/console-ramoops" #define LAST_KMSG_MAX_SZ (32 * 1024) +#ifndef RED_LED_PATH +#define RED_LED_PATH "/sys/class/leds/red/brightness" +#endif +#ifndef GREEN_LED_PATH +#define GREEN_LED_PATH "/sys/class/leds/green/brightness" +#endif +#ifndef BLUE_LED_PATH +#define BLUE_LED_PATH "/sys/class/leds/blue/brightness" +#endif #define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0) #define LOGW(x...) do { KLOG_WARNING("charger", x); } while (0) @@ -147,7 +156,7 @@ static struct frame batt_anim_frames[] = { { .disp_time = 750, .min_capacity = 80, - .level_only = true, + .level_only = false, .surface = NULL, }, { @@ -168,6 +177,35 @@ static struct animation battery_animation = { .capacity = 0, }; +enum { + RED_LED = 0x01 << 0, + GREEN_LED = 0x01 << 1, + BLUE_LED = 0x01 << 2, +}; + +#ifndef NO_CHARGER_LED +struct led_ctl { + int color; + const char *path; +}; + +struct led_ctl leds[3] = + {{RED_LED, RED_LED_PATH}, + {GREEN_LED, GREEN_LED_PATH}, + {BLUE_LED, BLUE_LED_PATH}}; + +struct soc_led_color_mapping { + int soc; + int color; +}; + +struct soc_led_color_mapping soc_leds[3] = { + {15, RED_LED}, + {90, RED_LED | GREEN_LED}, + {100, GREEN_LED}, +}; +#endif + static struct charger charger_state; static struct healthd_config *healthd_config; static struct android::BatteryProperties *batt_prop; @@ -175,6 +213,55 @@ static int char_width; static int char_height; static bool minui_inited; +#ifndef NO_CHARGER_LED +static int set_tricolor_led(int on, int color) +{ + int fd, i; + char buffer[10]; + + for (i = 0; i < (int)ARRAY_SIZE(leds); i++) { + if ((color & leds[i].color) && (access(leds[i].path, R_OK | W_OK) == 0)) { + fd = open(leds[i].path, O_RDWR); + if (fd < 0) { + LOGE("Could not open led node %d\n", i); + continue; + } + if (on) + snprintf(buffer, sizeof(int), "%d\n", 255); + else + snprintf(buffer, sizeof(int), "%d\n", 0); + + if (write(fd, buffer, strlen(buffer)) < 0) + LOGE("Could not write to led node\n"); + if (fd >= 0) + close(fd); + } + } + + return 0; +} + +static int set_battery_soc_leds(int soc) +{ + int i, color; + static int old_color = 0; + + for (i = 0; i < (int)ARRAY_SIZE(soc_leds); i++) { + if (soc <= soc_leds[i].soc) + break; + } + color = soc_leds[i].color; + if (old_color != color) { + set_tricolor_led(0, old_color); + set_tricolor_led(1, color); + old_color = color; + LOGV("soc = %d, set led color 0x%x\n", soc, soc_leds[i].color); + } + + return 0; +} +#endif + /* current time in milliseconds */ static int64_t curr_time_ms(void) { @@ -314,6 +401,7 @@ static void draw_battery(struct charger *charger) batt_anim->cur_frame, frame->min_capacity, frame->disp_time); } + healthd_board_mode_charger_draw_battery(batt_prop); } static void redraw_screen(struct charger *charger) @@ -367,6 +455,7 @@ static void update_screen_state(struct charger *charger, int64_t now) gr_font_size(&char_width, &char_height); #ifndef CHARGER_DISABLE_INIT_BLANK + healthd_board_mode_charger_set_backlight(false); gr_fb_blank(true); #endif minui_inited = true; @@ -376,6 +465,7 @@ static void update_screen_state(struct charger *charger, int64_t now) if (batt_anim->cur_cycle == batt_anim->num_cycles) { reset_animation(batt_anim); charger->next_screen_transition = -1; + healthd_board_mode_charger_set_backlight(false); gr_fb_blank(true); LOGV("[%" PRId64 "] animation done\n", now); if (charger->charger_connected) @@ -406,9 +496,11 @@ static void update_screen_state(struct charger *charger, int64_t now) batt_anim->capacity = batt_prop->batteryLevel; } - /* unblank the screen on first cycle */ - if (batt_anim->cur_cycle == 0) + /* unblank the screen on first cycle */ + if (batt_anim->cur_cycle == 0) { gr_fb_blank(false); + healthd_board_mode_charger_set_backlight(true); + } /* draw the new frame (@ cur_frame) */ redraw_screen(charger); @@ -508,6 +600,7 @@ static void set_next_key_check(struct charger *charger, static void process_key(struct charger *charger, int code, int64_t now) { + struct animation *batt_anim = charger->batt_anim; struct key_state *key = &charger->keys[code]; if (code == KEY_POWER) { @@ -519,6 +612,8 @@ static void process_key(struct charger *charger, int code, int64_t now) accordingly. */ if (property_get_bool("ro.enable_boot_charger_mode", false)) { LOGW("[%" PRId64 "] booting from charger mode\n", now); + healthd_board_mode_charger_set_backlight(false); + gr_fb_blank(true); property_set("sys.boot_from_charger_mode", "1"); } else { if (charger->batt_anim->capacity >= charger->boot_min_cap) { @@ -534,19 +629,32 @@ static void process_key(struct charger *charger, int code, int64_t now) * make sure we wake up at the right-ish time to check */ set_next_key_check(charger, key, POWER_ON_KEY_TIME); - - /* Turn on the display and kick animation on power-key press - * rather than on key release - */ - kick_animation(charger->batt_anim); - request_suspend(false); } } else { - /* if the power key got released, force screen state cycle */ if (key->pending) { - kick_animation(charger->batt_anim); + /* If key is pressed when the animation is not running, kick + * the animation and quite suspend; If key is pressed when + * the animation is running, turn off the animation and request + * suspend. + */ + if (!batt_anim->run) { + kick_animation(batt_anim); + request_suspend(false); + } else { + reset_animation(batt_anim); + charger->next_screen_transition = -1; + healthd_board_mode_charger_set_backlight(false); + gr_fb_blank(true); + if (charger->charger_connected) + request_suspend(true); + } } } + } else { + if (key->pending) { + request_suspend(false); + kick_animation(charger->batt_anim); + } } key->pending = false; @@ -555,6 +663,7 @@ static void process_key(struct charger *charger, int code, int64_t now) static void handle_input_state(struct charger *charger, int64_t now) { process_key(charger, KEY_POWER, now); + process_key(charger, KEY_HOME, now); if (charger->next_key_check != -1 && now > charger->next_key_check) charger->next_key_check = -1; @@ -562,9 +671,27 @@ static void handle_input_state(struct charger *charger, int64_t now) static void handle_power_supply_state(struct charger *charger, int64_t now) { +#ifndef NO_CHARGER_LED + static int old_soc = 0; + int soc = 0; +#endif + if (!charger->have_battery_state) return; + healthd_board_mode_charger_battery_update(batt_prop); + +#ifndef NO_CHARGER_LED + if (batt_prop && batt_prop->batteryLevel >= 0) { + soc = batt_prop->batteryLevel; + } + + if (old_soc != soc) { + old_soc = soc; + set_battery_soc_leds(soc); + } +#endif + if (!charger->charger_connected) { /* Last cycle would have stopped at the extreme top of battery-icon @@ -683,6 +810,8 @@ void healthd_mode_charger_init(struct healthd_config* config) LOGW("--------------- STARTING CHARGER MODE ---------------\n"); + healthd_board_mode_charger_init(); + ret = ev_init(input_callback, charger); if (!ret) { epollfd = ev_get_epollfd(); diff --git a/include/cutils/android_reboot.h b/include/cutils/android_reboot.h index 85e1b7e..a3861a0 100644 --- a/include/cutils/android_reboot.h +++ b/include/cutils/android_reboot.h @@ -17,6 +17,8 @@ #ifndef __CUTILS_ANDROID_REBOOT_H__ #define __CUTILS_ANDROID_REBOOT_H__ +#include <mntent.h> + __BEGIN_DECLS /* Commands */ @@ -28,6 +30,9 @@ __BEGIN_DECLS #define ANDROID_RB_PROPERTY "sys.powerctl" int android_reboot(int cmd, int flags, const char *arg); +int android_reboot_with_callback( + int cmd, int flags, const char *arg, + void (*cb_on_remount)(const struct mntent*)); __END_DECLS diff --git a/include/cutils/iosched_policy.h b/include/cutils/iosched_policy.h index 07c5d1f..25b87ba 100644 --- a/include/cutils/iosched_policy.h +++ b/include/cutils/iosched_policy.h @@ -31,6 +31,8 @@ typedef enum { extern int android_set_ioprio(int pid, IoSchedClass clazz, int ioprio); extern int android_get_ioprio(int pid, IoSchedClass *clazz, int *ioprio); +extern int android_set_rt_ioprio(int pid, int rt); + #ifdef __cplusplus } #endif diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h index 2ed27dc..8b3390c 100644 --- a/include/private/android_filesystem_config.h +++ b/include/private/android_filesystem_config.h @@ -97,6 +97,12 @@ #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_QCOM_DIAG 3009 /* can read/write /dev/diag */ + +#define AID_SENSORS 3011 /* access to /dev/socket/sensor_ctl_socket & QCCI/QCSI */ + +#define AID_RFS 3012 /* Remote Filesystem for peripheral processors */ +#define AID_RFS_SHARED 3013 /* Shared files for Remote Filesystem for peripheral processors */ #define AID_EVERYBODY 9997 /* shared between all apps in the same profile */ #define AID_MISC 9998 /* access to misc storage */ @@ -172,6 +178,7 @@ static const struct android_id_info android_ids[] = { { "shell", AID_SHELL, }, { "cache", AID_CACHE, }, { "diag", AID_DIAG, }, + { "qcom_diag", AID_QCOM_DIAG, }, { "net_bt_admin", AID_NET_BT_ADMIN, }, { "net_bt", AID_NET_BT, }, @@ -182,6 +189,11 @@ static const struct android_id_info android_ids[] = { { "net_bw_acct", AID_NET_BW_ACCT, }, { "net_bt_stack", AID_NET_BT_STACK, }, + { "sensors", AID_SENSORS, }, + + { "rfs", AID_RFS, }, + { "rfs_shared", AID_RFS_SHARED, }, + { "everybody", AID_EVERYBODY, }, { "misc", AID_MISC, }, { "nobody", AID_NOBODY, }, diff --git a/include/system/camera.h b/include/system/camera.h index 5d0873a..0570ca0 100644 --- a/include/system/camera.h +++ b/include/system/camera.h @@ -88,9 +88,20 @@ enum { // Notify on autofocus start and stop. This is useful in continuous // autofocus - FOCUS_MODE_CONTINUOUS_VIDEO and FOCUS_MODE_CONTINUOUS_PICTURE. CAMERA_MSG_FOCUS_MOVE = 0x0800, // notifyCallback + CAMERA_MSG_VENDOR_START = 0x1000, + CAMERA_MSG_STATS_DATA = CAMERA_MSG_VENDOR_START, + CAMERA_MSG_META_DATA = 0x2000, + CAMERA_MSG_VENDOR_END = 0x8000, CAMERA_MSG_ALL_MSGS = 0xFFFF }; +/** meta data type in CameraMetaDataCallback */ +enum { + CAMERA_META_DATA_ASD = 0x001, //ASD data + CAMERA_META_DATA_FD = 0x002, //FD/FP data + CAMERA_META_DATA_HDR = 0x003, //Auto HDR data +}; + /** cmdType in sendCommand functions */ enum { CAMERA_CMD_START_SMOOTH_ZOOM = 1, @@ -189,7 +200,41 @@ enum { * IMPLEMENTATION_DEFINED, then HALv3 devices will use gralloc usage flags * of SW_READ_OFTEN. */ - CAMERA_CMD_SET_VIDEO_FORMAT = 11 +#ifndef CAMERA_VENDOR_L_COMPAT + CAMERA_CMD_SET_VIDEO_FORMAT = 11, + + CAMERA_CMD_VENDOR_START = 20, + /** + * Commands to enable/disable preview histogram + * + * Based on user's input to enable/disable histogram from the camera + * UI, send the appropriate command to the HAL to turn on/off the histogram + * stats and start sending the data to the application. + */ + CAMERA_CMD_HISTOGRAM_ON = CAMERA_CMD_VENDOR_START, + CAMERA_CMD_HISTOGRAM_OFF = CAMERA_CMD_VENDOR_START + 1, + CAMERA_CMD_HISTOGRAM_SEND_DATA = CAMERA_CMD_VENDOR_START + 2, + CAMERA_CMD_LONGSHOT_ON = CAMERA_CMD_VENDOR_START + 3, + CAMERA_CMD_LONGSHOT_OFF = CAMERA_CMD_VENDOR_START + 4, + CAMERA_CMD_STOP_LONGSHOT = CAMERA_CMD_VENDOR_START + 5, + CAMERA_CMD_METADATA_ON = CAMERA_CMD_VENDOR_START + 6, + CAMERA_CMD_METADATA_OFF = CAMERA_CMD_VENDOR_START + 7, + CAMERA_CMD_VENDOR_END = 200, +#else + + /** + * Values used by older HALs, provided as an option for compatibility + */ + CAMERA_CMD_HISTOGRAM_ON = 11, + CAMERA_CMD_HISTOGRAM_OFF = 12, + CAMERA_CMD_HISTOGRAM_SEND_DATA = 13, + CAMERA_CMD_LONGSHOT_ON = 14, + CAMERA_CMD_LONGSHOT_OFF = 15, + CAMERA_CMD_STOP_LONGSHOT = 16, + CAMERA_CMD_METADATA_ON = 100, + CAMERA_CMD_METADATA_OFF = 101, + CAMERA_CMD_SET_VIDEO_FORMAT = 102, +#endif }; /** camera fatal errors */ @@ -275,10 +320,32 @@ typedef struct camera_face { * -2000, -2000 if this is not supported. */ int32_t mouth[2]; + int32_t smile_degree; + int32_t smile_score; + int32_t blink_detected; + int32_t face_recognised; + int32_t gaze_angle; + int32_t updown_dir; + int32_t leftright_dir; + int32_t roll_dir; + int32_t left_right_gaze; + int32_t top_bottom_gaze; + int32_t leye_blink; + int32_t reye_blink; } camera_face_t; /** + * The information of a data type received in a camera frame. + */ +typedef enum { + /** Data buffer */ + CAMERA_FRAME_DATA_BUF = 0x000, + /** File descriptor */ + CAMERA_FRAME_DATA_FD = 0x100 +} camera_frame_data_type_t; + +/** * The metadata of the frame data. */ typedef struct camera_frame_metadata { diff --git a/init/Android.mk b/init/Android.mk index de065dc..85dfbfc 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -48,9 +48,33 @@ LOCAL_SRC_FILES:= \ ueventd.cpp \ ueventd_parser.cpp \ watchdogd.cpp \ + vendor_init.cpp + +SYSTEM_CORE_INIT_DEFINES := BOARD_CHARGING_MODE_BOOTING_LPM \ + BOARD_CHARGING_CMDLINE_NAME \ + BOARD_CHARGING_CMDLINE_VALUE + +$(foreach system_core_init_define,$(SYSTEM_CORE_INIT_DEFINES), \ + $(if $($(system_core_init_define)), \ + $(eval LOCAL_CFLAGS += -D$(system_core_init_define)=\"$($(system_core_init_define))\") \ + ) \ +) + +ifneq ($(TARGET_IGNORE_RO_BOOT_SERIALNO),) +LOCAL_CFLAGS += -DIGNORE_RO_BOOT_SERIALNO +endif + +ifneq ($(TARGET_IGNORE_RO_BOOT_REVISION),) +LOCAL_CFLAGS += -DIGNORE_RO_BOOT_REVISION +endif + +ifneq ($(TARGET_INIT_UMOUNT_AND_FSCK_IS_UNSAFE),) +LOCAL_CFLAGS += -DUMOUNT_AND_FSCK_IS_UNSAFE +endif LOCAL_MODULE:= init LOCAL_C_INCLUDES += \ + external/zlib \ system/extras/ext4_utils \ system/core/mkbootimg @@ -65,12 +89,14 @@ LOCAL_STATIC_LIBRARIES := \ liblogwrap \ libcutils \ libbase \ - libext4_utils_static \ libutils \ liblog \ libc \ libselinux \ libmincrypt \ + libext4_utils_static \ + libext2_blkid \ + libext2_uuid_static \ libc++_static \ libdl \ libsparse_static \ @@ -82,6 +108,11 @@ LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \ ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd LOCAL_CLANG := $(init_clang) + +ifneq ($(strip $(TARGET_INIT_VENDOR_LIB)),) +LOCAL_WHOLE_STATIC_LIBRARIES += $(TARGET_INIT_VENDOR_LIB) +endif + include $(BUILD_EXECUTABLE) diff --git a/init/NOTICE b/init/NOTICE index c5b1efa..383d0f5 100644 --- a/init/NOTICE +++ b/init/NOTICE @@ -188,3 +188,29 @@ END OF TERMS AND CONDITIONS +Copyright (c) 2013, The Linux Foundation. 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. + * Neither the name of The Linux Foundation 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 "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +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. diff --git a/init/builtins.cpp b/init/builtins.cpp index 8eb5b5b..1681d17 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -16,7 +16,9 @@ #include <errno.h> #include <fcntl.h> +#include <mntent.h> #include <net/if.h> +#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -38,6 +40,7 @@ #include <base/stringprintf.h> #include <cutils/partition_utils.h> #include <cutils/android_reboot.h> +#include <logwrap/logwrap.h> #include <private/android_filesystem_config.h> #include "init.h" @@ -49,6 +52,8 @@ #include "log.h" #define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW +#define UNMOUNT_CHECK_MS 5000 +#define UNMOUNT_CHECK_TIMES 10 int add_environment(const char *name, const char *value); @@ -109,25 +114,100 @@ static void service_start_if_not_disabled(struct service *svc) } } +static void unmount_and_fsck(const struct mntent *entry) +{ +#ifndef UMOUNT_AND_FSCK_IS_UNSAFE + if (strcmp(entry->mnt_type, "f2fs") && strcmp(entry->mnt_type, "ext4")) + return; + + /* First, lazily unmount the directory. This unmount request finishes when + * all processes that open a file or directory in |entry->mnt_dir| exit. + */ + TEMP_FAILURE_RETRY(umount2(entry->mnt_dir, MNT_DETACH)); + + /* Next, kill all processes except init, kthreadd, and kthreadd's + * children to finish the lazy unmount. Killing all processes here is okay + * because this callback function is only called right before reboot(). + * It might be cleaner to selectively kill processes that actually use + * |entry->mnt_dir| rather than killing all, probably by reusing a function + * like killProcessesWithOpenFiles() in vold/, but the selinux policy does + * not allow init to scan /proc/<pid> files which the utility function + * heavily relies on. The policy does not allow the process to execute + * killall/pkill binaries either. Note that some processes might + * automatically restart after kill(), but that is not really a problem + * because |entry->mnt_dir| is no longer visible to such new processes. + */ + service_for_each(service_stop); + TEMP_FAILURE_RETRY(kill(-1, SIGKILL)); + + int count = 0; + while (count++ < UNMOUNT_CHECK_TIMES) { + int fd = TEMP_FAILURE_RETRY(open(entry->mnt_fsname, O_RDONLY | O_EXCL)); + if (fd >= 0) { + /* |entry->mnt_dir| has sucessfully been unmounted. */ + close(fd); + break; + } else if (errno == EBUSY) { + /* Some processes using |entry->mnt_dir| are still alive. Wait for a + * while then retry. + */ + TEMP_FAILURE_RETRY( + usleep(UNMOUNT_CHECK_MS * 1000 / UNMOUNT_CHECK_TIMES)); + continue; + } else { + /* Cannot open the device. Give up. */ + return; + } + } + + int st; + if (!strcmp(entry->mnt_type, "f2fs")) { + const char *f2fs_argv[] = { + "/system/bin/fsck.f2fs", "-f", entry->mnt_fsname, + }; + android_fork_execvp_ext(ARRAY_SIZE(f2fs_argv), (char **)f2fs_argv, + &st, true, LOG_KLOG, true, NULL); + } else if (!strcmp(entry->mnt_type, "ext4")) { + const char *ext4_argv[] = { + "/system/bin/e2fsck", "-f", "-y", entry->mnt_fsname, + }; + android_fork_execvp_ext(ARRAY_SIZE(ext4_argv), (char **)ext4_argv, + &st, true, LOG_KLOG, true, NULL); + } +#endif +} + int do_class_start(int nargs, char **args) { - /* Starting a class does not start services - * which are explicitly disabled. They must - * be started individually. - */ + char prop[PROP_NAME_MAX]; + snprintf(prop, PROP_NAME_MAX, "class_start:%s", args[1]); + + /* Starting a class does not start services + * which are explicitly disabled. They must + * be started individually. + */ service_for_each_class(args[1], service_start_if_not_disabled); + action_for_each_trigger(prop, action_add_queue_tail); return 0; } int do_class_stop(int nargs, char **args) { + char prop[PROP_NAME_MAX]; + snprintf(prop, PROP_NAME_MAX, "class_stop:%s", args[1]); + service_for_each_class(args[1], service_stop); + action_for_each_trigger(prop, action_add_queue_tail); return 0; } int do_class_reset(int nargs, char **args) { + char prop[PROP_NAME_MAX]; + snprintf(prop, PROP_NAME_MAX, "class_reset:%s", args[1]); + service_for_each_class(args[1], service_reset); + action_for_each_trigger(prop, action_add_queue_tail); return 0; } @@ -401,6 +481,7 @@ int do_mount_all(int nargs, char **args) int ret = -1; int child_ret = -1; int status; + char boot_mode[PROP_VALUE_MAX] = {0}; struct fstab *fstab; if (nargs != 2) { @@ -450,10 +531,13 @@ int do_mount_all(int nargs, char **args) property_set("vold.decrypt", "trigger_default_encryption"); } else if (ret == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) { property_set("ro.crypto.state", "unencrypted"); - /* If fs_mgr determined this is an unencrypted device, then trigger - * that action. + /* If fs_mgr determined this is an unencrypted device and we are + * not booting into ffbm(fast factory boot mode),then trigger + * that action. */ - action_for_each_trigger("nonencrypted", action_add_queue_tail); + property_get("ro.bootmode", boot_mode); + if (strncmp(boot_mode, "ffbm", 4)) + action_for_each_trigger("nonencrypted", action_add_queue_tail); } else if (ret == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) { /* Setup a wipe via recovery, and reboot into recovery */ ERROR("fs_mgr_mount_all suggested recovery, so wiping data via recovery.\n"); @@ -468,7 +552,9 @@ int do_mount_all(int nargs, char **args) // Although encrypted, we have device key, so we do not need to // do anything different from the nonencrypted case. - action_for_each_trigger("nonencrypted", action_add_queue_tail); + property_get("ro.bootmode", boot_mode); + if (strncmp(boot_mode, "ffbm", 4)) + action_for_each_trigger("nonencrypted", action_add_queue_tail); } else if (ret == FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED) { if (e4crypt_install_keyring()) { return -1; @@ -559,6 +645,7 @@ int do_powerctl(int nargs, char **args) int len = 0; int cmd = 0; const char *reboot_target; + void (*callback_on_ro_remount)(const struct mntent*) = NULL; res = expand_props(command, args[1], sizeof(command)); if (res) { @@ -567,8 +654,12 @@ int do_powerctl(int nargs, char **args) } if (strncmp(command, "shutdown", 8) == 0) { + if (property_get_bool("init.shutdown_to_charging", false)) { + return android_reboot(ANDROID_RB_RESTART2, 0, "charging"); + } cmd = ANDROID_RB_POWEROFF; len = 8; + callback_on_ro_remount = unmount_and_fsck; } else if (strncmp(command, "reboot", 6) == 0) { cmd = ANDROID_RB_RESTART2; len = 6; @@ -578,7 +669,14 @@ int do_powerctl(int nargs, char **args) } if (command[len] == ',') { + char prop_value[PROP_VALUE_MAX] = {0}; reboot_target = &command[len + 1]; + + if ((property_get("init.svc.recovery", prop_value) == 0) && + (strncmp(reboot_target, "keys", 4) == 0)) { + ERROR("powerctl: permission denied\n"); + return -EINVAL; + } } else if (command[len] == '\0') { reboot_target = ""; } else { @@ -586,7 +684,8 @@ int do_powerctl(int nargs, char **args) return -EINVAL; } - return android_reboot(cmd, 0, reboot_target); + return android_reboot_with_callback(cmd, 0, reboot_target, + callback_on_ro_remount); } int do_trigger(int nargs, char **args) diff --git a/init/devices.cpp b/init/devices.cpp index 4944cec..bb54cca 100644 --- a/init/devices.cpp +++ b/init/devices.cpp @@ -47,6 +47,8 @@ #include "ueventd_parser.h" #include "util.h" #include "log.h" +#include "property_service.h" +#include <zlib.h> #define SYSFS_PREFIX "/sys" static const char *firmware_dirs[] = { "/etc/firmware", @@ -55,6 +57,8 @@ static const char *firmware_dirs[] = { "/etc/firmware", extern struct selabel_handle *sehandle; +extern char boot_device[PROP_VALUE_MAX]; + static int device_fd = -1; struct uevent { @@ -323,6 +327,35 @@ static void remove_platform_device(const char *path) } } +/* Given a path that may start with an MTD device (/devices/virtual/mtd/mtd8/mtdblock8), + * populate the supplied buffer with the MTD partition number and return 0. + * If it doesn't start with an MTD device, or there is some error, return -1 */ +static int find_mtd_device_prefix(const char *path, char *buf, ssize_t buf_sz) +{ + const char *start, *end; + + if (strncmp(path, "/devices/virtual/mtd", 20)) + return -1; + + /* Beginning of the prefix is the initial "mtdXX" after "/devices/virtual/mtd/" */ + start = path + 21; + + /* End of the prefix is one path '/' later, capturing the partition number + * Example: mtd8 */ + end = strchr(start, '/'); + if (!end) { + return -1; + } + + /* Make sure we have enough room for the string plus null terminator */ + if (end - start + 1 > buf_sz) + return -1; + + strncpy(buf, start, end - start); + buf[end - start] = '\0'; + return 0; +} + /* Given a path that may start with a PCI device, populate the supplied buffer * with the PCI domain/bus number and the peripheral ID and return 0. * If it doesn't start with a PCI device, or there is some error, return -1 */ @@ -472,6 +505,11 @@ static char **get_block_device_symlinks(struct uevent *uevent) char link_path[256]; int link_num = 0; char *p; + int is_bootdevice = -1; + int mtd_fd = -1; + int nr; + char mtd_name_path[256]; + char mtd_name[64]; pdev = find_platform_device(uevent->path); if (pdev) { @@ -480,19 +518,52 @@ static char **get_block_device_symlinks(struct uevent *uevent) } else if (!find_pci_device_prefix(uevent->path, buf, sizeof(buf))) { device = buf; type = "pci"; + } else if (!find_mtd_device_prefix(uevent->path, buf, sizeof(buf))) { + device = buf; + type = "mtd"; } else { return NULL; } - char **links = (char**) malloc(sizeof(char *) * 4); + char **links = (char**) malloc(sizeof(char *) * 6); if (!links) return NULL; - memset(links, 0, sizeof(char *) * 4); + memset(links, 0, sizeof(char *) * 6); INFO("found %s device %s\n", type, device); snprintf(link_path, sizeof(link_path), "/dev/block/%s/%s", type, device); + if(!strcmp(type, "mtd")) { + snprintf(mtd_name_path, sizeof(mtd_name_path), + "/sys/devices/virtual/%s/%s/name", type, device); + mtd_fd = open(mtd_name_path, O_RDONLY); + if(mtd_fd < 0) { + ERROR("Unable to open %s for reading", mtd_name_path); + return NULL; + } + nr = read(mtd_fd, mtd_name, sizeof(mtd_name) - 1); + if (nr <= 0) + return NULL; + close(mtd_fd); + mtd_name[nr - 1] = '\0'; + + p = strdup(mtd_name); + sanitize(p); + if (asprintf(&links[link_num], "/dev/block/%s/by-name/%s", type, p) > 0) + link_num++; + else + links[link_num] = NULL; + free(p); + } + + if (pdev && boot_device[0] != '\0' && strstr(device, boot_device)) { + make_link_init(link_path, "/dev/block/bootdevice"); + is_bootdevice = 1; + } else { + is_bootdevice = 0; + } + if (uevent->partition_name) { p = strdup(uevent->partition_name); sanitize(p); @@ -502,6 +573,13 @@ static char **get_block_device_symlinks(struct uevent *uevent) link_num++; else links[link_num] = NULL; + + if (is_bootdevice > 0) { + if (asprintf(&links[link_num], "/dev/block/bootdevice/by-name/%s", p) > 0) + link_num++; + else + links[link_num] = NULL; + } free(p); } @@ -510,6 +588,13 @@ static char **get_block_device_symlinks(struct uevent *uevent) link_num++; else links[link_num] = NULL; + + if (is_bootdevice > 0) { + if (asprintf(&links[link_num], "/dev/block/bootdevice/by-num/p%d", uevent->partition_num) > 0) + link_num++; + else + links[link_num] = NULL; + } } slash = strrchr(uevent->path, '/'); @@ -748,23 +833,20 @@ static void handle_device_event(struct uevent *uevent) } } -static int load_firmware(int fw_fd, int loading_fd, int data_fd) +static int load_firmware(int fw_fd, gzFile gz_fd, int loading_fd, int data_fd) { - struct stat st; - long len_to_copy; int ret = 0; - if(fstat(fw_fd, &st) < 0) - return -1; - len_to_copy = st.st_size; - write(loading_fd, "1", 1); /* start transfer */ - while (len_to_copy > 0) { + while (1) { char buf[PAGE_SIZE]; ssize_t nr; - nr = read(fw_fd, buf, sizeof(buf)); + if (gz_fd) + nr = gzread(gz_fd, buf, sizeof(buf)); + else + nr = read(fw_fd, buf, sizeof(buf)); if(!nr) break; if(nr < 0) { @@ -772,7 +854,6 @@ static int load_firmware(int fw_fd, int loading_fd, int data_fd) break; } - len_to_copy -= nr; while (nr > 0) { ssize_t nw = 0; @@ -788,8 +869,10 @@ static int load_firmware(int fw_fd, int loading_fd, int data_fd) out: if(!ret) write(loading_fd, "0", 1); /* successful end of transfer */ - else + else { + ERROR("%s: aborted transfer\n", __func__); write(loading_fd, "-1", 2); /* abort transfer */ + } return ret; } @@ -799,12 +882,29 @@ static int is_booting(void) return access("/dev/.booting", F_OK) == 0; } +gzFile fw_gzopen(const char *fname, const char *mode) +{ + char *file1 = NULL; + int l; + gzFile gz_fd = NULL; + + l = asprintf(&file1, "%s.gz", fname); + if (l == -1) + goto out; + + gz_fd = gzopen(file1, mode); + free(file1); +out: + return gz_fd; +} + static void process_firmware_event(struct uevent *uevent) { char *root, *loading, *data; int l, loading_fd, data_fd, fw_fd; size_t i; int booting = is_booting(); + gzFile gz_fd = NULL; INFO("firmware: loading '%s' for '%s'\n", uevent->firmware, uevent->path); @@ -837,15 +937,18 @@ try_loading_again: goto data_free_out; fw_fd = open(file, O_RDONLY|O_CLOEXEC); free(file); - if (fw_fd >= 0) { - if(!load_firmware(fw_fd, loading_fd, data_fd)) + if (fw_fd < 0){ + gz_fd = fw_gzopen(file, "rb"); + } + if (fw_fd >= 0 || gz_fd) { + if(!load_firmware(fw_fd, gz_fd, loading_fd, data_fd)) INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware); else INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware); break; } } - if (fw_fd < 0) { + if ((fw_fd < 0) && !gz_fd) { if (booting) { /* If we're not fully booted, we may be missing * filesystems needed for firmware, wait and retry. @@ -859,7 +962,10 @@ try_loading_again: goto data_close_out; } - close(fw_fd); + if (gz_fd) + gzclose(gz_fd); + else + close(fw_fd); data_close_out: close(data_fd); loading_close_out: diff --git a/init/init.cpp b/init/init.cpp index 93fe944..58d7d34 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -63,13 +63,20 @@ #include "util.h" #include "ueventd.h" #include "watchdogd.h" +#include "vendor_init.h" struct selabel_handle *sehandle; struct selabel_handle *sehandle_prop; static int property_triggers_enabled = 0; +#ifndef BOARD_CHARGING_CMDLINE_NAME +#define BOARD_CHARGING_CMDLINE_NAME "androidboot.battchg_pause" +#define BOARD_CHARGING_CMDLINE_VALUE "true" +#endif + static char qemu[32]; +static char battchg_pause[32]; static struct action *cur_action = NULL; static struct command *cur_command = NULL; @@ -529,6 +536,9 @@ static void msg_restart(const char *name) void handle_control_message(const char *msg, const char *arg) { + if (!vendor_handle_control_message(msg, arg)) + return; + if (!strcmp(msg,"start")) { msg_start(arg); } else if (!strcmp(msg,"stop")) { @@ -632,7 +642,7 @@ static int wait_for_coldboot_done_action(int nargs, char **args) { // Any longer than 1s is an unreasonable length of time to delay booting. // If you're hitting this timeout, check that you didn't make your // sepolicy regular expressions too expensive (http://b/19899875). - if (wait_for_file(COLDBOOT_DONE, 1)) { + if (wait_for_file(COLDBOOT_DONE, 5)) { ERROR("Timed out waiting for %s\n", COLDBOOT_DONE); } @@ -782,6 +792,8 @@ static void import_kernel_nv(char *name, bool for_emulator) if (!strcmp(name,"qemu")) { strlcpy(qemu, value, sizeof(qemu)); + } else if (!strcmp(name,BOARD_CHARGING_CMDLINE_NAME)) { + strlcpy(battchg_pause, value, sizeof(battchg_pause)); } else if (!strncmp(name, "androidboot.", 12) && name_len > 12) { const char *boot_prop_name = name + 12; char prop[PROP_NAME_MAX]; @@ -799,12 +811,16 @@ static void export_kernel_boot_props() { const char *dst_prop; const char *default_value; } prop_map[] = { +#ifndef IGNORE_RO_BOOT_SERIALNO { "ro.boot.serialno", "ro.serialno", "", }, +#endif { "ro.boot.mode", "ro.bootmode", "unknown", }, { "ro.boot.baseband", "ro.baseband", "unknown", }, { "ro.boot.bootloader", "ro.bootloader", "unknown", }, { "ro.boot.hardware", "ro.hardware", "unknown", }, +#ifndef IGNORE_RO_BOOT_REVISION { "ro.boot.revision", "ro.revision", "0", }, +#endif }; for (size_t i = 0; i < ARRAY_SIZE(prop_map); i++) { char value[PROP_VALUE_MAX]; @@ -986,6 +1002,24 @@ static void selinux_initialize(bool in_kernel_domain) { } } +static int charging_mode_booting(void) { +#ifndef BOARD_CHARGING_MODE_BOOTING_LPM + return 0; +#else + int f; + char cmb; + f = open(BOARD_CHARGING_MODE_BOOTING_LPM, O_RDONLY); + if (f < 0) + return 0; + + if (1 != read(f, (void *)&cmb,1)) + return 0; + + close(f); + return ('1' == cmb); +#endif +} + int main(int argc, char** argv) { if (!strcmp(basename(argv[0]), "ueventd")) { return ueventd_main(argc, argv); @@ -1097,8 +1131,13 @@ int main(int argc, char** argv) { // Don't mount filesystems or start core system services in charger mode. char bootmode[PROP_VALUE_MAX]; - if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) { + if (((property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) || + strcmp(battchg_pause, BOARD_CHARGING_CMDLINE_VALUE) == 0) + || charging_mode_booting()) { action_for_each_trigger("charger", action_add_queue_tail); + } else if (strncmp(bootmode, "ffbm", 4) == 0) { + KLOG_ERROR("Booting into ffbm mode\n"); + action_for_each_trigger("ffbm", action_add_queue_tail); } else { action_for_each_trigger("late-init", action_add_queue_tail); } diff --git a/init/init_parser.cpp b/init/init_parser.cpp index 9bab67d..c36d36e 100644 --- a/init/init_parser.cpp +++ b/init/init_parser.cpp @@ -44,7 +44,7 @@ struct import { const char *filename; }; -static void *parse_service(struct parse_state *state, int nargs, char **args); +static void *parse_service(struct parse_state *state, int nargs, char **args, bool redefine); static void parse_line_service(struct parse_state *state, int nargs, char **args); static void *parse_action(struct parse_state *state, int nargs, char **args); @@ -184,6 +184,7 @@ static int lookup_keyword(const char *s) case 's': if (!strcmp(s, "eclabel")) return K_seclabel; if (!strcmp(s, "ervice")) return K_service; + if (!strcmp(s, "ervice_redefine")) return K_service_redefine; if (!strcmp(s, "etenv")) return K_setenv; if (!strcmp(s, "etprop")) return K_setprop; if (!strcmp(s, "etrlimit")) return K_setrlimit; @@ -362,7 +363,8 @@ static void parse_new_section(struct parse_state *state, int kw, nargs > 1 ? args[1] : ""); switch(kw) { case K_service: - state->context = parse_service(state, nargs, args); + case K_service_redefine: + state->context = parse_service(state, nargs, args, (kw == K_service_redefine)); if (state->context) { state->parse_line = parse_line_service; return; @@ -725,7 +727,7 @@ service* make_exec_oneshot_service(int nargs, char** args) { return svc; } -static void *parse_service(struct parse_state *state, int nargs, char **args) +static void *parse_service(struct parse_state *state, int nargs, char **args, bool redefine) { if (nargs < 3) { parse_error(state, "services must have a name and a program\n"); @@ -737,13 +739,18 @@ static void *parse_service(struct parse_state *state, int nargs, char **args) } service* svc = (service*) service_find_by_name(args[1]); - if (svc) { + if (svc && !redefine) { parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]); return 0; } nargs -= 2; - svc = (service*) calloc(1, sizeof(*svc) + sizeof(char*) * nargs); + + if (!svc) { + svc = (service*) calloc(1, sizeof(*svc) + sizeof(char*) * nargs); + redefine = false; + } + if (!svc) { parse_error(state, "out of memory\n"); return 0; @@ -758,7 +765,8 @@ static void *parse_service(struct parse_state *state, int nargs, char **args) cur_trigger->name = "onrestart"; list_add_tail(&svc->onrestart.triggers, &cur_trigger->nlist); list_init(&svc->onrestart.commands); - list_add_tail(&service_list, &svc->slist); + if (!redefine) + list_add_tail(&service_list, &svc->slist); return svc; } diff --git a/init/keywords.h b/init/keywords.h index 0910f60..303685d 100644 --- a/init/keywords.h +++ b/init/keywords.h @@ -84,6 +84,7 @@ enum { KEYWORD(rmdir, COMMAND, 1, do_rmdir) KEYWORD(seclabel, OPTION, 0, 0) KEYWORD(service, SECTION, 0, 0) + KEYWORD(service_redefine, SECTION, 0, 0) KEYWORD(setenv, OPTION, 2, 0) KEYWORD(setprop, COMMAND, 2, do_setprop) KEYWORD(setrlimit, COMMAND, 3, do_setrlimit) diff --git a/init/property_service.cpp b/init/property_service.cpp index 52f6b98..fe82bef 100644 --- a/init/property_service.cpp +++ b/init/property_service.cpp @@ -54,6 +54,7 @@ #include "init.h" #include "util.h" #include "log.h" +#include "vendor_init.h" #define PERSISTENT_PROPERTY_DIR "/data/property" #define FSTAB_PREFIX "/fstab." @@ -149,6 +150,33 @@ int __property_get(const char *name, char *value) return __system_property_get(name, value); } +bool property_get_bool(const char *key, bool default_value) { + if (!key) { + return default_value; + } + + bool result = default_value; + char buf[PROP_VALUE_MAX] = {'\0',}; + + int len = __property_get(key, buf); + if (len == 1) { + char ch = buf[0]; + if (ch == '0' || ch == 'n') { + result = false; + } else if (ch == '1' || ch == 'y') { + result = true; + } + } else if (len > 1) { + if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) { + result = false; + } else if (!strcmp(buf, "yes") || !strcmp(buf, "true") || !strcmp(buf, "on")) { + result = true; + } + } + + return result; +} + static void write_persistent_property(const char *name, const char *value) { char tempPath[PATH_MAX]; @@ -518,6 +546,11 @@ void load_persist_props(void) { load_override_properties(); /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); + + /* update with vendor-specific property runtime + * overrides + */ + vendor_load_properties(); } void load_recovery_id_prop() { @@ -564,6 +597,7 @@ 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_recovery_id_prop(); } diff --git a/init/property_service.h b/init/property_service.h index 303f251..6b542b5 100644 --- a/init/property_service.h +++ b/init/property_service.h @@ -28,6 +28,7 @@ extern void start_property_service(void); void get_property_workspace(int *fd, int *sz); extern int __property_get(const char *name, char *value); extern int property_set(const char *name, const char *value); +extern bool property_get_bool(const char *name, bool def_value); extern bool properties_initialized(); #ifndef __clang__ diff --git a/init/ueventd.cpp b/init/ueventd.cpp index c63fdaa..2dd8b01 100644 --- a/init/ueventd.cpp +++ b/init/ueventd.cpp @@ -33,6 +33,8 @@ #include "ueventd_parser.h" #include "property_service.h" +char boot_device[PROP_VALUE_MAX]; + int ueventd_main(int argc, char **argv) { /* @@ -65,6 +67,8 @@ int ueventd_main(int argc, char **argv) ueventd_parse_config_file("/ueventd.rc"); ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware).c_str()); + property_get("ro.boot.bootdevice", boot_device); + device_init(); pollfd ufd; diff --git a/init/util.cpp b/init/util.cpp index a5392c6..b006e0b 100644 --- a/init/util.cpp +++ b/init/util.cpp @@ -458,9 +458,13 @@ int restorecon(const char* pathname) return selinux_android_restorecon(pathname, 0); } +#define RESTORECON_RECURSIVE_FLAGS \ + (SELINUX_ANDROID_RESTORECON_FORCE | \ + SELINUX_ANDROID_RESTORECON_RECURSE) + int restorecon_recursive(const char* pathname) { - return selinux_android_restorecon(pathname, SELINUX_ANDROID_RESTORECON_RECURSE); + return selinux_android_restorecon(pathname, RESTORECON_RECURSIVE_FLAGS); } /* diff --git a/init/vendor_init.cpp b/init/vendor_init.cpp new file mode 100644 index 0000000..d2964ad --- /dev/null +++ b/init/vendor_init.cpp @@ -0,0 +1,44 @@ +/* +Copyright (c) 2013, The Linux Foundation. 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. + * Neither the name of The Linux Foundation 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 "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +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 "vendor_init.h" +#include <errno.h> + +/* init vendor override stubs */ + +__attribute__ ((weak)) +void vendor_load_properties() +{ +} + +__attribute__ ((weak)) +int vendor_handle_control_message(const char *msg, const char *arg) +{ + return -ENOSYS; +} diff --git a/init/vendor_init.h b/init/vendor_init.h new file mode 100644 index 0000000..efa4eea --- /dev/null +++ b/init/vendor_init.h @@ -0,0 +1,34 @@ +/* +Copyright (c) 2013, The Linux Foundation. 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. + * Neither the name of The Linux Foundation 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 "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +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. + */ + +#ifndef __INIT_VENDOR__H__ +#define __INIT_VENDOR__H__ +extern void vendor_load_properties(void); +extern int vendor_handle_control_message(const char *msg, const char *arg); +#endif /* __INIT_VENDOR__H__ */ diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c index 6ae23c1..af7e189 100644 --- a/libcutils/android_reboot.c +++ b/libcutils/android_reboot.c @@ -14,43 +14,108 @@ * limitations under the License. */ -#include <unistd.h> -#include <sys/reboot.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <sys/stat.h> +#include <errno.h> #include <fcntl.h> #include <mntent.h> +#include <stdbool.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> +#include <sys/cdefs.h> +#include <sys/mount.h> +#include <sys/reboot.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <unistd.h> #include <cutils/android_reboot.h> +#include <cutils/klog.h> +#include <cutils/list.h> -#define UNUSED __attribute__((unused)) +#define TAG "android_reboot" +#define READONLY_CHECK_MS 5000 +#define READONLY_CHECK_TIMES 50 -/* Check to see if /proc/mounts contains any writeable filesystems - * backed by a block device. - * Return true if none found, else return false. +typedef struct { + struct listnode list; + struct mntent entry; +} mntent_list; + +static bool has_mount_option(const char* opts, const char* opt_to_find) +{ + bool ret = false; + char* copy = NULL; + char* opt; + char* rem; + + while ((opt = strtok_r(copy ? NULL : (copy = strdup(opts)), ",", &rem))) { + if (!strcmp(opt, opt_to_find)) { + ret = true; + break; + } + } + + free(copy); + return ret; +} + +static bool is_block_device(const char* fsname) +{ + return !strncmp(fsname, "/dev/block", 10); +} + +/* Find all read+write block devices in /proc/mounts and add them to + * |rw_entries|. */ -static int remount_ro_done(void) +static void find_rw(struct listnode* rw_entries) { FILE* fp; struct mntent* mentry; - int found_rw_fs = 0; if ((fp = setmntent("/proc/mounts", "r")) == NULL) { - /* If we can't read /proc/mounts, just give up. */ - return 1; + KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n"); + return; } while ((mentry = getmntent(fp)) != NULL) { - if (!strncmp(mentry->mnt_fsname, "/dev/block", 10) && strstr(mentry->mnt_opts, "rw,")) { - found_rw_fs = 1; - break; + if (is_block_device(mentry->mnt_fsname) && + has_mount_option(mentry->mnt_opts, "rw")) { + mntent_list* item = (mntent_list*)calloc(1, sizeof(mntent_list)); + item->entry = *mentry; + item->entry.mnt_fsname = strdup(mentry->mnt_fsname); + item->entry.mnt_dir = strdup(mentry->mnt_dir); + item->entry.mnt_type = strdup(mentry->mnt_type); + item->entry.mnt_opts = strdup(mentry->mnt_opts); + list_add_tail(rw_entries, &item->list); } } endmntent(fp); +} + +static void free_entries(struct listnode* entries) +{ + struct listnode* node; + struct listnode* n; + list_for_each_safe(node, n, entries) { + mntent_list* item = node_to_item(node, mntent_list, list); + free(item->entry.mnt_fsname); + free(item->entry.mnt_dir); + free(item->entry.mnt_type); + free(item->entry.mnt_opts); + free(item); + } +} - return !found_rw_fs; +static mntent_list* find_item(struct listnode* rw_entries, const char* fsname_to_find) +{ + struct listnode* node; + list_for_each(node, rw_entries) { + mntent_list* item = node_to_item(node, mntent_list, list); + if (!strcmp(item->entry.mnt_fsname, fsname_to_find)) { + return item; + } + } + return NULL; } /* Remounting filesystems read-only is difficult when there are files @@ -64,38 +129,92 @@ static int remount_ro_done(void) * repeatedly until there are no more writable filesystems mounted on * block devices. */ -static void remount_ro(void) +static void remount_ro(void (*cb_on_remount)(const struct mntent*)) { - int fd, cnt = 0; + int fd, cnt; + FILE* fp; + struct mntent* mentry; + struct listnode* node; + + list_declare(rw_entries); + list_declare(ro_entries); + + sync(); + find_rw(&rw_entries); /* Trigger the remount of the filesystems as read-only, * which also marks them clean. */ - fd = open("/proc/sysrq-trigger", O_WRONLY); + fd = TEMP_FAILURE_RETRY(open("/proc/sysrq-trigger", O_WRONLY)); if (fd < 0) { - return; + KLOG_WARNING(TAG, "Failed to open sysrq-trigger.\n"); + /* TODO: Try to remount each rw parition manually in readonly mode. + * This may succeed if no process is using the partition. + */ + goto out; + } + if (TEMP_FAILURE_RETRY(write(fd, "u", 1)) != 1) { + close(fd); + KLOG_WARNING(TAG, "Failed to write to sysrq-trigger.\n"); + /* TODO: The same. Manually remount the paritions. */ + goto out; } - write(fd, "u", 1); close(fd); - /* Now poll /proc/mounts till it's done */ - while (!remount_ro_done() && (cnt < 50)) { - usleep(100000); + cnt = 0; + while (cnt < READONLY_CHECK_TIMES) { + if ((fp = setmntent("/proc/mounts", "r")) == NULL) { + /* If we can't read /proc/mounts, just give up. */ + KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n"); + goto out; + } + while ((mentry = getmntent(fp)) != NULL) { + if (!is_block_device(mentry->mnt_fsname) || + !has_mount_option(mentry->mnt_opts, "ro")) { + continue; + } + mntent_list* item = find_item(&rw_entries, mentry->mnt_fsname); + if (item) { + /* |item| has now been ro remounted. */ + list_remove(&item->list); + list_add_tail(&ro_entries, &item->list); + } + } + endmntent(fp); + if (list_empty(&rw_entries)) { + /* All rw block devices are now readonly. */ + break; + } + TEMP_FAILURE_RETRY( + usleep(READONLY_CHECK_MS * 1000 / READONLY_CHECK_TIMES)); cnt++; } - return; -} + list_for_each(node, &rw_entries) { + mntent_list* item = node_to_item(node, mntent_list, list); + KLOG_WARNING(TAG, "Failed to remount %s in readonly mode.\n", + item->entry.mnt_fsname); + } + + if (cb_on_remount) { + list_for_each(node, &ro_entries) { + mntent_list* item = node_to_item(node, mntent_list, list); + cb_on_remount(&item->entry); + } + } +out: + free_entries(&rw_entries); + free_entries(&ro_entries); +} -int android_reboot(int cmd, int flags UNUSED, const char *arg) +int android_reboot_with_callback( + int cmd, int flags __unused, const char *arg, + void (*cb_on_remount)(const struct mntent*)) { int ret; - - sync(); - remount_ro(); - + remount_ro(cb_on_remount); switch (cmd) { case ANDROID_RB_RESTART: ret = reboot(RB_AUTOBOOT); @@ -117,3 +236,7 @@ int android_reboot(int cmd, int flags UNUSED, const char *arg) return ret; } +int android_reboot(int cmd, int flags, const char *arg) +{ + return android_reboot_with_callback(cmd, flags, arg, NULL); +} diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c index 9a1ad19..7b23d71 100644 --- a/libcutils/fs_config.c +++ b/libcutils/fs_config.c @@ -121,9 +121,10 @@ static const struct fs_path_config android_files[] = { { 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private/*" }, { 00644, AID_APP, AID_APP, 0, "data/data/*" }, + /* CM's daemonized su doesn't need the setuid bit */ + { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/su" }, /* the following five files are INTENTIONALLY set-uid, but they * are NOT included on user builds. */ - { 04750, AID_ROOT, AID_SHELL, 0, "system/xbin/su" }, { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/librank" }, { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procrank" }, { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procmem" }, @@ -136,6 +137,7 @@ static const struct fs_path_config android_files[] = { { 00750, AID_ROOT, AID_ROOT, 0, "system/bin/uncrypt" }, { 00750, AID_ROOT, AID_ROOT, 0, "system/bin/install-recovery.sh" }, { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/etc/init.d/*" }, { 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" }, { 00755, AID_ROOT, AID_ROOT, 0, "system/lib64/valgrind/*" }, { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" }, diff --git a/libcutils/iosched_policy.c b/libcutils/iosched_policy.c index 8946d3c..745e2b6 100644 --- a/libcutils/iosched_policy.c +++ b/libcutils/iosched_policy.c @@ -1,9 +1,10 @@ /* -** Copyright 2007, The Android Open Source Project +** Copyright 2007-2014, The Android Open Source Project +** Copyright 2015, The CyanogenMod 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 +** 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 ** @@ -20,28 +21,30 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <pthread.h> #include <cutils/iosched_policy.h> +#define LOG_TAG "iosched_policy" +#include <cutils/log.h> + +#define __android_unused __attribute__((__unused__)) #ifdef HAVE_ANDROID_OS #include <linux/ioprio.h> #include <sys/syscall.h> -#define __android_unused -#else -#define __android_unused __attribute__((__unused__)) -#endif +#include <sys/stat.h> + +static int __rtio_cgroup_supported = -1; +static pthread_once_t __rtio_init_once = PTHREAD_ONCE_INIT; int android_set_ioprio(int pid __android_unused, IoSchedClass clazz __android_unused, int ioprio __android_unused) { -#ifdef HAVE_ANDROID_OS if (syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, pid, ioprio | (clazz << IOPRIO_CLASS_SHIFT))) { return -1; } -#endif return 0; } int android_get_ioprio(int pid __android_unused, IoSchedClass *clazz, int *ioprio) { -#ifdef HAVE_ANDROID_OS int rc; if ((rc = syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, pid)) < 0) { @@ -50,9 +53,83 @@ int android_get_ioprio(int pid __android_unused, IoSchedClass *clazz, int *iopri *clazz = (rc >> IOPRIO_CLASS_SHIFT); *ioprio = (rc & 0xff); + return 0; +} + +static void __initialize_rtio(void) { + if (!access("/sys/fs/cgroup/bfqio/tasks", W_OK) || + !access("/sys/fs/cgroup/bfqio/rt-display/tasks", W_OK)) { + __rtio_cgroup_supported = 1; + } else { + __rtio_cgroup_supported = 0; + } +} + +int android_set_rt_ioprio(int tid, int rt) { + int fd = -1, rc = -1; + + pthread_once(&__rtio_init_once, __initialize_rtio); + if (__rtio_cgroup_supported != 1) { + return -1; + } + + if (rt) { + fd = open("/sys/fs/cgroup/bfqio/rt-display/tasks", O_WRONLY | O_CLOEXEC); + } else { + fd = open("/sys/fs/cgroup/bfqio/tasks", O_WRONLY | O_CLOEXEC); + } + + if (fd < 0) { + return -1; + } + +#ifdef HAVE_GETTID + if (tid == 0) { + tid = gettid(); + } +#endif + + // specialized itoa -- works for tid > 0 + char text[22]; + char *end = text + sizeof(text) - 1; + char *ptr = end; + *ptr = '\0'; + while (tid > 0) { + *--ptr = '0' + (tid % 10); + tid = tid / 10; + } + + rc = write(fd, ptr, end - ptr); + if (rc < 0) { + /* + * If the thread is in the process of exiting, + * don't flag an error + */ + if (errno == ESRCH) { + rc = 0; + } else { + SLOGV("android_set_rt_ioprio failed to write '%s' (%s); fd=%d\n", + ptr, strerror(errno), fd); + } + } + + close(fd); + return rc; +} + #else +int android_set_ioprio(int pid __android_unused, IoSchedClass clazz __android_unused, int ioprio __android_unused) { + return 0; +} + +int android_get_ioprio(int pid __android_unused, IoSchedClass *clazz, int *ioprio) { *clazz = IoSchedClass_NONE; *ioprio = 0; -#endif return 0; } + +int android_set_rt_ioprio(int tid __android_unused, int rt __android_unused) +{ + return 0; +} +#endif diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c index 83222f4..b302bef 100644 --- a/libcutils/sched_policy.c +++ b/libcutils/sched_policy.c @@ -53,26 +53,24 @@ static inline SchedPolicy _policy(SchedPolicy p) #define TIMER_SLACK_FG 50000 static pthread_once_t the_once = PTHREAD_ONCE_INIT; +static pthread_once_t sched_once = PTHREAD_ONCE_INIT; +static pthread_once_t cpuset_once = PTHREAD_ONCE_INIT; static int __sys_supports_schedgroups = -1; +static int __sys_supports_cpusets = -1; +static char proc_name[32] = {0}; // File descriptors open to /dev/cpuctl/../tasks, setup by initialize, or -1 on error. static int bg_cgroup_fd = -1; static int fg_cgroup_fd = -1; // File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error +static int system_bg_cpuset_fd = -1; static int bg_cpuset_fd = -1; static int fg_cpuset_fd = -1; -/* Add tid to the scheduling group defined by the policy */ -static int add_tid_to_cgroup(int tid, int fd) +static int write_tid_to_fd(int tid, int fd) { - if (fd < 0) { - SLOGE("add_tid_to_cgroup failed; fd=%d\n", fd); - errno = EINVAL; - return -1; - } - // specialized itoa -- works for tid > 0 char text[22]; char *end = text + sizeof(text) - 1; @@ -90,8 +88,42 @@ static int add_tid_to_cgroup(int tid, int fd) */ if (errno == ESRCH) return 0; - SLOGW("add_tid_to_cgroup failed to write '%s' (%s); fd=%d\n", - ptr, strerror(errno), fd); + return -1; + } + + return 0; +} + +/* Add tid to the scheduling group defined by the policy */ +static int add_tid_to_cgroup(int tid, int fd) +{ + if (fd < 0) { + SLOGE("%s add_tid_to_cgroup failed; fd=%d\n", proc_name, fd); + errno = EINVAL; + return -1; + } + + if (write_tid_to_fd(tid, fd) != 0) { + SLOGW("%s add_tid_to_cgroup failed to write '%d' (%s); fd=%d\n", + proc_name, tid, strerror(errno), fd); + errno = EINVAL; + return -1; + } + + return 0; +} + +static int add_tid_to_cpuset(int tid, int fd) +{ + if (fd < 0) { + SLOGE("%s add_tid_to_cpuset failed; fd=%d\n", proc_name, fd); + errno = EINVAL; + return -1; + } + + if (write_tid_to_fd(tid, fd) != 0) { + SLOGW("%s add_tid_to_cpuset failed to write '%d' (%s); fd=%d\n", + proc_name, tid, strerror(errno), fd); errno = EINVAL; return -1; } @@ -100,32 +132,95 @@ static int add_tid_to_cgroup(int tid, int fd) } static void __initialize(void) { + int pfd; + int ptid = gettid(); + + sprintf(proc_name, "/proc/%d/cmdline", ptid); + + pfd = open(proc_name, O_RDONLY); + memset(proc_name, 0, sizeof(proc_name)); + if (pfd > 0) { + read(pfd, proc_name, sizeof(proc_name) - 1); + close(pfd); + } +} + +static void __init_sched(void) { char* filename; + + pthread_once(&the_once, __initialize); + if (!access("/dev/cpuctl/tasks", F_OK)) { __sys_supports_schedgroups = 1; filename = "/dev/cpuctl/tasks"; fg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC); if (fg_cgroup_fd < 0) { - SLOGE("open of %s failed: %s\n", filename, strerror(errno)); + SLOGE("%s open of %s failed: %s\n", proc_name, filename, strerror(errno)); + __sys_supports_schedgroups = 0; } filename = "/dev/cpuctl/bg_non_interactive/tasks"; bg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC); if (bg_cgroup_fd < 0) { - SLOGE("open of %s failed: %s\n", filename, strerror(errno)); + SLOGE("%s open of %s failed: %s\n", proc_name, filename, strerror(errno)); + __sys_supports_schedgroups = 0; + } + + if (!__sys_supports_schedgroups) { + close(bg_cgroup_fd); + bg_cgroup_fd = -1; + + close(fg_cgroup_fd); + fg_cgroup_fd = -1; } } else { __sys_supports_schedgroups = 0; } +} + +static void __init_cpuset(void) { + char *filename; + + pthread_once(&the_once, __initialize); #ifdef USE_CPUSETS if (!access("/dev/cpuset/tasks", F_OK)) { + __sys_supports_cpusets = 1; filename = "/dev/cpuset/foreground/tasks"; fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC); + if (fg_cpuset_fd < 0) { + SLOGE("%s open of %s failed %s\n", proc_name, filename, strerror(errno)); + __sys_supports_cpusets = 0; + } + filename = "/dev/cpuset/background/tasks"; bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC); + if (bg_cpuset_fd < 0) { + SLOGE("%s open of %s failed %s\n", proc_name, filename, strerror(errno)); + __sys_supports_cpusets = 0; + } + + filename = "/dev/cpuset/system-background/tasks"; + system_bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC); + if (system_bg_cpuset_fd < 0) { + SLOGE("%s open of %s failed %s\n", proc_name, filename, strerror(errno)); + __sys_supports_cpusets = 0; + } + + if (!__sys_supports_cpusets) { + close(fg_cpuset_fd); + fg_cpuset_fd = -1; + + close(bg_cpuset_fd); + bg_cpuset_fd = -1; + + close(system_bg_cpuset_fd); + system_bg_cpuset_fd = -1; + } + } else { + __sys_supports_cpusets = 0; } #endif @@ -190,11 +285,11 @@ static int getSchedulerGroup(int tid, char* buf, size_t bufLen) return 0; } - SLOGE("Failed to find cpu subsys"); + SLOGE("%s Failed to find cpu subsys", proc_name); fclose(fp); return -1; out_bad_data: - SLOGE("Bad cgroup data {%s}", lineBuf); + SLOGE("%s Bad cgroup data {%s}", proc_name, lineBuf); fclose(fp); return -1; #else @@ -208,7 +303,8 @@ int get_sched_policy(int tid, SchedPolicy *policy) if (tid == 0) { tid = gettid(); } - pthread_once(&the_once, __initialize); + + pthread_once(&sched_once, __init_sched); if (__sys_supports_schedgroups) { char grpBuf[32]; @@ -247,8 +343,13 @@ int set_cpuset_policy(int tid, SchedPolicy policy) if (tid == 0) { tid = gettid(); } + + pthread_once(&cpuset_once, __init_cpuset); + + if (!__sys_supports_cpusets) + return set_sched_policy(tid, policy); + policy = _policy(policy); - pthread_once(&the_once, __initialize); int fd; switch (policy) { @@ -260,12 +361,15 @@ int set_cpuset_policy(int tid, SchedPolicy policy) case SP_AUDIO_SYS: fd = fg_cpuset_fd; break; + case SP_SYSTEM: + fd = system_bg_cpuset_fd; + break; default: fd = -1; break; } - if (add_tid_to_cgroup(tid, fd) != 0) { + if (add_tid_to_cpuset(tid, fd) != 0) { if (errno != ESRCH && errno != ENOENT) return -errno; } @@ -280,7 +384,8 @@ int set_sched_policy(int tid, SchedPolicy policy) tid = gettid(); } policy = _policy(policy); - pthread_once(&the_once, __initialize); + + pthread_once(&sched_once, __init_sched); #if POLICY_DEBUG char statfile[64]; diff --git a/libdiskconfig/Android.mk b/libdiskconfig/Android.mk index 624e385..5e7400e 100644 --- a/libdiskconfig/Android.mk +++ b/libdiskconfig/Android.mk @@ -15,6 +15,13 @@ LOCAL_SYSTEM_SHARED_LIBRARIES := libcutils liblog libc LOCAL_CFLAGS := -Werror include $(BUILD_SHARED_LIBRARY) +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(commonSources) +LOCAL_MODULE := libdiskconfig +LOCAL_MODULE_TAGS := optional +#LOCAL_STATIC_LIBRARIES := libcutils liblog libc +include $(BUILD_STATIC_LIBRARY) + ifeq ($(HOST_OS),linux) include $(CLEAR_VARS) LOCAL_SRC_FILES := $(commonSources) diff --git a/liblog/Android.mk b/liblog/Android.mk index 115dd79..6714498 100644 --- a/liblog/Android.mk +++ b/liblog/Android.mk @@ -25,11 +25,13 @@ include $(CLEAR_VARS) liblog_cflags := -DLIBLOG_LOG_TAG=1005 ifneq ($(TARGET_USES_LOGD),false) -liblog_sources := logd_write.c log_event_write.c +liblog_sources := logd_write.c else liblog_sources := logd_write_kern.c endif +liblog_sources += log_event_write.c + # some files must not be compiled when building against Mingw # they correspond to features not used by our host development tools # which are also hard or even impossible to port to native Win32 diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c index 7a8e33f..cd85ff6 100644 --- a/liblog/log_is_loggable.c +++ b/liblog/log_is_loggable.c @@ -93,7 +93,7 @@ static int __android_log_level(const char *tag, int def) if (taglen) { uint32_t current_local_serial = current_global_serial; - if (!last_tag || strcmp(last_tag, tag)) { + if (!last_tag || (last_tag[0] != tag[0]) || strcmp(last_tag + 1, tag + 1)) { /* invalidate log.tag.<tag> cache */ for(i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) { tag_cache[i].pinfo = NULL; diff --git a/liblog/logd_write.c b/liblog/logd_write.c index bdee28f..7f772af 100644 --- a/liblog/logd_write.c +++ b/liblog/logd_write.c @@ -513,3 +513,42 @@ int __android_log_bswrite(int32_t tag, const char *payload) return write_to_log(LOG_ID_EVENTS, vec, 4); } + +#ifdef MTK_HARDWARE +struct xlog_record { + const char *tag_str; + const char *fmt_str; + int prio; +}; + +void __attribute__((weak)) __xlog_buf_printf(__unused int bufid, const struct xlog_record *xlog_record, ...) { + va_list args; + va_start(args, xlog_record); +#if HAVE_LIBC_SYSTEM_PROPERTIES + int len = 0; + int do_xlog = 0; + char results[PROP_VALUE_MAX]; + + + // MobileLog + len = __system_property_get ("debug.MB.running", results); + if (len && atoi(results)) + do_xlog = 1; + + // ModemLog + len = __system_property_get ("debug.mdlogger.Running", results); + if (len && atoi(results)) + do_xlog = 1; + + // Manual + len = __system_property_get ("persist.debug.xlog.enable", results); + if (len && atoi(results)) + do_xlog = 1; + + if (do_xlog > 0) +#endif + __android_log_vprint(xlog_record->prio, xlog_record->tag_str, xlog_record->fmt_str, args); + + return; +} +#endif diff --git a/liblog/logd_write_kern.c b/liblog/logd_write_kern.c index 8742b34..bd43238 100644 --- a/liblog/logd_write_kern.c +++ b/liblog/logd_write_kern.c @@ -26,7 +26,9 @@ #include <time.h> #include <unistd.h> +#ifdef __BIONIC__ #include <android/set_abort_message.h> +#endif #include <log/log.h> #include <log/logd.h> @@ -167,9 +169,11 @@ int __android_log_buf_write(int bufID, int prio, const char *tag, const char *ms tag = tmp_tag; } +#ifdef __BIONIC__ if (prio == ANDROID_LOG_FATAL) { android_set_abort_message(msg); } +#endif vec[0].iov_base = (unsigned char *) &prio; vec[0].iov_len = 1; diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c index 7d2a5fb..3bd59c7 100644 --- a/libnetutils/ifc_utils.c +++ b/libnetutils/ifc_utils.c @@ -698,6 +698,8 @@ ifc_configure(const char *ifname, property_set(dns_prop_name, dns1 ? ipaddr_to_string(dns1) : ""); snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns2", ifname); property_set(dns_prop_name, dns2 ? ipaddr_to_string(dns2) : ""); + snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.gw", ifname); + property_set(dns_prop_name, gateway ? ipaddr_to_string(gateway) : ""); return 0; } diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk index f02da7f..e6c9094 100644 --- a/libpixelflinger/Android.mk +++ b/libpixelflinger/Android.mk @@ -7,9 +7,16 @@ include $(CLEAR_VARS) include $(CLEAR_VARS) PIXELFLINGER_SRC_FILES:= \ + codeflinger/CodeCache.cpp \ + format.cpp \ + clear.cpp \ + raster.cpp \ + buffer.cpp + +ifeq ($(filter x86%,$(TARGET_ARCH)),) +PIXELFLINGER_SRC_FILES += \ codeflinger/ARMAssemblerInterface.cpp \ codeflinger/ARMAssemblerProxy.cpp \ - codeflinger/CodeCache.cpp \ codeflinger/GGLAssembler.cpp \ codeflinger/load_store.cpp \ codeflinger/blending.cpp \ @@ -19,10 +26,8 @@ PIXELFLINGER_SRC_FILES:= \ pixelflinger.cpp.arm \ trap.cpp.arm \ scanline.cpp.arm \ - format.cpp \ - clear.cpp \ - raster.cpp \ - buffer.cpp + +endif PIXELFLINGER_CFLAGS := -fstrict-aliasing -fomit-frame-pointer @@ -43,6 +48,18 @@ PIXELFLINGER_SRC_FILES_arm64 := \ arch-arm64/col32cb16blend.S \ arch-arm64/t32cb16blend.S \ +PIXELFLINGER_SRC_FILES_x86 := \ + codeflinger/x86/X86Assembler.cpp \ + codeflinger/x86/GGLX86Assembler.cpp \ + codeflinger/x86/load_store.cpp \ + codeflinger/x86/blending.cpp \ + codeflinger/x86/texturing.cpp \ + fixed.cpp \ + picker.cpp \ + pixelflinger.cpp \ + trap.cpp \ + scanline.cpp + ifndef ARCH_MIPS_REV6 PIXELFLINGER_SRC_FILES_mips := \ codeflinger/MIPSAssembler.cpp \ @@ -58,12 +75,16 @@ LOCAL_MODULE:= libpixelflinger 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_x86 := $(PIXELFLINGER_SRC_FILES_x86) +LOCAL_SRC_FILES_x86_64 := $(PIXELFLINGER_SRC_FILES_x86) LOCAL_SRC_FILES_mips := $(PIXELFLINGER_SRC_FILES_mips) LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS) LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS) \ external/safe-iop/include LOCAL_SHARED_LIBRARIES := libcutils liblog libutils +LOCAL_WHOLE_STATIC_LIBRARIES_x86 := libenc +LOCAL_WHOLE_STATIC_LIBRARIES_x86_64 := libenc # Really this should go away entirely or at least not depend on # libhardware, but this at least gets us built. diff --git a/libpixelflinger/codeflinger/Android.mk b/libpixelflinger/codeflinger/Android.mk new file mode 100644 index 0000000..8004af7 --- /dev/null +++ b/libpixelflinger/codeflinger/Android.mk @@ -0,0 +1,3 @@ +ifneq ($(filter x86%,$(TARGET_ARCH)),) +include $(call all-named-subdir-makefiles,x86/libenc) +endif diff --git a/libpixelflinger/codeflinger/x86/GGLX86Assembler.cpp b/libpixelflinger/codeflinger/x86/GGLX86Assembler.cpp new file mode 100644 index 0000000..1b24503 --- /dev/null +++ b/libpixelflinger/codeflinger/x86/GGLX86Assembler.cpp @@ -0,0 +1,1507 @@ +/* libs/pixelflinger/codeflinger/x86/GGLX86Assembler.cpp +** +** Copyright 2006, 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 "GGLX86Assembler" + +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <cutils/log.h> + +#include "codeflinger/x86/GGLX86Assembler.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +GGLX86Assembler::GGLX86Assembler(const sp<Assembly>& assembly) + : X86Assembler(assembly), X86RegisterAllocator(), mOptLevel(7) +{ +} + +GGLX86Assembler::~GGLX86Assembler() +{ +} + +void GGLX86Assembler::reset(int opt_level) +{ + X86Assembler::reset(); + X86RegisterAllocator::reset(); + mOptLevel = opt_level; +} + +// --------------------------------------------------------------------------- + +int GGLX86Assembler::scanline(const needs_t& needs, context_t const* c) +{ + int err = 0; + err = scanline_core(needs, c); + if (err != 0) + ALOGE("scanline_core failed probably due to running out of the registers: %d\n", err); + + // XXX: in theory, pcForLabel is not valid before generate() + char* fragment_start_pc = pcForLabel("fragment_loop"); + char* fragment_end_pc = pcForLabel("fragment_end"); + const int per_fragment_ins_size = int(fragment_end_pc - fragment_start_pc); + + // build a name for our pipeline + char name[128]; + sprintf(name, + "scanline__%08X:%08X_%08X_%08X [%3d ipp ins size]", + needs.p, needs.n, needs.t[0], needs.t[1], per_fragment_ins_size); + + if (err) { + ALOGE("Error while generating ""%s""\n", name); + disassemble(name); + return -1; + } + + return generate(name); +} + +int GGLX86Assembler::scanline_core(const needs_t& needs, context_t const* c) +{ + int64_t duration = ggl_system_time(); + + mBlendFactorCached = 0; + mBlending = 0; + mMasking = 0; + mAA = GGL_READ_NEEDS(P_AA, needs.p); + mDithering = GGL_READ_NEEDS(P_DITHER, needs.p); + mAlphaTest = GGL_READ_NEEDS(P_ALPHA_TEST, needs.p) + GGL_NEVER; + mDepthTest = GGL_READ_NEEDS(P_DEPTH_TEST, needs.p) + GGL_NEVER; + mFog = GGL_READ_NEEDS(P_FOG, needs.p) != 0; + mSmooth = GGL_READ_NEEDS(SHADE, needs.n) != 0; + mBuilderContext.needs = needs; + mBuilderContext.c = c; + mBuilderContext.Rctx = obtainReg(); //dynamically obtain if used and then immediately recycle it if not used + mCbFormat = c->formats[ GGL_READ_NEEDS(CB_FORMAT, needs.n) ]; + + // ------------------------------------------------------------------------ + + decodeLogicOpNeeds(needs); + + decodeTMUNeeds(needs, c); + + mBlendSrc = ggl_needs_to_blendfactor(GGL_READ_NEEDS(BLEND_SRC, needs.n)); + mBlendDst = ggl_needs_to_blendfactor(GGL_READ_NEEDS(BLEND_DST, needs.n)); + mBlendSrcA = ggl_needs_to_blendfactor(GGL_READ_NEEDS(BLEND_SRCA, needs.n)); + mBlendDstA = ggl_needs_to_blendfactor(GGL_READ_NEEDS(BLEND_DSTA, needs.n)); + + if (!mCbFormat.c[GGLFormat::ALPHA].h) { + if ((mBlendSrc == GGL_ONE_MINUS_DST_ALPHA) || + (mBlendSrc == GGL_DST_ALPHA)) { + mBlendSrc = GGL_ONE; + } + if ((mBlendSrcA == GGL_ONE_MINUS_DST_ALPHA) || + (mBlendSrcA == GGL_DST_ALPHA)) { + mBlendSrcA = GGL_ONE; + } + if ((mBlendDst == GGL_ONE_MINUS_DST_ALPHA) || + (mBlendDst == GGL_DST_ALPHA)) { + mBlendDst = GGL_ONE; + } + if ((mBlendDstA == GGL_ONE_MINUS_DST_ALPHA) || + (mBlendDstA == GGL_DST_ALPHA)) { + mBlendDstA = GGL_ONE; + } + } + + // if we need the framebuffer, read it now + const int blending = blending_codes(mBlendSrc, mBlendDst) | + blending_codes(mBlendSrcA, mBlendDstA); + + // XXX: handle special cases, destination not modified... + if ((mBlendSrc==GGL_ZERO) && (mBlendSrcA==GGL_ZERO) && + (mBlendDst==GGL_ONE) && (mBlendDstA==GGL_ONE)) { + // Destination unmodified (beware of logic ops) + } else if ((mBlendSrc==GGL_ZERO) && (mBlendSrcA==GGL_ZERO) && + (mBlendDst==GGL_ZERO) && (mBlendDstA==GGL_ZERO)) { + // Destination is zero (beware of logic ops) + } + + int fbComponents = 0; + const int masking = GGL_READ_NEEDS(MASK_ARGB, needs.n); + for (int i=0 ; i<4 ; i++) { + const int mask = 1<<i; + component_info_t& info = mInfo[i]; + int fs = i==GGLFormat::ALPHA ? mBlendSrcA : mBlendSrc; + int fd = i==GGLFormat::ALPHA ? mBlendDstA : mBlendDst; + if (fs==GGL_SRC_ALPHA_SATURATE && i==GGLFormat::ALPHA) + fs = GGL_ONE; + info.masked = !!(masking & mask); + info.inDest = !info.masked && mCbFormat.c[i].h && + ((mLogicOp & LOGIC_OP_SRC) || (!mLogicOp)); + if (mCbFormat.components >= GGL_LUMINANCE && + (i==GGLFormat::GREEN || i==GGLFormat::BLUE)) { + info.inDest = false; + } + info.needed = (i==GGLFormat::ALPHA) && + (isAlphaSourceNeeded() || mAlphaTest != GGL_ALWAYS); + info.replaced = !!(mTextureMachine.replaced & mask); + info.iterated = (!info.replaced && (info.inDest || info.needed)); + info.smooth = mSmooth && info.iterated; + info.fog = mFog && info.inDest && (i != GGLFormat::ALPHA); + info.blend = (fs != int(GGL_ONE)) || (fd > int(GGL_ZERO)); + + mBlending |= (info.blend ? mask : 0); + mMasking |= (mCbFormat.c[i].h && info.masked) ? mask : 0; + fbComponents |= mCbFormat.c[i].h ? mask : 0; + } + + mAllMasked = (mMasking == fbComponents); + if (mAllMasked) { + mDithering = 0; + } + + fragment_parts_t parts; + + // ------------------------------------------------------------------------ + callee_work(); + // ------------------------------------------------------------------------ + + mCurSp = -12; // %ebx, %edi, %esi + prepare_esp(0); + build_scanline_preparation(parts, needs); + recycleReg(mBuilderContext.Rctx); + + if (registerFile().status()) + return registerFile().status(); + + // ------------------------------------------------------------------------ + label("fragment_loop"); + // ------------------------------------------------------------------------ + { + Scratch regs(registerFile()); + int temp_reg = -1; + + if (mDithering) { + // update the dither index. + temp_reg = regs.obtain(); + //To load to register and calculate should be fast than the memory operations + MOV_MEM_TO_REG(parts.count.offset_ebp, PhysicalReg_EBP, temp_reg); + ROR(GGL_DITHER_ORDER_SHIFT, temp_reg); + ADD_IMM_TO_REG(1 << (32 - GGL_DITHER_ORDER_SHIFT), temp_reg); + ROR(32 - GGL_DITHER_ORDER_SHIFT, temp_reg); + MOV_REG_TO_MEM(temp_reg, parts.count.offset_ebp, PhysicalReg_EBP); + regs.recycle(temp_reg); + + } + + // XXX: could we do an early alpha-test here in some cases? + // It would probaly be used only with smooth-alpha and no texture + // (or no alpha component in the texture). + + // Early z-test + if (mAlphaTest==GGL_ALWAYS) { + build_depth_test(parts, Z_TEST|Z_WRITE); + } else { + // we cannot do the z-write here, because + // it might be killed by the alpha-test later + build_depth_test(parts, Z_TEST); + } + + { // texture coordinates + Scratch scratches(registerFile()); + + // texel generation + build_textures(parts, regs); + + } + + if ((blending & (FACTOR_DST|BLEND_DST)) || + (mMasking && !mAllMasked) || + (mLogicOp & LOGIC_OP_DST)) + { + // blending / logic_op / masking need the framebuffer + mDstPixel.setTo(regs.obtain(), &mCbFormat); + + // load the framebuffer pixel + comment("fetch color-buffer"); + parts.cbPtr.reg = regs.obtain(); + MOV_MEM_TO_REG(parts.cbPtr.offset_ebp, PhysicalReg_EBP, parts.cbPtr.reg); + load(parts.cbPtr, mDstPixel); + mCurSp = mCurSp - 4; + mDstPixel.offset_ebp = mCurSp; + MOV_REG_TO_MEM(mDstPixel.reg, mDstPixel.offset_ebp, EBP); + regs.recycle(mDstPixel.reg); + regs.recycle(parts.cbPtr.reg); + mDstPixel.reg = -1; + } + + if (registerFile().status()) + return registerFile().status(); + + pixel_t pixel; + int directTex = mTextureMachine.directTexture; + if (directTex | parts.packed) { + // note: we can't have both here + // iterated color or direct texture + if(directTex) { + pixel.offset_ebp = parts.texel[directTex-1].offset_ebp; + } + else + pixel.offset_ebp = parts.iterated.offset_ebp; + pixel.reg = regs.obtain(); + MOV_MEM_TO_REG(pixel.offset_ebp, EBP, pixel.reg); + //pixel = directTex ? parts.texel[directTex-1] : parts.iterated; + pixel.flags &= ~CORRUPTIBLE; + } else { + if (mDithering) { + mBuilderContext.Rctx = regs.obtain(); + temp_reg = regs.obtain(); + const int ctxtReg = mBuilderContext.Rctx; + MOV_MEM_TO_REG(8, EBP, ctxtReg); + const int mask = GGL_DITHER_SIZE-1; + parts.dither = reg_t(regs.obtain()); + MOV_MEM_TO_REG(parts.count.offset_ebp, EBP, parts.dither.reg); + AND_IMM_TO_REG(mask, parts.dither.reg); + ADD_REG_TO_REG(ctxtReg, parts.dither.reg); + MOVZX_MEM_TO_REG(OpndSize_8, parts.dither.reg, GGL_OFFSETOF(ditherMatrix), temp_reg); + MOV_REG_TO_REG(temp_reg, parts.dither.reg); + mCurSp = mCurSp - 4; + parts.dither.offset_ebp = mCurSp; + MOV_REG_TO_MEM(parts.dither.reg, parts.dither.offset_ebp, EBP); + regs.recycle(parts.dither.reg); + regs.recycle(temp_reg); + regs.recycle(mBuilderContext.Rctx); + + } + + // allocate a register for the resulting pixel + pixel.setTo(regs.obtain(), &mCbFormat, FIRST); + + build_component(pixel, parts, GGLFormat::ALPHA, regs); + + if (mAlphaTest!=GGL_ALWAYS) { + // only handle the z-write part here. We know z-test + // was successful, as well as alpha-test. + build_depth_test(parts, Z_WRITE); + } + + build_component(pixel, parts, GGLFormat::RED, regs); + build_component(pixel, parts, GGLFormat::GREEN, regs); + build_component(pixel, parts, GGLFormat::BLUE, regs); + + pixel.flags |= CORRUPTIBLE; + } + + if (registerFile().status()) { + return registerFile().status(); + } + + if (pixel.reg == -1) { + // be defensive here. if we're here it's probably + // that this whole fragment is a no-op. + pixel = mDstPixel; + } + + if (!mAllMasked) { + // logic operation + build_logic_op(pixel, regs); + + // masking + build_masking(pixel, regs); + + comment("store"); + parts.cbPtr.reg = regs.obtain(); + MOV_MEM_TO_REG(parts.cbPtr.offset_ebp, EBP, parts.cbPtr.reg); + store(parts.cbPtr, pixel, WRITE_BACK); + MOV_REG_TO_MEM(parts.cbPtr.reg, parts.cbPtr.offset_ebp, EBP); + regs.recycle(parts.cbPtr.reg); + regs.recycle(pixel.reg); + } + } + + if (registerFile().status()) + return registerFile().status(); + + // update the iterated color... + if (parts.reload != 3) { + build_smooth_shade(parts); + } + + // update iterated z + build_iterate_z(parts); + + // update iterated fog + build_iterate_f(parts); + + //SUB_IMM_TO_REG(1<<16, parts.count.reg); + SUB_IMM_TO_MEM(1<<16, parts.count.offset_ebp, EBP); + + JCC(Mnemonic_JNS, "fragment_loop"); + label("fragment_end"); + int update_esp_offset, shrink_esp_offset; + update_esp_offset = shrink_esp_offset = -mCurSp - 12; // 12 is ebx, esi, edi + update_esp(update_esp_offset); + shrink_esp(shrink_esp_offset); + return_work(); + + if ((mAlphaTest!=GGL_ALWAYS) || (mDepthTest!=GGL_ALWAYS)) { + if (mDepthTest!=GGL_ALWAYS) { + label("discard_before_textures"); + build_iterate_texture_coordinates(parts); + } + label("discard_after_textures"); + build_smooth_shade(parts); + build_iterate_z(parts); + build_iterate_f(parts); + if (!mAllMasked) { + //ADD_IMM_TO_REG(parts.cbPtr.size>>3, parts.cbPtr.reg); + ADD_IMM_TO_MEM(parts.cbPtr.size>>3, parts.cbPtr.offset_ebp, EBP); + } + SUB_IMM_TO_MEM(1<<16, parts.count.offset_ebp, EBP); + //SUB_IMM_TO_REG(1<<16, parts.count.reg); + JCC(Mnemonic_JNS, "fragment_loop"); + update_esp_offset = shrink_esp_offset = -mCurSp - 12; // 12 is ebx, esi, edi + update_esp(update_esp_offset); + shrink_esp(shrink_esp_offset); + return_work(); + } + + return registerFile().status(); +} + +// --------------------------------------------------------------------------- + +void GGLX86Assembler::build_scanline_preparation( + fragment_parts_t& parts, const needs_t& needs) +{ + Scratch scratches(registerFile()); + + // compute count + comment("compute ct (# of pixels to process)"); + int temp_reg; + parts.count.setTo(obtainReg()); + int Rx = scratches.obtain(); + int Ry = scratches.obtain(); + // the only argument is +8 bytes relative to the current EBP + MOV_MEM_TO_REG(8, EBP, mBuilderContext.Rctx); + CONTEXT_LOAD(Rx, iterators.xl); + CONTEXT_LOAD(parts.count.reg, iterators.xr); + CONTEXT_LOAD(Ry, iterators.y); + + // parts.count = iterators.xr - Rx + SUB_REG_TO_REG(Rx, parts.count.reg); + SUB_IMM_TO_REG(1, parts.count.reg); + + if (mDithering) { + // parts.count.reg = 0xNNNNXXDD + // NNNN = count-1 + // DD = dither offset + // XX = 0xxxxxxx (x = garbage) + Scratch scratches(registerFile()); + int tx = scratches.obtain(); + int ty = scratches.obtain(); + + MOV_REG_TO_REG(Rx,tx); + AND_IMM_TO_REG(GGL_DITHER_MASK, tx); + MOV_REG_TO_REG(Ry,ty); + AND_IMM_TO_REG(GGL_DITHER_MASK, ty); + SHL(GGL_DITHER_ORDER_SHIFT, ty); + ADD_REG_TO_REG(ty, tx); + SHL(16, parts.count.reg); + OR_REG_TO_REG(tx, parts.count.reg); + scratches.recycle(tx); + scratches.recycle(ty); + } else { + // parts.count.reg = 0xNNNN0000 + // NNNN = count-1 + SHL(16, parts.count.reg); + } + mCurSp = mCurSp - 4; + parts.count.offset_ebp = mCurSp; //ebx, esi, edi, parts.count.reg + MOV_REG_TO_MEM(parts.count.reg, parts.count.offset_ebp, EBP); + //PUSH(parts.count.reg); + recycleReg(parts.count.reg); + parts.count.reg=-1; + if (!mAllMasked) { + // compute dst ptr + comment("compute color-buffer pointer"); + const int cb_bits = mCbFormat.size*8; + int Rs = scratches.obtain(); + temp_reg = scratches.obtain(); + CONTEXT_LOAD(Rs, state.buffers.color.stride); + MOVSX_REG_TO_REG(OpndSize_16, Ry, temp_reg); + MOVSX_REG_TO_REG(OpndSize_16, Rs, Rs); + IMUL(temp_reg, Rs); + scratches.recycle(temp_reg); + ADD_REG_TO_REG(Rx, Rs); + + parts.cbPtr.setTo(obtainReg(), cb_bits); + CONTEXT_LOAD(parts.cbPtr.reg, state.buffers.color.data); + reg_t temp_reg_t; + temp_reg_t.setTo(Rs); + base_offset(parts.cbPtr, parts.cbPtr, temp_reg_t); + + mCurSp = mCurSp - 4; + parts.cbPtr.offset_ebp = mCurSp; //ebx, esi, edi, parts.count.reg, parts.cbPtr.reg + MOV_REG_TO_MEM(parts.cbPtr.reg, parts.cbPtr.offset_ebp, EBP); + //PUSH(parts.cbPtr.reg); + recycleReg(parts.cbPtr.reg); + parts.cbPtr.reg=-1; + scratches.recycle(Rs); + } + + // init fog + const int need_fog = GGL_READ_NEEDS(P_FOG, needs.p); + if (need_fog) { + comment("compute initial fog coordinate"); + Scratch scratches(registerFile()); + int ydfdy = scratches.obtain(); + int dfdx = scratches.obtain(); + CONTEXT_LOAD(dfdx, generated_vars.dfdx); + IMUL(Rx, dfdx); + CONTEXT_LOAD(ydfdy, iterators.ydfdy); + ADD_REG_TO_REG(ydfdy, dfdx); // Rx * dfdx + ydfdy + CONTEXT_STORE(dfdx, generated_vars.f); + scratches.recycle(dfdx); + scratches.recycle(ydfdy); + } + + // init Z coordinate + if ((mDepthTest != GGL_ALWAYS) || GGL_READ_NEEDS(P_MASK_Z, needs.p)) { + parts.z = reg_t(obtainReg()); + comment("compute initial Z coordinate"); + Scratch scratches(registerFile()); + int dzdx = scratches.obtain(); + int ydzdy = parts.z.reg; + CONTEXT_LOAD(dzdx, generated_vars.dzdx); // 1.31 fixed-point + IMUL(Rx, dzdx); + CONTEXT_LOAD(ydzdy, iterators.ydzdy); // 1.31 fixed-point + ADD_REG_TO_REG(dzdx, ydzdy); // parts.z.reg = Rx * dzdx + ydzdy + + mCurSp = mCurSp - 4; + parts.z.offset_ebp = mCurSp; //ebx, esi, edi, parts.count.reg, parts.cbPtr.reg, parts.z.reg + MOV_REG_TO_MEM(ydzdy, parts.z.offset_ebp, EBP); + //PUSH(ydzdy); + recycleReg(ydzdy); + parts.z.reg=-1; + + // we're going to index zbase of parts.count + // zbase = base + (xl-count + stride*y)*2 by arm + // !!! Actually, zbase = base + (xl + stride*y)*2 + int Rs = dzdx; + int zbase = scratches.obtain(); + temp_reg = zbase; + CONTEXT_LOAD(Rs, state.buffers.depth.stride); + MOVSX_REG_TO_REG(OpndSize_16, Rs, Rs); + MOV_REG_TO_REG(Ry, temp_reg); + MOVSX_REG_TO_REG(OpndSize_16, temp_reg, temp_reg); + IMUL(temp_reg, Rs); + ADD_REG_TO_REG(Rx, Rs); + // load parts.count.reg + MOV_MEM_TO_REG(parts.count.offset_ebp, EBP, temp_reg); + SHR(16, temp_reg); + ADD_REG_TO_REG(temp_reg, Rs); + SHL(1, Rs); + CONTEXT_LOAD(zbase, state.buffers.depth.data); + ADD_REG_TO_REG(Rs, zbase); + CONTEXT_STORE(zbase, generated_vars.zbase); + scratches.recycle(zbase); + scratches.recycle(dzdx); + } + // the rgisters are all used up + + // init texture coordinates + init_textures(parts.coords, reg_t(Rx), reg_t(Ry)); + scratches.recycle(Ry); + + // iterated color + init_iterated_color(parts, reg_t(Rx)); + + // init coverage factor application (anti-aliasing) + if (mAA) { + parts.covPtr.setTo(obtainReg(), 16); + CONTEXT_LOAD(parts.covPtr.reg, state.buffers.coverage); + SHL(1, Rx); + ADD_REG_TO_REG(Rx, parts.covPtr.reg); + + mCurSp = mCurSp - 4; + parts.covPtr.offset_ebp = mCurSp; + MOV_REG_TO_MEM(parts.covPtr.reg, parts.covPtr.offset_ebp, EBP); + //PUSH(parts.covPtr.reg); + recycleReg(parts.covPtr.reg); + parts.covPtr.reg=-1; + } + scratches.recycle(Rx); +} + +// --------------------------------------------------------------------------- + +void GGLX86Assembler::build_component( pixel_t& pixel, + fragment_parts_t& parts, + int component, + Scratch& regs) +{ + static char const * comments[] = {"alpha", "red", "green", "blue"}; + comment(comments[component]); + + // local register file + Scratch scratches(registerFile()); + const int dst_component_size = pixel.component_size(component); + + component_t temp(-1); + build_incoming_component( temp, dst_component_size, + parts, component, scratches, regs); + + if (mInfo[component].inDest) { + // blending... + build_blending( temp, mDstPixel, component, scratches ); + + // downshift component and rebuild pixel... + downshift(pixel, component, temp, parts.dither); + } +} + +void GGLX86Assembler::build_incoming_component( + component_t& temp, + int dst_size, + fragment_parts_t& parts, + int component, + Scratch& scratches, + Scratch& global_regs) +{ + const uint32_t component_mask = 1<<component; + + // Figure out what we need for the blending stage... + int fs = component==GGLFormat::ALPHA ? mBlendSrcA : mBlendSrc; + int fd = component==GGLFormat::ALPHA ? mBlendDstA : mBlendDst; + if (fs==GGL_SRC_ALPHA_SATURATE && component==GGLFormat::ALPHA) { + fs = GGL_ONE; + } + + // Figure out what we need to extract and for what reason + const int blending = blending_codes(fs, fd); + + // Are we actually going to blend? + const int need_blending = (fs != int(GGL_ONE)) || (fd > int(GGL_ZERO)); + + // expand the source if the destination has more bits + int need_expander = false; + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT-1 ; i++) { + texture_unit_t& tmu = mTextureMachine.tmu[i]; + if ((tmu.format_idx) && + (parts.texel[i].component_size(component) < dst_size)) { + need_expander = true; + } + } + + // do we need to extract this component? + const bool multiTexture = mTextureMachine.activeUnits > 1; + const int blend_needs_alpha_source = (component==GGLFormat::ALPHA) && + (isAlphaSourceNeeded()); + int need_extract = mInfo[component].needed; + if (mInfo[component].inDest) + { + need_extract |= ((need_blending ? + (blending & (BLEND_SRC|FACTOR_SRC)) : need_expander)); + need_extract |= (mTextureMachine.mask != mTextureMachine.replaced); + need_extract |= mInfo[component].smooth; + need_extract |= mInfo[component].fog; + need_extract |= mDithering; + need_extract |= multiTexture; + } + + if (need_extract) { + Scratch& regs = blend_needs_alpha_source ? global_regs : scratches; + component_t fragment; + + // iterated color + fragment.setTo( regs.obtain(), 0, 32, CORRUPTIBLE); + build_iterated_color(fragment, parts, component, regs); + + // texture environment (decal, modulate, replace) + build_texture_environment(fragment, parts, component, regs); + + // expand the source if the destination has more bits + if (need_expander && (fragment.size() < dst_size)) { + // we're here only if we fetched a texel + // (so we know for sure fragment is CORRUPTIBLE) + //fragment is stored on the stack + expand(fragment, fragment, dst_size); + } + + mCurSp = mCurSp - 4; + fragment.offset_ebp = mCurSp; + MOV_REG_TO_MEM(fragment.reg, fragment.offset_ebp, EBP); + regs.recycle(fragment.reg); + + // We have a few specific things to do for the alpha-channel + if ((component==GGLFormat::ALPHA) && + (mInfo[component].needed || fragment.size()<dst_size)) + { + // convert to integer_t first and make sure + // we don't corrupt a needed register + if (fragment.l) { + //component_t incoming(fragment); + // actually fragment is not corruptible + //modify(fragment, regs); + //MOV_REG_TO_REG(incoming.reg, fragment.reg); + SHR(fragment.l, fragment.offset_ebp, EBP); + fragment.h -= fragment.l; + fragment.l = 0; + } + + // I haven't found any case to trigger coverage and the following alpha test (mAlphaTest != GGL_ALWAYS) + fragment.reg = regs.obtain(); + MOV_MEM_TO_REG(fragment.offset_ebp, EBP, fragment.reg); + + // coverage factor application + build_coverage_application(fragment, parts, regs); + // alpha-test + build_alpha_test(fragment, parts); + + MOV_REG_TO_MEM(fragment.reg, fragment.offset_ebp, EBP); + regs.recycle(fragment.reg); + + if (blend_needs_alpha_source) { + // We keep only 8 bits for the blending stage + const int shift = fragment.h <= 8 ? 0 : fragment.h-8; + + if (fragment.flags & CORRUPTIBLE) { + fragment.flags &= ~CORRUPTIBLE; + mAlphaSource.setTo(fragment.reg, + fragment.size(), fragment.flags, fragment.offset_ebp); + //mCurSp = mCurSp - 4; + //mAlphaSource.offset_ebp = mCurSp; + if (shift) { + SHR(shift, mAlphaSource.offset_ebp, EBP); + } + } else { + // XXX: it would better to do this in build_blend_factor() + // so we can avoid the extra MOV below. + mAlphaSource.setTo(regs.obtain(), + fragment.size(), CORRUPTIBLE); + mCurSp = mCurSp - 4; + mAlphaSource.offset_ebp = mCurSp; + if (shift) { + MOV_MEM_TO_REG(fragment.offset_ebp, EBP, mAlphaSource.reg); + SHR(shift, mAlphaSource.reg); + } else { + MOV_MEM_TO_REG(fragment.offset_ebp, EBP, mAlphaSource.reg); + } + MOV_REG_TO_MEM(mAlphaSource.reg, mAlphaSource.offset_ebp, EBP); + regs.recycle(mAlphaSource.reg); + } + mAlphaSource.s -= shift; + + } + } + + // fog... + build_fog( fragment, component, regs ); + + temp = fragment; + } else { + if (mInfo[component].inDest) { + // extraction not needed and replace + // we just select the right component + if ((mTextureMachine.replaced & component_mask) == 0) { + // component wasn't replaced, so use it! + temp = component_t(parts.iterated, component); + } + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + const texture_unit_t& tmu = mTextureMachine.tmu[i]; + if ((tmu.mask & component_mask) && + ((tmu.replaced & component_mask) == 0)) { + temp = component_t(parts.texel[i], component); + } + } + } + } +} + +bool GGLX86Assembler::isAlphaSourceNeeded() const +{ + // XXX: also needed for alpha-test + const int bs = mBlendSrc; + const int bd = mBlendDst; + return bs==GGL_SRC_ALPHA_SATURATE || + bs==GGL_SRC_ALPHA || bs==GGL_ONE_MINUS_SRC_ALPHA || + bd==GGL_SRC_ALPHA || bd==GGL_ONE_MINUS_SRC_ALPHA ; +} + +// --------------------------------------------------------------------------- + +void GGLX86Assembler::build_smooth_shade(fragment_parts_t& parts) +{ + if (mSmooth && !parts.iterated_packed) { + // update the iterated color in a pipelined way... + comment("update iterated color"); + Scratch scratches(registerFile()); + mBuilderContext.Rctx = scratches.obtain(); + MOV_MEM_TO_REG(8, EBP, mBuilderContext.Rctx); + + const int reload = parts.reload; + for (int i=0 ; i<4 ; i++) { + if (!mInfo[i].iterated) + continue; + + int dx = parts.argb_dx[i].reg; + int c = parts.argb[i].reg; + dx = scratches.obtain(); + c = scratches.obtain(); + CONTEXT_LOAD(dx, generated_vars.argb[i].dx); + CONTEXT_LOAD(c, generated_vars.argb[i].c); + + //if (reload & 1) { + // c = scratches.obtain(); + // CONTEXT_LOAD(c, generated_vars.argb[i].c); + //} + //if (reload & 2) { + // dx = scratches.obtain(); + // CONTEXT_LOAD(dx, generated_vars.argb[i].dx); + //} + + if (mSmooth) { + ADD_REG_TO_REG(dx, c); + } + + CONTEXT_STORE(c, generated_vars.argb[i].c); + scratches.recycle(c); + scratches.recycle(dx); + //if (reload & 1) { + // CONTEXT_STORE(c, generated_vars.argb[i].c); + // scratches.recycle(c); + //} + //if (reload & 2) { + // scratches.recycle(dx); + //} + } + scratches.recycle(mBuilderContext.Rctx); + } +} + +// --------------------------------------------------------------------------- + +void GGLX86Assembler::build_coverage_application(component_t& fragment, + fragment_parts_t& parts, Scratch& regs) +{ + // here fragment.l is guarenteed to be 0 + if (mAA) { + // coverages are 1.15 fixed-point numbers + comment("coverage application"); + + component_t incoming(fragment); + modify(fragment, regs); + + Scratch scratches(registerFile()); + int cf = scratches.obtain(); + parts.covPtr.reg = scratches.obtain(); + MOV_MEM_TO_REG(parts.covPtr.offset_ebp, EBP, parts.covPtr.reg); + MOVZX_MEM_TO_REG(OpndSize_16, parts.covPtr.reg, 2, cf); // refer to LDRH definition + scratches.recycle(parts.covPtr.reg); + if (fragment.h > 31) { + fragment.h--; + + int flag_push_edx = 0; + int flag_reserve_edx = 0; + int temp_reg2 = -1; + int edx_offset_ebp = 0; + if(scratches.isUsed(EDX) == 1) { + if(incoming.reg != EDX && cf != EDX) { + flag_push_edx = 1; + mCurSp = mCurSp - 4; + edx_offset_ebp = mCurSp; + MOV_REG_TO_MEM(EDX, edx_offset_ebp, EBP); + } + } + else { + flag_reserve_edx = 1; + scratches.reserve(EDX); + } + if(scratches.isUsed(EAX)) { + if( cf == EAX || incoming.reg == EAX) { + MOVSX_REG_TO_REG(OpndSize_16, cf, cf); + if(cf == EAX) + IMUL(incoming.reg); + else + IMUL(cf); + SHL(16, EDX); + SHR(16, EAX); + MOV_REG_TO_REG(EAX, EDX, OpndSize_16); + MOV_REG_TO_REG(EDX, incoming.reg); + } + else { + int eax_offset_ebp = 0; + if(scratches.countFreeRegs() > 0) { + temp_reg2 = scratches.obtain(); + MOV_REG_TO_REG(EAX, temp_reg2); + } + else { + mCurSp = mCurSp - 4; + eax_offset_ebp = mCurSp; + MOV_REG_TO_MEM(EAX, eax_offset_ebp, EBP); + } + MOV_REG_TO_REG(cf, EAX); + MOVSX_REG_TO_REG(OpndSize_16, EAX, EAX); + IMUL(incoming.reg); + SHL(16, EDX); + SHR(16, EAX); + MOV_REG_TO_REG(EAX, EDX, OpndSize_16); + MOV_REG_TO_REG(EDX, incoming.reg); + if(temp_reg2 > -1) { + MOV_REG_TO_REG(temp_reg2, EAX); + scratches.recycle(temp_reg2); + } + else { + MOV_MEM_TO_REG(eax_offset_ebp, EBP, EAX); + } + } + } + else { + MOV_REG_TO_REG(cf, EAX); + MOVSX_REG_TO_REG(OpndSize_16, EAX, EAX); + IMUL(incoming.reg); + SHL(16, EDX); + SHR(16, EAX); + MOV_REG_TO_REG(EAX, EDX, OpndSize_16); + MOV_REG_TO_REG(EDX, incoming.reg); + } + if(flag_push_edx == 1) { + MOV_MEM_TO_REG(edx_offset_ebp, EBP, EDX); + } + if(flag_reserve_edx ==1) + scratches.recycle(EDX); + + MOV_REG_TO_REG(incoming.reg, fragment.reg); + + //IMUL(cf, incoming.reg); + } else { + MOV_REG_TO_REG(incoming.reg, fragment.reg); + SHL(1, fragment.reg); + + int flag_push_edx = 0; + int flag_reserve_edx = 0; + int temp_reg2 = -1; + int edx_offset_ebp = 0; + if(scratches.isUsed(EDX) == 1) { + if(fragment.reg != EDX && cf != EDX) { + flag_push_edx = 1; + mCurSp = mCurSp - 4; + edx_offset_ebp = mCurSp; + MOV_REG_TO_MEM(EDX, edx_offset_ebp, EBP); + } + } + else { + flag_reserve_edx = 1; + scratches.reserve(EDX); + } + if(scratches.isUsed(EAX)) { + if( cf == EAX || fragment.reg == EAX) { + MOVSX_REG_TO_REG(OpndSize_16, cf, cf); + if(cf == EAX) + IMUL(fragment.reg); + else + IMUL(cf); + SHL(16, EDX); + SHR(16, EAX); + MOV_REG_TO_REG(EAX, EDX, OpndSize_16); + MOV_REG_TO_REG(EDX, fragment.reg); + } + else { + int eax_offset_ebp = 0; + if(scratches.countFreeRegs() > 0) { + temp_reg2 = scratches.obtain(); + MOV_REG_TO_REG(EAX, temp_reg2); + } + else { + mCurSp = mCurSp - 4; + eax_offset_ebp = mCurSp; + MOV_REG_TO_MEM(EAX, eax_offset_ebp, EBP); + } + MOV_REG_TO_REG(cf, EAX); + MOVSX_REG_TO_REG(OpndSize_16, EAX, EAX); + IMUL(fragment.reg); + SHL(16, EDX); + SHR(16, EAX); + MOV_REG_TO_REG(EAX, EDX, OpndSize_16); + MOV_REG_TO_REG(EDX, fragment.reg); + if(temp_reg2 > -1) { + MOV_REG_TO_REG(temp_reg2, EAX); + scratches.recycle(temp_reg2); + } + else { + MOV_MEM_TO_REG(eax_offset_ebp, EBP, EAX); + } + } + } + else { + MOV_REG_TO_REG(cf, EAX); + MOVSX_REG_TO_REG(OpndSize_16, EAX, EAX); + IMUL(fragment.reg); + SHL(16, EDX); + SHR(16, EAX); + MOV_REG_TO_REG(EAX, EDX, OpndSize_16); + MOV_REG_TO_REG(EDX, fragment.reg); + } + if(flag_push_edx == 1) { + MOV_MEM_TO_REG(edx_offset_ebp, EBP, EDX); + } + if(flag_reserve_edx ==1) + scratches.recycle(EDX); + + //IMUL(cf, fragment.reg); + } + scratches.recycle(cf); + } +} + +// --------------------------------------------------------------------------- + +void GGLX86Assembler::build_alpha_test(component_t& fragment, + const fragment_parts_t& parts) +{ + if (mAlphaTest != GGL_ALWAYS) { + comment("Alpha Test"); + Scratch scratches(registerFile()); + int ref = scratches.obtain(); + mBuilderContext.Rctx = scratches.obtain(); + MOV_MEM_TO_REG(8, EBP, mBuilderContext.Rctx); + const int shift = GGL_COLOR_BITS-fragment.size(); + CONTEXT_LOAD(ref, state.alpha_test.ref); + scratches.recycle(mBuilderContext.Rctx); + if (shift) { + SHR(shift, ref); + CMP_REG_TO_REG(ref, fragment.reg); + } else CMP_REG_TO_REG(ref, fragment.reg); + Mnemonic cc = Mnemonic_NULL; + //int cc = NV; + switch (mAlphaTest) { + case GGL_NEVER: + JMP("discard_after_textures"); + return; + break; + case GGL_LESS: + cc = Mnemonic_JNL; + break; + case GGL_EQUAL: + cc = Mnemonic_JNE; + break; + case GGL_LEQUAL: + cc = Mnemonic_JB; + break; + case GGL_GREATER: + cc = Mnemonic_JLE; + break; + case GGL_NOTEQUAL: + cc = Mnemonic_JE; + break; + case GGL_GEQUAL: + cc = Mnemonic_JNC; + break; + } + JCC(cc, "discard_after_textures"); + //B(cc^1, "discard_after_textures"); + } +} + +// --------------------------------------------------------------------------- + +void GGLX86Assembler::build_depth_test( + const fragment_parts_t& parts, uint32_t mask) +{ + mask &= Z_TEST|Z_WRITE; + int store_flag = 0; + const needs_t& needs = mBuilderContext.needs; + const int zmask = GGL_READ_NEEDS(P_MASK_Z, needs.p); + Scratch scratches(registerFile()); + + if (mDepthTest != GGL_ALWAYS || zmask) { + Mnemonic ic = Mnemonic_NULL; + switch (mDepthTest) { + case GGL_LESS: + ic = Mnemonic_JBE; + break; + case GGL_EQUAL: + ic = Mnemonic_JNE; + break; + case GGL_LEQUAL: + ic = Mnemonic_JB; + break; + case GGL_GREATER: + ic = Mnemonic_JGE; + break; + case GGL_NOTEQUAL: + ic = Mnemonic_JE; + break; + case GGL_GEQUAL: + ic = Mnemonic_JA; + break; + case GGL_NEVER: + // this never happens, because it's taken care of when + // computing the needs. but we keep it for completness. + comment("Depth Test (NEVER)"); + JMP("discard_before_textures"); + return; + case GGL_ALWAYS: + // we're here because zmask is enabled + mask &= ~Z_TEST; // test always passes. + break; + } + + + if ((mask & Z_WRITE) && !zmask) { + mask &= ~Z_WRITE; + } + + if (!mask) + return; + + comment("Depth Test"); + + int zbase = scratches.obtain(); + mBuilderContext.Rctx = scratches.obtain(); + MOV_MEM_TO_REG(8, EBP, mBuilderContext.Rctx); + CONTEXT_LOAD(zbase, generated_vars.zbase); // stall + scratches.recycle(mBuilderContext.Rctx); + + int temp_reg1 = scratches.obtain(); + int depth = scratches.obtain(); + int z = parts.z.reg; + MOV_MEM_TO_REG(parts.count.offset_ebp, PhysicalReg_EBP, temp_reg1); + SHR(15, temp_reg1); + SUB_REG_TO_REG(temp_reg1, zbase); + + // above does zbase = zbase + ((count >> 16) << 1) + + if (mask & Z_TEST) { + MOVZX_MEM_TO_REG(OpndSize_16, zbase, 0, depth); + MOV_MEM_TO_REG(parts.z.offset_ebp, PhysicalReg_EBP, temp_reg1); + SHR(16, temp_reg1); + CMP_REG_TO_REG(temp_reg1, depth); + JCC(ic, "discard_before_textures"); + + } + if (mask & Z_WRITE) { + if (mask == Z_WRITE) { + // only z-write asked, cc is meaningless + store_flag = 1; + } + // actually it must be stored since the above branch is not taken + MOV_REG_TO_MEM(temp_reg1, 0, zbase, OpndSize_16); + } + scratches.recycle(temp_reg1); + scratches.recycle(zbase); + scratches.recycle(depth); + } +} + +void GGLX86Assembler::build_iterate_z(const fragment_parts_t& parts) +{ + const needs_t& needs = mBuilderContext.needs; + if ((mDepthTest != GGL_ALWAYS) || GGL_READ_NEEDS(P_MASK_Z, needs.p)) { + Scratch scratches(registerFile()); + int dzdx = scratches.obtain(); + mBuilderContext.Rctx = scratches.obtain(); + MOV_MEM_TO_REG(8, EBP, mBuilderContext.Rctx); + CONTEXT_LOAD(dzdx, generated_vars.dzdx); // stall + scratches.recycle(mBuilderContext.Rctx); + ADD_REG_TO_MEM(dzdx, EBP, parts.z.offset_ebp); + scratches.recycle(dzdx); + } +} + +void GGLX86Assembler::build_iterate_f(const fragment_parts_t& parts) +{ + const needs_t& needs = mBuilderContext.needs; + if (GGL_READ_NEEDS(P_FOG, needs.p)) { + Scratch scratches(registerFile()); + int dfdx = scratches.obtain(); + int f = scratches.obtain(); + mBuilderContext.Rctx = scratches.obtain(); + MOV_MEM_TO_REG(8, EBP, mBuilderContext.Rctx); + CONTEXT_LOAD(f, generated_vars.f); + CONTEXT_LOAD(dfdx, generated_vars.dfdx); // stall + ADD_REG_TO_REG(dfdx, f); + CONTEXT_STORE(f, generated_vars.f); + scratches.recycle(mBuilderContext.Rctx); + scratches.recycle(dfdx); + scratches.recycle(f); + } +} + +// --------------------------------------------------------------------------- + +void GGLX86Assembler::build_logic_op(pixel_t& pixel, Scratch& regs) +{ + const needs_t& needs = mBuilderContext.needs; + const int opcode = GGL_READ_NEEDS(LOGIC_OP, needs.n) | GGL_CLEAR; + if (opcode == GGL_COPY) + return; + + comment("logic operation"); + + pixel_t s(pixel); + if (!(pixel.flags & CORRUPTIBLE)) { + pixel.reg = regs.obtain(); + pixel.flags |= CORRUPTIBLE; + } + + pixel_t d(mDstPixel); + d.reg = regs.obtain(); + MOV_MEM_TO_REG(mDstPixel.offset_ebp, EBP, d.reg); + switch(opcode) { + case GGL_CLEAR: + MOV_IMM_TO_REG(0, pixel.reg); + break; + case GGL_AND: + MOV_REG_TO_REG(d.reg, pixel.reg); + AND_REG_TO_REG(s.reg, pixel.reg); + break; + case GGL_AND_REVERSE: + MOV_REG_TO_REG(d.reg, pixel.reg); + NOT(pixel.reg); + AND_REG_TO_REG(s.reg, pixel.reg); + break; + case GGL_COPY: + break; + case GGL_AND_INVERTED: + MOV_REG_TO_REG(s.reg, pixel.reg); + NOT(pixel.reg); + AND_REG_TO_REG(d.reg, pixel.reg); + break; + case GGL_NOOP: + MOV_REG_TO_REG(d.reg, pixel.reg); + break; + case GGL_XOR: + MOV_REG_TO_REG(d.reg, pixel.reg); + XOR(s.reg, pixel.reg); + break; + case GGL_OR: + MOV_REG_TO_REG(d.reg, pixel.reg); + OR_REG_TO_REG(s.reg, pixel.reg); + break; + case GGL_NOR: + MOV_REG_TO_REG(d.reg, pixel.reg); + OR_REG_TO_REG(s.reg, pixel.reg); + NOT(pixel.reg); + break; + case GGL_EQUIV: + MOV_REG_TO_REG(d.reg, pixel.reg); + XOR(s.reg, pixel.reg); + NOT(pixel.reg); + break; + case GGL_INVERT: + MOV_REG_TO_REG(d.reg, pixel.reg); + NOT(pixel.reg); + break; + case GGL_OR_REVERSE: // s | ~d == ~(~s & d) + MOV_REG_TO_REG(s.reg, pixel.reg); + NOT(pixel.reg); + AND_REG_TO_REG(d.reg, pixel.reg); + NOT(pixel.reg); + break; + case GGL_COPY_INVERTED: + MOV_REG_TO_REG(s.reg, pixel.reg); + NOT(pixel.reg); + break; + case GGL_OR_INVERTED: // ~s | d == ~(s & ~d) + MOV_REG_TO_REG(d.reg, pixel.reg); + NOT(pixel.reg); + AND_REG_TO_REG(s.reg, pixel.reg); + NOT(pixel.reg); + break; + case GGL_NAND: + MOV_REG_TO_REG(d.reg, pixel.reg); + AND_REG_TO_REG(s.reg, pixel.reg); + NOT(pixel.reg); + break; + case GGL_SET: + MOV_IMM_TO_REG(0, pixel.reg); + NOT(pixel.reg); + break; + }; + regs.recycle(d.reg); +} + +// --------------------------------------------------------------------------- + + +void GGLX86Assembler::build_and_immediate(int d, int s, uint32_t mask, int bits) +{ + uint32_t rot; + uint32_t size = ((bits>=32) ? 0 : (1LU << bits)) - 1; + mask &= size; + + if (mask == size) { + if (d != s) + MOV_REG_TO_REG(s, d); + return; + } + + MOV_REG_TO_REG(s, d); + AND_IMM_TO_REG(mask, d); +} + +void GGLX86Assembler::build_masking(pixel_t& pixel, Scratch& regs) +{ + if (!mMasking || mAllMasked) { + return; + } + + comment("color mask"); + + pixel_t fb(mDstPixel); + fb.reg = regs.obtain(); + MOV_MEM_TO_REG(mDstPixel.offset_ebp, EBP, fb.reg); + pixel_t s(pixel); + if (!(pixel.flags & CORRUPTIBLE)) { + pixel.reg = regs.obtain(); + pixel.flags |= CORRUPTIBLE; + } + + int mask = 0; + for (int i=0 ; i<4 ; i++) { + const int component_mask = 1<<i; + const int h = fb.format.c[i].h; + const int l = fb.format.c[i].l; + if (h && (!(mMasking & component_mask))) { + mask |= ((1<<(h-l))-1) << l; + } + } + + // There is no need to clear the masked components of the source + // (unless we applied a logic op), because they're already zeroed + // by construction (masked components are not computed) + + if (mLogicOp) { + const needs_t& needs = mBuilderContext.needs; + const int opcode = GGL_READ_NEEDS(LOGIC_OP, needs.n) | GGL_CLEAR; + if (opcode != GGL_CLEAR) { + // clear masked component of source + build_and_immediate(pixel.reg, s.reg, mask, fb.size()); + s = pixel; + } + } + + // clear non masked components of destination + build_and_immediate(fb.reg, fb.reg, ~mask, fb.size()); + + // or back the channels that were masked + if (s.reg == fb.reg) { + // this is in fact a MOV + if (s.reg == pixel.reg) { + // ugh. this in in fact a nop + } else { + MOV_REG_TO_REG(fb.reg, pixel.reg); + } + } else { + MOV_REG_TO_REG(fb.reg, pixel.reg); + OR_REG_TO_REG(s.reg, pixel.reg); + } + MOV_REG_TO_MEM(fb.reg, mDstPixel.offset_ebp, EBP); +} + +// --------------------------------------------------------------------------- + +void GGLX86Assembler::base_offset(pointer_t& d, pointer_t& b, const reg_t& o) +{ +// d and b are the same reference + Scratch scratches(registerFile()); + int temp_reg = scratches.obtain(); + switch (b.size) { + case 32: + MOV_REG_TO_REG(b.reg, temp_reg); + MOV_REG_TO_REG(o.reg, d.reg); + SHL(2,d.reg); + ADD_REG_TO_REG(temp_reg, d.reg); + break; + case 24: + if (d.reg == b.reg) { + MOV_REG_TO_REG(b.reg, temp_reg); + MOV_REG_TO_REG(o.reg, d.reg); + SHL(1,d.reg); + ADD_REG_TO_REG(temp_reg, d.reg); + ADD_REG_TO_REG(o.reg, d.reg); + } else { + MOV_REG_TO_REG(o.reg, temp_reg); + SHL(1,temp_reg); + MOV_REG_TO_REG(temp_reg, d.reg); + ADD_REG_TO_REG(o.reg, d.reg); + ADD_REG_TO_REG(b.reg, d.reg); + } + break; + case 16: + MOV_REG_TO_REG(b.reg, temp_reg); + MOV_REG_TO_REG(o.reg, d.reg); + SHL(1,d.reg); + ADD_REG_TO_REG(temp_reg, d.reg); + break; + case 8: + MOV_REG_TO_REG(b.reg, temp_reg); + MOV_REG_TO_REG(o.reg, d.reg); + ADD_REG_TO_REG(temp_reg, d.reg); + break; + } + scratches.recycle(temp_reg); +} + +// ---------------------------------------------------------------------------- +// cheezy register allocator... +// ---------------------------------------------------------------------------- + +void X86RegisterAllocator::reset() +{ + mRegs.reset(); +} + +int X86RegisterAllocator::reserveReg(int reg) +{ + return mRegs.reserve(reg); +} + +int X86RegisterAllocator::obtainReg() +{ + return mRegs.obtain(); +} + +void X86RegisterAllocator::recycleReg(int reg) +{ + mRegs.recycle(reg); +} + +X86RegisterAllocator::RegisterFile& X86RegisterAllocator::registerFile() +{ + return mRegs; +} + +// ---------------------------------------------------------------------------- + +X86RegisterAllocator::RegisterFile::RegisterFile() + : mRegs(0), mTouched(0), mStatus(0) +{ + //reserve(PhysicalReg_EBP); + //reserve(PhysicalReg_ESP); +} + +X86RegisterAllocator::RegisterFile::RegisterFile(const RegisterFile& rhs) + : mRegs(rhs.mRegs), mTouched(rhs.mTouched) +{ +} + +X86RegisterAllocator::RegisterFile::~RegisterFile() +{ +} + +bool X86RegisterAllocator::RegisterFile::operator == (const RegisterFile& rhs) const +{ + return (mRegs == rhs.mRegs); +} + +void X86RegisterAllocator::RegisterFile::reset() +{ + mRegs = mTouched = mStatus = 0; +} + +int X86RegisterAllocator::RegisterFile::reserve(int reg) +{ + LOG_ALWAYS_FATAL_IF(isUsed(reg), + "reserving register %d, but already in use", + reg); + if(isUsed(reg)) return -1; + mRegs |= (1<<reg); + mTouched |= mRegs; + return reg; +} + +void X86RegisterAllocator::RegisterFile::reserveSeveral(uint32_t regMask) +{ + mRegs |= regMask; + mTouched |= regMask; +} + +int X86RegisterAllocator::RegisterFile::isUsed(int reg) const +{ + LOG_ALWAYS_FATAL_IF(reg>=6, "invalid register %d", reg); + return mRegs & (1<<reg); +} + +int X86RegisterAllocator::RegisterFile::obtain() +{ +//multiplication result is in edx:eax +//ebx, ecx, edi, esi, eax, edx + const char priorityList[6] = { PhysicalReg_EBX, PhysicalReg_ECX,PhysicalReg_EDI, PhysicalReg_ESI, PhysicalReg_EAX, PhysicalReg_EDX }; + + const int nbreg = sizeof(priorityList); + int i, r; + for (i=0 ; i<nbreg ; i++) { + r = priorityList[i]; + if (!isUsed(r)) { + break; + } + } + // this is not an error anymore because, we'll try again with + // a lower optimization level. + ALOGE_IF(i >= nbreg, "pixelflinger ran out of registers\n"); + if (i >= nbreg) { + mStatus |= OUT_OF_REGISTERS; + // we return SP so we can more easily debug things + // the code will never be run anyway. + printf("pixelflinger ran out of registers\n"); + return PhysicalReg_ESP; + //return -1; + } + reserve(r); + return r; +} + +bool X86RegisterAllocator::RegisterFile::hasFreeRegs() const +{ + return ((mRegs & 0x3F) == 0x3F) ? false : true; +} + +int X86RegisterAllocator::RegisterFile::countFreeRegs() const +{ + int f = ~mRegs & 0x3F; + // now count number of 1 + f = (f & 0x5555) + ((f>>1) & 0x5555); + f = (f & 0x3333) + ((f>>2) & 0x3333); + f = (f & 0x0F0F) + ((f>>4) & 0x0F0F); + f = (f & 0x00FF) + ((f>>8) & 0x00FF); + return f; +} + +void X86RegisterAllocator::RegisterFile::recycle(int reg) +{ + LOG_FATAL_IF(!isUsed(reg), + "recycling unallocated register %d", + reg); + mRegs &= ~(1<<reg); +} + +void X86RegisterAllocator::RegisterFile::recycleSeveral(uint32_t regMask) +{ + LOG_FATAL_IF((mRegs & regMask)!=regMask, + "recycling unallocated registers " + "(recycle=%08x, allocated=%08x, unallocated=%08x)", + regMask, mRegs, mRegs®Mask); + mRegs &= ~regMask; +} + +uint32_t X86RegisterAllocator::RegisterFile::touched() const +{ + return mTouched; +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/libpixelflinger/codeflinger/x86/GGLX86Assembler.h b/libpixelflinger/codeflinger/x86/GGLX86Assembler.h new file mode 100644 index 0000000..1960cfc --- /dev/null +++ b/libpixelflinger/codeflinger/x86/GGLX86Assembler.h @@ -0,0 +1,563 @@ +/* libs/pixelflinger/codeflinger/x86/GGLX86Assembler.h +** +** Copyright 2006, 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_GGLX86ASSEMBLER_H +#define ANDROID_GGLX86ASSEMBLER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <private/pixelflinger/ggl_context.h> + +#include "codeflinger/x86/X86Assembler.h" + + +namespace android { + +// ---------------------------------------------------------------------------- + +#define CONTEXT_LOAD(REG, FIELD) \ + MOV_MEM_TO_REG(GGL_OFFSETOF(FIELD), mBuilderContext.Rctx, REG) + +#define CONTEXT_STORE(REG, FIELD) \ + MOV_REG_TO_MEM(REG, GGL_OFFSETOF(FIELD), mBuilderContext.Rctx) + +class X86RegisterAllocator +{ +public: + class RegisterFile; + + RegisterFile& registerFile(); + int reserveReg(int reg); + int obtainReg(); + void recycleReg(int reg); + void reset(); + + class RegisterFile + { + public: + RegisterFile(); + RegisterFile(const RegisterFile& rhs); + ~RegisterFile(); + + void reset(); + + bool operator == (const RegisterFile& rhs) const; + bool operator != (const RegisterFile& rhs) const { + return !operator == (rhs); + } + + int reserve(int reg); + void reserveSeveral(uint32_t regMask); + + void recycle(int reg); + void recycleSeveral(uint32_t regMask); + + int obtain(); + inline int isUsed(int reg) const; + + bool hasFreeRegs() const; + int countFreeRegs() const; + + uint32_t touched() const; + inline uint32_t status() const { return mStatus; } + + enum { + OUT_OF_REGISTERS = 0x1 + }; + + private: + uint32_t mRegs; + uint32_t mTouched; + uint32_t mStatus; + }; + + class Scratch + { + public: + Scratch(RegisterFile& regFile) + : mRegFile(regFile), mScratch(0) { + } + ~Scratch() { + mRegFile.recycleSeveral(mScratch); + } + int obtain() { + int reg = mRegFile.obtain(); + mScratch |= 1<<reg; + return reg; + } + void reserve(int reg) { + mRegFile.reserve(reg); + mScratch |= 1<<reg; + } + void recycle(int reg) { + mRegFile.recycle(reg); + mScratch &= ~(1<<reg); + } + bool isUsed(int reg) { + return (mScratch & (1<<reg)); + } + int countFreeRegs() { + return mRegFile.countFreeRegs(); + } + private: + RegisterFile& mRegFile; + uint32_t mScratch; + }; + +/* +// currently we don't use it + + class Spill + { + public: + Spill(RegisterFile& regFile, X86Assembler& gen, uint32_t reglist) + : mRegFile(regFile), mGen(gen), mRegList(reglist), mCount(0) + { + if (reglist) { + int count = 0; + while (reglist) { + count++; + reglist &= ~(1 << (31 - __builtin_clz(reglist))); + } + if (count == 1) { + int reg = 31 - __builtin_clz(mRegList); + // move to the stack + } else { + // move to the stack + } + mRegFile.recycleSeveral(mRegList); + mCount = count; + } + } + ~Spill() { + if (mRegList) { + if (mCount == 1) { + int reg = 31 - __builtin_clz(mRegList); + // move to the stack + } else { + } + mRegFile.reserveSeveral(mRegList); + } + } + private: + RegisterFile& mRegFile; + X86Assembler& mGen; + uint32_t mRegList; + int mCount; + }; +*/ + +private: + RegisterFile mRegs; +}; + +// ---------------------------------------------------------------------------- + +class GGLX86Assembler : public X86Assembler, public X86RegisterAllocator +{ +public: + + GGLX86Assembler(const sp<Assembly>& assembly); + ~GGLX86Assembler(); + + char* base() const { return 0; } // XXX + char* pc() const { return 0; } // XXX + + void reset(int opt_level); + + + // generate scanline code for given needs + int scanline(const needs_t& needs, context_t const* c); + int scanline_core(const needs_t& needs, context_t const* c); + + enum { + CLEAR_LO = 0x0001, + CLEAR_HI = 0x0002, + CORRUPTIBLE = 0x0004, + FIRST = 0x0008 + }; + + enum { //load/store flags + WRITE_BACK = 0x0001 + }; + + struct reg_t { + reg_t() : reg(-1), flags(0), offset_ebp(0) { + } + reg_t(int r, int f=0, int offset=0) + : reg(r), flags(f), offset_ebp(offset) { + } + void setTo(int r, int f=0, int offset=0) { + reg=r; flags=f; offset_ebp=offset; + } + int reg; + uint16_t flags; + int offset_ebp; + }; + + struct integer_t : public reg_t { + integer_t() : reg_t(), s(0) { + } + integer_t(int r, int sz=32, int f=0, int offset=0) + : reg_t(r, f, offset), s(sz) { + } + void setTo(int r, int sz=32, int f=0, int offset=0) { + reg_t::setTo(r, f, offset); s=sz; + } + int8_t s; + inline int size() const { return s; } + }; + + struct pixel_t : public reg_t { + pixel_t() : reg_t() { + memset(&format, 0, sizeof(GGLFormat)); + } + pixel_t(int r, const GGLFormat* fmt, int f=0, int offset=0) + : reg_t(r, f, offset), format(*fmt) { + } + void setTo(int r, const GGLFormat* fmt, int f=0, int offset=0) { + reg_t::setTo(r, f, offset); format = *fmt; + } + GGLFormat format; + inline int hi(int c) const { return format.c[c].h; } + inline int low(int c) const { return format.c[c].l; } + inline int mask(int c) const { return ((1<<size(c))-1) << low(c); } + inline int size() const { return format.size*8; } + inline int size(int c) const { return component_size(c); } + inline int component_size(int c) const { return hi(c) - low(c); } + }; + + struct component_t : public reg_t { + component_t() : reg_t(), h(0), l(0) { + } + component_t(int r, int f=0, int offset=0) + : reg_t(r, f, offset), h(0), l(0) { + } + component_t(int r, int lo, int hi, int f=0, int offset=0) + : reg_t(r, f, offset), h(hi), l(lo) { + } + explicit component_t(const integer_t& rhs) + : reg_t(rhs.reg, rhs.flags, rhs.offset_ebp), h(rhs.s), l(0) { + } + explicit component_t(const pixel_t& rhs, int component) { + setTo( rhs.reg, + rhs.format.c[component].l, + rhs.format.c[component].h, + rhs.flags|CLEAR_LO|CLEAR_HI, rhs.offset_ebp); + } + void setTo(int r, int lo=0, int hi=0, int f=0, int offset=0) { + reg_t::setTo(r, f, offset); h=hi; l=lo; + } + int8_t h; + int8_t l; + inline int size() const { return h-l; } + }; + + struct pointer_t : public reg_t { + pointer_t() : reg_t(), size(0) { + } + pointer_t(int r, int s, int f=0, int offset=0) + : reg_t(r, f, offset), size(s) { + } + void setTo(int r, int s, int f=0, int offset=0) { + reg_t::setTo(r, f, offset); size=s; + } + int8_t size; + }; + + +private: + struct tex_coord_t { + reg_t s; + reg_t t; + pointer_t ptr; + }; + + struct fragment_parts_t { + uint32_t packed : 1; + uint32_t reload : 2; + uint32_t iterated_packed : 1; + pixel_t iterated; + pointer_t cbPtr; + pointer_t covPtr; + reg_t count; + reg_t argb[4]; + reg_t argb_dx[4]; + reg_t z; + reg_t dither; + pixel_t texel[GGL_TEXTURE_UNIT_COUNT]; + tex_coord_t coords[GGL_TEXTURE_UNIT_COUNT]; + }; + + struct texture_unit_t { + int format_idx; + GGLFormat format; + int bits; + int swrap; + int twrap; + int env; + int pot; + int linear; + uint8_t mask; + uint8_t replaced; + }; + + struct texture_machine_t { + texture_unit_t tmu[GGL_TEXTURE_UNIT_COUNT]; + uint8_t mask; + uint8_t replaced; + uint8_t directTexture; + uint8_t activeUnits; + }; + + struct component_info_t { + bool masked : 1; + bool inDest : 1; + bool needed : 1; + bool replaced : 1; + bool iterated : 1; + bool smooth : 1; + bool blend : 1; + bool fog : 1; + }; + + struct builder_context_t { + context_t const* c; + needs_t needs; + int Rctx; + }; + + template <typename T> + void modify(T& r, Scratch& regs) + { + if (!(r.flags & CORRUPTIBLE)) { + r.reg = regs.obtain(); + r.flags |= CORRUPTIBLE; + } + } + + // helpers + void base_offset(pointer_t& d, pointer_t& b, const reg_t& o); + + // texture environement + void modulate( component_t& dest, + const component_t& incoming, + const pixel_t& texel, int component); + + void decal( component_t& dest, + const component_t& incoming, + const pixel_t& texel, int component); + + void blend( component_t& dest, + const component_t& incoming, + const pixel_t& texel, int component, int tmu); + + void add( component_t& dest, + const component_t& incoming, + const pixel_t& texel, int component); + + // load/store stuff + void store(const pointer_t& addr, const pixel_t& src, uint32_t flags=0); + void load(pointer_t& addr, const pixel_t& dest, uint32_t flags=0); + + void extract(integer_t& d, const pixel_t& s, int component); + void extract(component_t& d, const pixel_t& s, int component); + void extract(integer_t& d, int s, int h, int l, int bits=32); + void expand(integer_t& d, const integer_t& s, int dbits); + void expand(integer_t& d, const component_t& s, int dbits); + void expand(component_t& d, const component_t& s, int dbits); + void downshift(pixel_t& d, int component, component_t s, reg_t& dither); + + + void mul_factor( component_t& d, + const integer_t& v, + const integer_t& f, Scratch& scratches); + + void mul_factor_add( component_t& d, + const integer_t& v, + const integer_t& f, + const component_t& a); + + void component_add( component_t& d, + const integer_t& dst, + const integer_t& src); + + void component_sat( const component_t& v, const int temp_reg); + + + void build_scanline_preparation(fragment_parts_t& parts, + const needs_t& needs); + + void build_smooth_shade(fragment_parts_t& parts); + + void build_component( pixel_t& pixel, + fragment_parts_t& parts, + int component, + Scratch& global_scratches); + + void build_incoming_component( + component_t& temp, + int dst_size, + fragment_parts_t& parts, + int component, + Scratch& scratches, + Scratch& global_scratches); + + void init_iterated_color(fragment_parts_t& parts, const reg_t& x); + + void build_iterated_color( component_t& fragment, + fragment_parts_t& parts, + int component, + Scratch& regs); + + void decodeLogicOpNeeds(const needs_t& needs); + + void decodeTMUNeeds(const needs_t& needs, context_t const* c); + + void init_textures( tex_coord_t* coords, + const reg_t& x, + const reg_t& y); + + void build_textures( fragment_parts_t& parts, + Scratch& regs); + + void filter8( const fragment_parts_t& parts, + pixel_t& texel, const texture_unit_t& tmu, + reg_t reg_U, reg_t reg_V, pointer_t& txPtr, + int FRAC_BITS, Scratch& scratches); + + void filter16( const fragment_parts_t& parts, + pixel_t& texel, const texture_unit_t& tmu, + reg_t reg_U, reg_t reg_V, pointer_t& txPtr, + int FRAC_BITS, Scratch& scratches); + + void filter24( const fragment_parts_t& parts, + pixel_t& texel, const texture_unit_t& tmu, + int U, int V, pointer_t& txPtr, + int FRAC_BITS); + + void filter32( const fragment_parts_t& parts, + pixel_t& texel, const texture_unit_t& tmu, + reg_t reg_U, reg_t reg_V, pointer_t& txPtr, + int FRAC_BITS, Scratch& scratches); + + void build_texture_environment( component_t& fragment, + fragment_parts_t& parts, + int component, + Scratch& regs); + + void wrapping( int d, + int coord, int size, + int tx_wrap, int tx_linear, Scratch& scratches); + + void build_fog( component_t& temp, + int component, + Scratch& parent_scratches); + + void build_blending( component_t& in_out, + pixel_t& pixel, + int component, + Scratch& parent_scratches); + + void build_blend_factor( + integer_t& factor, int f, int component, + const pixel_t& dst_pixel, + integer_t& fragment, + integer_t& fb, + Scratch& scratches); + + void build_blendFOneMinusF( component_t& temp, + const integer_t& factor, + const integer_t& fragment, + const integer_t& fb); + + void build_blendOneMinusFF( component_t& temp, + const integer_t& factor, + const integer_t& fragment, + const integer_t& fb); + + void build_coverage_application(component_t& fragment, + fragment_parts_t& parts, + Scratch& regs); + + void build_alpha_test(component_t& fragment, const fragment_parts_t& parts); + + enum { Z_TEST=1, Z_WRITE=2 }; + void build_depth_test(const fragment_parts_t& parts, uint32_t mask); + void build_iterate_z(const fragment_parts_t& parts); + void build_iterate_f(const fragment_parts_t& parts); + void build_iterate_texture_coordinates(const fragment_parts_t& parts); + + void build_logic_op(pixel_t& pixel, Scratch& regs); + + void build_masking(pixel_t& pixel, Scratch& regs); + + void build_and_immediate(int d, int s, uint32_t mask, int bits); + + bool isAlphaSourceNeeded() const; + + enum { + FACTOR_SRC=1, FACTOR_DST=2, BLEND_SRC=4, BLEND_DST=8 + }; + + enum { + LOGIC_OP=1, LOGIC_OP_SRC=2, LOGIC_OP_DST=4 + }; + + static int blending_codes(int fs, int fd); + + builder_context_t mBuilderContext; + texture_machine_t mTextureMachine; + component_info_t mInfo[4]; + int mBlending; + int mMasking; + int mAllMasked; + int mLogicOp; + int mAlphaTest; + int mAA; + int mDithering; + int mDepthTest; + + int mSmooth; + int mFog; + pixel_t mDstPixel; + + GGLFormat mCbFormat; + + int mBlendFactorCached; + integer_t mAlphaSource; + + int mBaseRegister; + + int mBlendSrc; + int mBlendDst; + int mBlendSrcA; + int mBlendDstA; + + int mOptLevel; + + // to stretch esp and shrink esp + int mCurSp; +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_GGLX86ASSEMBLER_H diff --git a/libpixelflinger/codeflinger/x86/X86Assembler.cpp b/libpixelflinger/codeflinger/x86/X86Assembler.cpp new file mode 100644 index 0000000..2a717ac --- /dev/null +++ b/libpixelflinger/codeflinger/x86/X86Assembler.cpp @@ -0,0 +1,618 @@ +/* libs/pixelflinger/codeflinger/x86/X86Assembler.cpp +** +** Copyright 2006, 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 "X86Assembler" + +#include <stdio.h> +#include <stdlib.h> +#include <cutils/log.h> +#include <cutils/properties.h> +#include <string.h> + +#if defined(WITH_LIB_HARDWARE) +#include <hardware_legacy/qemu_tracing.h> +#endif + +#include <private/pixelflinger/ggl_context.h> + +#include "codeflinger/CodeCache.h" +#include "codeflinger/x86/X86Assembler.h" + +// ---------------------------------------------------------------------------- + +namespace android { + +// ---------------------------------------------------------------------------- + +X86Assembler::X86Assembler(const sp<Assembly>& assembly) + : mAssembly(assembly) +{ + mBase = mStream = (char *)assembly->base(); + mDuration = ggl_system_time(); +#if defined(WITH_LIB_HARDWARE) + mQemuTracing = true; +#endif +} + +X86Assembler::~X86Assembler() +{ +} + +char* X86Assembler::pc() const +{ + return mStream; +} + +char* X86Assembler::base() const +{ + return mBase; +} + +void X86Assembler::reset() +{ + mBase = mStream = (char *)mAssembly->base(); + mBranchTargets.clear(); + mLabels.clear(); + mLabelsInverseMapping.clear(); + mComments.clear(); +} + +// ---------------------------------------------------------------------------- + +void X86Assembler::disassemble(const char* name) +{ + if (name) { + printf("%s:\n", name); + } + size_t count = pc()-base(); + unsigned insLength; + unsigned insSize; + char* curStream = (char*)base(); + while (count>0) { + ssize_t label = mLabelsInverseMapping.indexOfKey(curStream); + if (label >= 0) { + printf("%s:\n", mLabelsInverseMapping.valueAt(label)); + } + ssize_t comment = mComments.indexOfKey(curStream); + if (comment >= 0) { + printf("; %s\n", mComments.valueAt(comment)); + } + insLength = decodeThenPrint(curStream); + curStream = curStream + insLength; + count = count - insLength; + } +} + +void X86Assembler::comment(const char* string) +{ + mComments.add(mStream, string); +} + +void X86Assembler::label(const char* theLabel) +{ + mLabels.add(theLabel, mStream); + mLabelsInverseMapping.add(mStream, theLabel); +} + +//the conditional jump +void X86Assembler::JCC(Mnemonic cc, const char* label) { + switch (cc) { + case Mnemonic_JO: + encoder_imm(Mnemonic_JO, OpndSize_32, 0/*imm*/, mStream); + break; + case Mnemonic_JNO: + encoder_imm(Mnemonic_JNO, OpndSize_32, 0/*imm*/, mStream); + break; + case Mnemonic_JB: + encoder_imm(Mnemonic_JB, OpndSize_32, 0/*imm*/, mStream); + break; + case Mnemonic_JNB: + encoder_imm(Mnemonic_JNB, OpndSize_32, 0/*imm*/, mStream); + break; + case Mnemonic_JZ: + encoder_imm(Mnemonic_JZ, OpndSize_32, 0/*imm*/, mStream); + break; + case Mnemonic_JNZ: + encoder_imm(Mnemonic_JNZ, OpndSize_32, 0/*imm*/, mStream); + break; + case Mnemonic_JBE: + encoder_imm(Mnemonic_JBE, OpndSize_32, 0/*imm*/, mStream); + break; + case Mnemonic_JNBE: + encoder_imm(Mnemonic_JNBE, OpndSize_32, 0/*imm*/, mStream); + break; + case Mnemonic_JS: + encoder_imm(Mnemonic_JS, OpndSize_32, 0/*imm*/, mStream); + break; + case Mnemonic_JNS: + encoder_imm(Mnemonic_JNS, OpndSize_32, 0/*imm*/, mStream); + break; + case Mnemonic_JP: + encoder_imm(Mnemonic_JP, OpndSize_32, 0/*imm*/, mStream); + break; + case Mnemonic_JNP: + encoder_imm(Mnemonic_JNP, OpndSize_32, 0/*imm*/, mStream); + break; + case Mnemonic_JL: + encoder_imm(Mnemonic_JL, OpndSize_32, 0/*imm*/, mStream); + break; + case Mnemonic_JNL: + encoder_imm(Mnemonic_JNL, OpndSize_32, 0/*imm*/, mStream); + break; + case Mnemonic_JLE: + encoder_imm(Mnemonic_JLE, OpndSize_32, 0/*imm*/, mStream); + break; + case Mnemonic_JNLE: + encoder_imm(Mnemonic_JNLE, OpndSize_32, 0/*imm*/, mStream); + break; + default : + printf("the condition is not supported.\n"); + return; + } + mStreamNext = mStream + encoder_get_inst_size(mStream); + //the offset is relative to the next instruction of the current PC + mBranchTargets.add(branch_target_t(label, mStream, mStreamNext)); + mStream = mStreamNext; +} + +void X86Assembler::JMP(const char* label) { + encoder_imm(Mnemonic_JMP, OpndSize_32, 0/*imm*/, mStream); + mStreamNext = mStream + encoder_get_inst_size(mStream); + mBranchTargets.add(branch_target_t(label, mStream, mStreamNext)); + mStream = mStreamNext; +} + +void X86Assembler::prepare_esp(int old_offset) +{ + mStreamUpdate = mStream; + SUB_IMM_TO_REG(old_offset, ESP); +} + +void X86Assembler::update_esp(int new_offset) +{ + encoder_update_imm_rm(new_offset, mStreamUpdate); +} + +void X86Assembler::shrink_esp(int shrink_offset) +{ + ADD_IMM_TO_REG(shrink_offset, ESP); +} + +void X86Assembler::callee_work() +{ + //push EBX, ESI, EDI which need to be done in callee + /* + push %ebp + mov %esp,%ebp + push %ebx + push %esi + push %edi + */ + PUSH(EBP); + MOV_REG_TO_REG(ESP, EBP); + PUSH(EBX); + PUSH(ESI); + PUSH(EDI); +} + +void X86Assembler::return_work() +{ +// pop %esi +// pop %edi +// pop %ebx +// movl %ebp,%esp +// pop %ebp +// ret +// ret is equivalent to below +// pop %eax // the return address +// jmp *%eax + POP(EDI); + POP(ESI); + POP(EBX); + POP(EBP); + encoder_return(mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +int X86Assembler::generate(const char* name) +{ + // fixup all the branches + size_t count = mBranchTargets.size(); + while (count--) { + const branch_target_t& bt = mBranchTargets[count]; + char* target_pc = mLabels.valueFor(bt.label); + LOG_ALWAYS_FATAL_IF(!target_pc, + "error resolving branch targets, target_pc is null"); + //the offset is relative to the next instruction of the current PC + int32_t offset = int32_t(target_pc - bt.next_pc); + encoder_update_imm(offset, bt.pc); + } + + mAssembly->resize((int)(pc()-base())); + + // the instruction cache is flushed by CodeCache + const int64_t duration = ggl_system_time() - mDuration; + const char * const format = "generated %s (%d ins size) at [%p:%p] in %lld ns\n"; + ALOGI(format, name, int(pc()-base()), base(), pc(), duration); + +#if defined(WITH_LIB_HARDWARE) + if (__builtin_expect(mQemuTracing, 0)) { + int err = qemu_add_mapping(uintptr_t(base()), name); + mQemuTracing = (err >= 0); + } +#endif + + char value[PROPERTY_VALUE_MAX]; + property_get("debug.pf.disasm", value, "0"); + if (atoi(value) != 0) { + printf(format, name, int(pc()-base()), base(), pc(), duration); + disassemble(name); + } + + return NO_ERROR; +} + +char* X86Assembler::pcForLabel(const char* label) +{ + return mLabels.valueFor(label); +} + +// ---------------------------------------------------------------------------- + +void X86Assembler::PUSH(int reg) { + encoder_reg(Mnemonic_PUSH, OpndSize_32, reg, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::POP(int reg) { + encoder_reg(Mnemonic_POP, OpndSize_32, reg, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +//arithmetic +void X86Assembler::ADD_REG_TO_REG(int src, int dst) { + encoder_reg_reg(Mnemonic_ADD, OpndSize_32, src, 0/*isPhysical*/, dst/*dst is the destination*/, 0/*isPhysical2*/,LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::ADD_IMM_TO_REG(int imm, int dst) { + encoder_imm_reg(Mnemonic_ADD, OpndSize_32, imm, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::ADD_IMM_TO_MEM(int imm, int disp, int dst) { + encoder_imm_mem(Mnemonic_ADD, OpndSize_32, imm, disp, dst, 0/*isBasePhysical*/, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::ADD_MEM_TO_REG(int base_reg, int disp, int dst) { + encoder_mem_reg(Mnemonic_ADD, OpndSize_32, disp, base_reg, 0/*isBasePhysical*/, dst, 0/*isPhysical*/,LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::ADD_REG_TO_MEM(int src, int base_reg, int disp) { + encoder_reg_mem(Mnemonic_ADD, OpndSize_32, src, 0/*isPhysical*/, disp, base_reg, 0/*isBasePhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::SUB_REG_TO_REG(int src, int dst) { + encoder_reg_reg(Mnemonic_SUB, OpndSize_32, src, 0/*isPhysical*/, dst/*dst is the destination*/, 0/*isPhysical2*/,LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::SUB_IMM_TO_REG(int imm, int dst) { + encoder_imm_reg(Mnemonic_SUB, OpndSize_32, imm, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::SUB_IMM_TO_MEM(int imm, int disp, int dst) { + encoder_imm_mem(Mnemonic_SUB, OpndSize_32, imm, disp, dst, 0/*isBasePhysical*/, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::SUB_REG_TO_MEM(int src, int base_reg, int disp) { + encoder_reg_mem(Mnemonic_SUB, OpndSize_32, src, 0/*isPhysical*/, disp, base_reg, 0/*isBasePhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +//test +void X86Assembler::TEST_REG_TO_REG(int src, int dst, OpndSize size) { + encoder_reg_reg(Mnemonic_TEST, size, src, 0/*isPhysical*/, dst/*dst is the destination*/, 0/*isPhysical2*/,LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +//compare +void X86Assembler::CMP_REG_TO_REG(int src, int dst, OpndSize size) { + encoder_reg_reg(Mnemonic_CMP, size, src, 0/*isPhysical*/, dst/*dst is the destination*/, 0/*isPhysical2*/,LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::CMP_IMM_TO_REG(int imm, int dst) { + encoder_imm_reg(Mnemonic_CMP, OpndSize_32, imm, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::CMP_MEM_TO_REG(int base_reg, int disp, int dst, OpndSize size) { + encoder_mem_reg(Mnemonic_CMP, size, disp, base_reg, 0/*isBasePhysical*/, + dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::CMP_REG_TO_MEM(int reg, int disp, int base_reg, OpndSize size) +{ + encoder_reg_mem(Mnemonic_CMP, size, reg, 0/*isPhysical*/, disp, base_reg, 0/*isBasePhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +//logical +void X86Assembler::AND_REG_TO_REG(int src, int dst) { + encoder_reg_reg(Mnemonic_AND, OpndSize_32, src, 0/*isPhysical*/, dst/*dst is the destination*/, 0/*isPhysical2*/,LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::AND_IMM_TO_REG(int imm, int dst) { + encoder_imm_reg(Mnemonic_AND, OpndSize_32, imm, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::OR_REG_TO_REG(int src, int dst) { + encoder_reg_reg(Mnemonic_OR, OpndSize_32, src, 0/*isPhysical*/, dst/*dst is the destination*/, 0/*isPhysical2*/,LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::XOR(int src, int dst) { + encoder_reg_reg(Mnemonic_XOR, OpndSize_32, src, 0/*isPhysical*/, dst/*dst is the destination*/, 0/*isPhysical2*/,LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::OR_IMM_TO_REG(int imm, int dst) { + encoder_imm_reg(Mnemonic_OR, OpndSize_32, imm, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::NOT(int dst) { + encoder_reg(Mnemonic_NOT, OpndSize_32, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::NEG(int dst) { + encoder_reg(Mnemonic_NEG, OpndSize_32, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} +//shift +void X86Assembler::SHL(int imm, int dst) { + encoder_imm_reg(Mnemonic_SHL, OpndSize_32, imm, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::SHL(int imm, int disp, int dst) { + encoder_imm_mem(Mnemonic_SHL, OpndSize_32, imm, disp, dst, 0/*isBasePhysical*/, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::SHR(int imm, int dst) { + encoder_imm_reg(Mnemonic_SHR, OpndSize_32, imm, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::SHR(int imm, int disp, int dst) { + encoder_imm_mem(Mnemonic_SHR, OpndSize_32, imm, disp, dst, 0/*isBasePhysical*/, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::SAR(int imm, int dst) { + encoder_imm_reg(Mnemonic_SAR, OpndSize_32, imm, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::ROR(const int imm, int dst) { + encoder_imm_reg(Mnemonic_ROR, OpndSize_32, imm, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::ROR(int imm, int disp, int dst) { + encoder_imm_mem(Mnemonic_ROR, OpndSize_32, imm, disp, dst, 0/*isBasePhysical*/, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} +//signed extension +void X86Assembler::MOVSX_MEM_TO_REG(OpndSize size, int base_reg, int disp, int dst) { + encoder_moves_mem_to_reg(size, disp, base_reg, 0/*isBasePhysical*/, dst, 0/*isPhysical*/, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::MOVSX_REG_TO_REG(OpndSize size, int src, int dst) { + encoder_moves_reg_to_reg(size, src, 0/*isPhysical*/, dst, 0/*isPhysical2*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} +//zero entension +void X86Assembler::MOVZX_MEM_TO_REG(OpndSize size, int base_reg, int disp, int dst) { + encoder_movez_mem_to_reg(size, disp, base_reg, 0/*isBasePhysical*/, dst, 0/*isPhysical*/, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::MOVZX_REG_TO_REG(OpndSize size, int src, int dst) { + encoder_movez_reg_to_reg(size, src, 0/*isPhysical*/, dst, 0/*isPhysical2*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +// multiply... +// the first source operand is placed in EAX +void X86Assembler::IMUL(int reg) { + encoder_reg(Mnemonic_IMUL, OpndSize_32, reg, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::IMUL(int src, int dst) { + encoder_reg_reg(Mnemonic_IMUL, OpndSize_32, src, 0/*isPhysical*/, dst/*dst is the destination*/, 0/*isPhysical2*/,LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::MUL(int reg) { + encoder_reg(Mnemonic_MUL, OpndSize_32, reg, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + + +// data transfer... +void X86Assembler::MOV_IMM_TO_REG(int32_t imm, int dst) { + encoder_imm_reg(Mnemonic_MOV, OpndSize_32, imm, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::MOV_REG_TO_REG(int src, int dst, OpndSize size) +{ + if(src == dst) return; + encoder_reg_reg(Mnemonic_MOV, size, src, 0/*isPhysical*/, dst, 0/*isPhysical2*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::MOV_REG_TO_MEM(int reg, int disp, int base_reg, OpndSize size) +{ + encoder_reg_mem(Mnemonic_MOV, size, reg, 0/*isPhysical*/, disp, base_reg, 0/*isBasePhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::MOV_MEM_TO_REG(int disp, int base_reg, int reg, OpndSize size) +{ + encoder_mem_reg(Mnemonic_MOV, size, disp, base_reg, 0/*isBasePhysical*/, + reg, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::MOV_MEM_SCALE_TO_REG(int base_reg, int index_reg, int scale, int reg, OpndSize size) +{ + encoder_mem_scale_reg(Mnemonic_MOV, size, base_reg, 0/*isBasePhysical*/, index_reg, 0/*isIndexPhysical*/, scale, reg, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + mStream = mStream + encoder_get_inst_size(mStream); +} +// the conditional move +void X86Assembler::CMOV_REG_TO_REG(Mnemonic cc, int src, int dst, OpndSize size) +{ + switch (cc) { + case Mnemonic_CMOVO: + encoder_reg_reg(Mnemonic_CMOVO, size, src, 0/*isPhysical*/, dst, 0/*isPhysical2*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVNO: + encoder_reg_reg(Mnemonic_CMOVNO, size, src, 0/*isPhysical*/, dst, 0/*isPhysical2*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVB: + encoder_reg_reg(Mnemonic_CMOVB, size, src, 0/*isPhysical*/, dst, 0/*isPhysical2*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVNB: + encoder_reg_reg(Mnemonic_CMOVNB, size, src, 0/*isPhysical*/, dst, 0/*isPhysical2*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVZ: + encoder_reg_reg(Mnemonic_CMOVZ, size, src, 0/*isPhysical*/, dst, 0/*isPhysical2*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVNZ: + encoder_reg_reg(Mnemonic_CMOVNZ, size, src, 0/*isPhysical*/, dst, 0/*isPhysical2*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVBE: + encoder_reg_reg(Mnemonic_CMOVBE, size, src, 0/*isPhysical*/, dst, 0/*isPhysical2*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVNBE: + encoder_reg_reg(Mnemonic_CMOVNBE, size, src, 0/*isPhysical*/, dst, 0/*isPhysical2*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVS: + encoder_reg_reg(Mnemonic_CMOVS, size, src, 0/*isPhysical*/, dst, 0/*isPhysical2*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVNS: + encoder_reg_reg(Mnemonic_CMOVNS, size, src, 0/*isPhysical*/, dst, 0/*isPhysical2*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVP: + encoder_reg_reg(Mnemonic_CMOVP, size, src, 0/*isPhysical*/, dst, 0/*isPhysical2*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVNP: + encoder_reg_reg(Mnemonic_CMOVNP, size, src, 0/*isPhysical*/, dst, 0/*isPhysical2*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVL: + encoder_reg_reg(Mnemonic_CMOVL, size, src, 0/*isPhysical*/, dst, 0/*isPhysical2*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVNL: + encoder_reg_reg(Mnemonic_CMOVNL, size, src, 0/*isPhysical*/, dst, 0/*isPhysical2*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVLE: + encoder_reg_reg(Mnemonic_CMOVLE, size, src, 0/*isPhysical*/, dst, 0/*isPhysical2*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVNLE: + encoder_reg_reg(Mnemonic_CMOVNLE, size, src, 0/*isPhysical*/, dst, 0/*isPhysical2*/, LowOpndRegType_gp, mStream); + break; + default : + printf("the condition is not supported.\n"); + return; + } + mStream = mStream + encoder_get_inst_size(mStream); +} + +void X86Assembler::CMOV_MEM_TO_REG(Mnemonic cc, int disp, int base_reg, int dst, OpndSize size) +{ + switch (cc) { + case Mnemonic_CMOVO: + encoder_mem_reg(Mnemonic_CMOVO, size, disp, base_reg, 0/*isBasePhysical*/, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVNO: + encoder_mem_reg(Mnemonic_CMOVNO, size, disp, base_reg, 0/*isBasePhysical*/, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVB: + encoder_mem_reg(Mnemonic_CMOVB, size, disp, base_reg, 0/*isBasePhysical*/, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVNB: + encoder_mem_reg(Mnemonic_CMOVNB, size, disp, base_reg, 0/*isBasePhysical*/, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVZ: + encoder_mem_reg(Mnemonic_CMOVZ, size, disp, base_reg, 0/*isBasePhysical*/, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVNZ: + encoder_mem_reg(Mnemonic_CMOVNZ, size, disp, base_reg, 0/*isBasePhysical*/, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVBE: + encoder_mem_reg(Mnemonic_CMOVBE, size, disp, base_reg, 0/*isBasePhysical*/, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVNBE: + encoder_mem_reg(Mnemonic_CMOVNBE, size, disp, base_reg, 0/*isBasePhysical*/, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVS: + encoder_mem_reg(Mnemonic_CMOVS, size, disp, base_reg, 0/*isBasePhysical*/, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVNS: + encoder_mem_reg(Mnemonic_CMOVNS, size, disp, base_reg, 0/*isBasePhysical*/, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVP: + encoder_mem_reg(Mnemonic_CMOVP, size, disp, base_reg, 0/*isBasePhysical*/, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVNP: + encoder_mem_reg(Mnemonic_CMOVNP, size, disp, base_reg, 0/*isBasePhysical*/, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVL: + encoder_mem_reg(Mnemonic_CMOVL, size, disp, base_reg, 0/*isBasePhysical*/, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVNL: + encoder_mem_reg(Mnemonic_CMOVNL, size, disp, base_reg, 0/*isBasePhysical*/, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVLE: + encoder_mem_reg(Mnemonic_CMOVLE, size, disp, base_reg, 0/*isBasePhysical*/, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + break; + case Mnemonic_CMOVNLE: + encoder_mem_reg(Mnemonic_CMOVNLE, size, disp, base_reg, 0/*isBasePhysical*/, dst, 0/*isPhysical*/, LowOpndRegType_gp, mStream); + break; + default : + printf("the condition is not supported.\n"); + return; + } + mStream = mStream + encoder_get_inst_size(mStream); +} + +}; // namespace android diff --git a/libpixelflinger/codeflinger/x86/X86Assembler.h b/libpixelflinger/codeflinger/x86/X86Assembler.h new file mode 100644 index 0000000..03502d5 --- /dev/null +++ b/libpixelflinger/codeflinger/x86/X86Assembler.h @@ -0,0 +1,163 @@ +/* libs/pixelflinger/codeflinger/x86/X86Assembler.h +** +** Copyright 2006, 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_X86ASSEMBLER_H +#define ANDROID_X86ASSEMBLER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Vector.h> +#include <utils/KeyedVector.h> + +#include "codeflinger/tinyutils/smartpointer.h" +#include "codeflinger/CodeCache.h" +#include "enc_wrapper.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +class X86Assembler +{ +public: + + enum { + EAX = PhysicalReg_EAX, EBX = PhysicalReg_EBX, ECX = PhysicalReg_ECX, + EDX = PhysicalReg_EDX, EDI = PhysicalReg_EDI, ESI = PhysicalReg_ESI, + ESP = PhysicalReg_ESP, EBP = PhysicalReg_EBP + }; + + X86Assembler(const sp<Assembly>& assembly); + ~X86Assembler(); + + char* base() const; + char* pc() const; + + + void disassemble(const char* name); + + // ------------------------------------------------------------------------ + // X86AssemblerInterface... + // ------------------------------------------------------------------------ + + void reset(); + + int generate(const char* name); + + void comment(const char* string); + + void label(const char* theLabel); + + void JCC(Mnemonic cc, const char* label); + + void JMP(const char* label); + + void prepare_esp(int old_offset); + + void update_esp(int new_offset); + + void shrink_esp(int shrink_offset); + + void callee_work(); + + void return_work(); + + char* pcForLabel(const char* label); + + void PUSH(int reg); + + void POP(int reg); + + void ADD_REG_TO_REG(int src, int dst); + void ADD_IMM_TO_REG(int imm, int dst); + void ADD_IMM_TO_MEM(int imm, int disp, int dst); + void ADD_MEM_TO_REG(int base_reg, int disp, int dst); + void ADD_REG_TO_MEM(int src, int base_reg, int disp); + void SUB_REG_TO_REG(int src, int dst); + void SUB_IMM_TO_REG(int imm, int dst); + void SUB_IMM_TO_MEM(int imm, int disp, int dst); + void SUB_REG_TO_MEM(int src, int base_reg, int disp); + + void TEST_REG_TO_REG(int src, int dst, OpndSize size=OpndSize_32); + void CMP_REG_TO_REG(int src, int dst, OpndSize size=OpndSize_32); + void CMP_MEM_TO_REG(int base_reg, int disp, int dst, OpndSize size=OpndSize_32); + void CMP_REG_TO_MEM(int reg, int disp, int base_reg, OpndSize size=OpndSize_32); + void CMP_IMM_TO_REG(int imm, int dst); + + void AND_REG_TO_REG(int src, int dst); + void AND_IMM_TO_REG(int imm, int dst); + void OR_REG_TO_REG(int src, int dst); + void XOR(int src, int dst); + void OR_IMM_TO_REG(int imm, int dst); + void NOT(int dst); + void NEG(int dst); + void SHL(int imm, int dst); + void SHL(int imm, int disp, int dst); + void SHR(int imm, int dst); + void SHR(int imm, int disp, int dst); + void SAR(int imm, int dst); + void ROR(const int imm, int dst); + void ROR(int imm, int disp, int dst); + void IMUL(int reg); + void IMUL(int src, int dst); + void MUL(int reg); + + void MOVSX_MEM_TO_REG(OpndSize size, int base_reg, int disp, int dst); + void MOVSX_REG_TO_REG(OpndSize size, int src, int dst); + void MOVZX_MEM_TO_REG(OpndSize size, int base_reg, int disp, int dst); + void MOVZX_REG_TO_REG(OpndSize size, int src, int dst); + void MOV_IMM_TO_REG(int32_t imm, int dst); + void MOV_REG_TO_REG(int src, int dst, OpndSize size=OpndSize_32); + void MOV_MEM_TO_REG(int disp, int base_reg, int reg, OpndSize size=OpndSize_32); + void MOV_REG_TO_MEM(int reg, int disp, int base_reg, OpndSize size=OpndSize_32); + void MOV_MEM_SCALE_TO_REG(int base_reg, int index_reg, int scale, int reg, OpndSize size=OpndSize_32); + void CMOV_REG_TO_REG(Mnemonic cc, int src, int dst, OpndSize size=OpndSize_32); + void CMOV_MEM_TO_REG(Mnemonic cc, int disp, int base_reg, int dst, OpndSize size=OpndSize_32); + + + sp<Assembly> mAssembly; + char* mBase; + char* mStream; + //branch target offset is relative to the next instruction + char* mStreamNext; + //updating esp after iterating the loop + char* mStreamUpdate; + + int64_t mDuration; +#if defined(WITH_LIB_HARDWARE) + bool mQemuTracing; +#endif + + struct branch_target_t { + inline branch_target_t() : label(0), pc(0), next_pc(0) { } + inline branch_target_t(const char* l, char* p, char* next_p) + : label(l), pc(p), next_pc(next_p) { } + const char* label; + char* pc; + char* next_pc; + }; + + Vector<branch_target_t> mBranchTargets; + KeyedVector< const char*, char* > mLabels; + KeyedVector< char*, const char* > mLabelsInverseMapping; + KeyedVector< char*, const char* > mComments; +}; + +}; // namespace android + +#endif //ANDROID_X86ASSEMBLER_H diff --git a/libpixelflinger/codeflinger/x86/blending.cpp b/libpixelflinger/codeflinger/x86/blending.cpp new file mode 100644 index 0000000..f918ffd --- /dev/null +++ b/libpixelflinger/codeflinger/x86/blending.cpp @@ -0,0 +1,974 @@ +/* libs/pixelflinger/codeflinger/x86/blending.cpp +** +** Copyright 2006, 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 <assert.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> + +#include <cutils/log.h> + +#include "codeflinger/x86/GGLX86Assembler.h" + + +namespace android { + +void GGLX86Assembler::build_fog( + component_t& temp, // incomming fragment / output + int component, + Scratch& regs) +{ + if (mInfo[component].fog) { + Scratch scratches(registerFile()); + comment("fog"); + + temp.reg = scratches.obtain(); + MOV_MEM_TO_REG(temp.offset_ebp, EBP, temp.reg); + integer_t fragment(temp.reg, temp.h, temp.flags, temp.offset_ebp); + if (!(temp.flags & CORRUPTIBLE)) { + temp.reg = regs.obtain(); + temp.flags |= CORRUPTIBLE; + } + + integer_t fogColor(scratches.obtain(), 8, CORRUPTIBLE); + mBuilderContext.Rctx = scratches.obtain(); + MOV_MEM_TO_REG(8, EBP, mBuilderContext.Rctx); + MOVZX_MEM_TO_REG(OpndSize_8, mBuilderContext.Rctx, GGL_OFFSETOF(state.fog.color[component]), fogColor.reg); + + integer_t factor(scratches.obtain(), 16, CORRUPTIBLE); + CONTEXT_LOAD(factor.reg, generated_vars.f); + scratches.recycle(mBuilderContext.Rctx); + + // clamp fog factor (TODO: see if there is a way to guarantee + // we won't overflow, when setting the iterators) + int temp_reg = scratches.obtain(); + MOV_REG_TO_REG(factor.reg, temp_reg); + SAR(31, temp_reg); + NOT(temp_reg); + AND_REG_TO_REG(temp_reg, factor.reg); + MOV_IMM_TO_REG(0x10000, temp_reg); + CMP_IMM_TO_REG(0x10000, factor.reg); + CMOV_REG_TO_REG(Mnemonic_CMOVAE, temp_reg, factor.reg); + scratches.recycle(temp_reg); + + //we will resue factor.reg + build_blendFOneMinusF(temp, factor, fragment, fogColor); + MOV_REG_TO_MEM(temp.reg, temp.offset_ebp, EBP); + scratches.recycle(temp.reg); + } +} + +void GGLX86Assembler::build_blending( + component_t& temp, // incomming fragment / output + pixel_t& pixel, // framebuffer + int component, + Scratch& regs) +{ + if (!mInfo[component].blend) + return; + + int fs = component==GGLFormat::ALPHA ? mBlendSrcA : mBlendSrc; + int fd = component==GGLFormat::ALPHA ? mBlendDstA : mBlendDst; + if (fs==GGL_SRC_ALPHA_SATURATE && component==GGLFormat::ALPHA) + fs = GGL_ONE; + const int blending = blending_codes(fs, fd); + if (!temp.size()) { + // here, blending will produce something which doesn't depend on + // that component (eg: GL_ZERO:GL_*), so the register has not been + // allocated yet. Will never be used as a source. + //temp = component_t(regs.obtain(), CORRUPTIBLE, temp_offset_ebp); + temp.reg = regs.obtain(); + temp.flags = CORRUPTIBLE; + temp.h = temp.l = 0; + } else { + temp.reg = regs.obtain(); + } + MOV_MEM_TO_REG(temp.offset_ebp, EBP, temp.reg); + // we are doing real blending... + // fb: extracted dst + // fragment: extracted src + // temp: component_t(fragment) and result + + // scoped register allocator + Scratch scratches(registerFile()); + comment("blending"); + + // we can optimize these cases a bit... + // (1) saturation is not needed + // (2) we can use only one multiply instead of 2 + // (3) we can reduce the register pressure + // R = S*f + D*(1-f) = (S-D)*f + D + // R = S*(1-f) + D*f = (D-S)*f + S + + const bool same_factor_opt1 = + (fs==GGL_DST_COLOR && fd==GGL_ONE_MINUS_DST_COLOR) || + (fs==GGL_SRC_COLOR && fd==GGL_ONE_MINUS_SRC_COLOR) || + (fs==GGL_DST_ALPHA && fd==GGL_ONE_MINUS_DST_ALPHA) || + (fs==GGL_SRC_ALPHA && fd==GGL_ONE_MINUS_SRC_ALPHA); + + const bool same_factor_opt2 = + (fs==GGL_ONE_MINUS_DST_COLOR && fd==GGL_DST_COLOR) || + (fs==GGL_ONE_MINUS_SRC_COLOR && fd==GGL_SRC_COLOR) || + (fs==GGL_ONE_MINUS_DST_ALPHA && fd==GGL_DST_ALPHA) || + (fs==GGL_ONE_MINUS_SRC_ALPHA && fd==GGL_SRC_ALPHA); + + + // XXX: we could also optimize these cases: + // R = S*f + D*f = (S+D)*f + // R = S*(1-f) + D*(1-f) = (S+D)*(1-f) + // R = S*D + D*S = 2*S*D + + + pixel.reg = scratches.obtain(); + MOV_MEM_TO_REG(pixel.offset_ebp, EBP, pixel.reg); + // see if we need to extract 'component' from the destination (fb) + integer_t fb; + if (blending & (BLEND_DST|FACTOR_DST)) { + fb.setTo(scratches.obtain(), 32); + extract(fb, pixel, component); + if (mDithering) { + // XXX: maybe what we should do instead, is simply + // expand fb -or- fragment to the larger of the two + if (fb.size() < temp.size()) { + // for now we expand 'fb' to min(fragment, 8) + int new_size = temp.size() < 8 ? temp.size() : 8; + expand(fb, fb, new_size); + } + } + } + + // convert input fragment to integer_t + if (temp.l && (temp.flags & CORRUPTIBLE)) { + SHR(temp.l, temp.reg); + temp.h -= temp.l; + temp.l = 0; + } + integer_t fragment(temp.reg, temp.size(), temp.flags, temp.offset_ebp); + + // if not done yet, convert input fragment to integer_t + if (temp.l) { + // here we know temp is not CORRUPTIBLE + fragment.reg = scratches.obtain(); + MOV_REG_TO_REG(temp.reg, fragment.reg); + SHR(temp.l, fragment.reg); + fragment.flags |= CORRUPTIBLE; + } + + if (!(temp.flags & CORRUPTIBLE)) { + // temp is not corruptible, but since it's the destination it + // will be modified, so we need to allocate a new register. + temp.reg = regs.obtain(); + temp.flags &= ~CORRUPTIBLE; + fragment.flags &= ~CORRUPTIBLE; + } + + if ((blending & BLEND_SRC) && !same_factor_opt1) { + // source (fragment) is needed for the blending stage + // so it's not CORRUPTIBLE (unless we're doing same_factor_opt1) + fragment.flags &= ~CORRUPTIBLE; + } + + + if (same_factor_opt1) { + // R = S*f + D*(1-f) = (S-D)*f + D + integer_t factor; + build_blend_factor(factor, fs, + component, pixel, fragment, fb, scratches); + // fb is always corruptible from this point + fb.flags |= CORRUPTIBLE; + //we will reuse factor in mul_factor_add of build_blendFOneMinusF, unless factor.reg == fragment.reg == temp.reg or factor.reg == fb.reg in build_blend_factor + if(factor.reg == fragment.reg || factor.reg == fb.reg) + MOV_REG_TO_REG(factor.reg, pixel.reg); + else + scratches.recycle(pixel.reg); + build_blendFOneMinusF(temp, factor, fragment, fb); + if(factor.reg == fragment.reg || factor.reg == fb.reg) { + MOV_REG_TO_REG(pixel.reg, factor.reg); + scratches.recycle(pixel.reg); + } + scratches.recycle(fb.reg); + //scratches.recycle(factor.reg); + } else if (same_factor_opt2) { + // R = S*(1-f) + D*f = (D-S)*f + S + integer_t factor; + // fb is always corrruptible here + fb.flags |= CORRUPTIBLE; + build_blend_factor(factor, fd, + component, pixel, fragment, fb, scratches); + //we will reuse factor in mul_factor_add of build_blendFOneMinusFF, unless factor.reg == fragment.reg == temp.reg or factor.reg == fb.reg in build_blend_factor + if(factor.reg == fragment.reg || factor.reg == fb.reg) + MOV_REG_TO_REG(factor.reg, pixel.reg); + else + scratches.recycle(pixel.reg); + build_blendOneMinusFF(temp, factor, fragment, fb); + if(factor.reg == fragment.reg || factor.reg == fb.reg) { + MOV_REG_TO_REG(pixel.reg, factor.reg); + scratches.recycle(pixel.reg); + } + scratches.recycle(fb.reg); + } else { + integer_t src_factor; + integer_t dst_factor; + + // if destination (fb) is not needed for the blending stage, + // then it can be marked as CORRUPTIBLE + if (!(blending & BLEND_DST)) { + fb.flags |= CORRUPTIBLE; + } + + // XXX: try to mark some registers as CORRUPTIBLE + // in most case we could make those corruptible + // when we're processing the last component + // but not always, for instance + // when fragment is constant and not reloaded + // when fb is needed for logic-ops or masking + // when a register is aliased (for instance with mAlphaSource) + + // blend away... + if (fs==GGL_ZERO) { + if (fd==GGL_ZERO) { // R = 0 + // already taken care of + } else if (fd==GGL_ONE) { // R = D + // already taken care of + } else { // R = D*fd + // compute fd + build_blend_factor(dst_factor, fd, + component, pixel, fragment, fb, scratches); + scratches.recycle(pixel.reg); + mul_factor(temp, fb, dst_factor, regs); + scratches.recycle(fb.reg); + } + } else if (fs==GGL_ONE) { + int temp_reg; + if (fd==GGL_ZERO) { // R = S + // NOP, taken care of + } else if (fd==GGL_ONE) { // R = S + D + component_add(temp, fb, fragment); // args order matters + temp_reg = scratches.obtain(); + component_sat(temp, temp_reg); + scratches.recycle(temp_reg); + } else { // R = S + D*fd + // compute fd + build_blend_factor(dst_factor, fd, + component, pixel, fragment, fb, scratches); + //we will probably change src_factor in mul_factor_add, unless factor.reg == fragment.reg == temp.reg or factor.reg == fb.reg in build_blend_factor + if(dst_factor.reg == fragment.reg || dst_factor.reg == fb.reg) + MOV_REG_TO_REG(dst_factor.reg, pixel.reg); + else + scratches.recycle(pixel.reg); + mul_factor_add(temp, fb, dst_factor, component_t(fragment)); + if(dst_factor.reg == fragment.reg || dst_factor.reg == fb.reg) { + MOV_REG_TO_REG(pixel.reg, dst_factor.reg); + scratches.recycle(pixel.reg); + } + temp_reg = fb.reg; + component_sat(temp, temp_reg); + scratches.recycle(fb.reg); + } + } else { + // compute fs + int temp_reg; + build_blend_factor(src_factor, fs, + component, pixel, fragment, fb, scratches); + if (fd==GGL_ZERO) { // R = S*fs + mul_factor(temp, fragment, src_factor, regs); + if (scratches.isUsed(src_factor.reg)) + scratches.recycle(src_factor.reg); + } else if (fd==GGL_ONE) { // R = S*fs + D + //we will probably change src_factor in mul_factor_add, unless factor.reg == fragment.reg == temp.reg or factor.reg == fb.reg in build_blend_factor + if(src_factor.reg == fragment.reg || src_factor.reg == fb.reg) + MOV_REG_TO_REG(src_factor.reg, pixel.reg); + else + scratches.recycle(pixel.reg); + mul_factor_add(temp, fragment, src_factor, component_t(fb)); + if(src_factor.reg == fragment.reg || src_factor.reg == fb.reg) { + MOV_REG_TO_REG(pixel.reg, src_factor.reg); + scratches.recycle(pixel.reg); + } + temp_reg = fb.reg; + component_sat(temp, temp_reg); + scratches.recycle(fb.reg); + } else { // R = S*fs + D*fd + mul_factor(temp, fragment, src_factor, regs); + if (scratches.isUsed(src_factor.reg)) + scratches.recycle(src_factor.reg); + // compute fd + build_blend_factor(dst_factor, fd, + component, pixel, fragment, fb, scratches); + //we will probably change dst_factor in mul_factor_add, unless factor.reg == fragment.reg == temp.reg or factor.reg == fb.reg + if(dst_factor.reg == fragment.reg || dst_factor.reg == fb.reg) + MOV_REG_TO_REG(dst_factor.reg, pixel.reg); + else + scratches.recycle(pixel.reg); + mul_factor_add(temp, fb, dst_factor, temp); + if(dst_factor.reg == fragment.reg || dst_factor.reg == fb.reg) { + MOV_REG_TO_REG(pixel.reg, dst_factor.reg); + scratches.recycle(pixel.reg); + } + if (!same_factor_opt1 && !same_factor_opt2) { + temp_reg = fb.reg; + component_sat(temp, temp_reg); + } + scratches.recycle(fb.reg); + } + if(scratches.isUsed(pixel.reg)) + scratches.recycle(pixel.reg); + } + } + // temp is modified, but it will be used immediately in downshift + //printf("temp.offset_ebp: %d \n", temp.offset_ebp); + //below will be triggered on CDK for surfaceflinger + if(temp.offset_ebp == mAlphaSource.offset_ebp) { + mCurSp = mCurSp - 4; + temp.offset_ebp = mCurSp; + } + // the r, g, b value must be stored, otherwise the color of globaltime is incorrect. + MOV_REG_TO_MEM(temp.reg, temp.offset_ebp, EBP); + regs.recycle(temp.reg); + + // now we can be corrupted (it's the dest) + temp.flags |= CORRUPTIBLE; +} + +void GGLX86Assembler::build_blend_factor( + integer_t& factor, int f, int component, + const pixel_t& dst_pixel, + integer_t& fragment, + integer_t& fb, + Scratch& scratches) +{ + integer_t src_alpha(fragment); + + // src_factor/dst_factor won't be used after blending, + // so it's fine to mark them as CORRUPTIBLE (if not aliased) + factor.flags |= CORRUPTIBLE; + int temp_reg; + switch(f) { + case GGL_ONE_MINUS_SRC_ALPHA: + case GGL_SRC_ALPHA: + if (component==GGLFormat::ALPHA && !isAlphaSourceNeeded()) { + // we're processing alpha, so we already have + // src-alpha in fragment, and we need src-alpha just this time. + } else { + // alpha-src will be needed for other components + factor = mAlphaSource; + factor.flags &= ~CORRUPTIBLE; + factor.reg = scratches.obtain(); + //printf("mAlphaSource.offset_ebp: %d \n", mAlphaSource.offset_ebp); + //printf("fragment.offset_ebp: %d \n", fragment.offset_ebp); + //printf("factor.offset_ebp: %d \n", factor.offset_ebp); + MOV_MEM_TO_REG(mAlphaSource.offset_ebp, EBP, factor.reg); + if (!mBlendFactorCached || mBlendFactorCached==f) { + src_alpha = mAlphaSource; + // we already computed the blend factor before, nothing to do. + if (mBlendFactorCached) + return; + // this is the first time, make sure to compute the blend + // factor properly. + mBlendFactorCached = f; + break; + } else { + // we have a cached alpha blend factor, but we want another one, + // this should really not happen because by construction, + // we cannot have BOTH source and destination + // blend factors use ALPHA *and* ONE_MINUS_ALPHA (because + // the blending stage uses the f/(1-f) optimization + + // for completeness, we handle this case though. Since there + // are only 2 choices, this meens we want "the other one" + // (1-factor) + //factor = mAlphaSource; + //factor.flags &= ~CORRUPTIBLE; + NEG(factor.reg); + ADD_IMM_TO_REG((1<<factor.s), factor.reg); + MOV_REG_TO_MEM(factor.reg, factor.offset_ebp, EBP); + mBlendFactorCached = f; + return; + } + } + // fall-through... + case GGL_ONE_MINUS_DST_COLOR: + case GGL_DST_COLOR: + case GGL_ONE_MINUS_SRC_COLOR: + case GGL_SRC_COLOR: + case GGL_ONE_MINUS_DST_ALPHA: + case GGL_DST_ALPHA: + case GGL_SRC_ALPHA_SATURATE: + // help us find out what register we can use for the blend-factor + // CORRUPTIBLE registers are chosen first, or a new one is allocated. + if (fragment.flags & CORRUPTIBLE) { + factor.setTo(fragment.reg, 32, CORRUPTIBLE, fragment.offset_ebp); + fragment.flags &= ~CORRUPTIBLE; + } else if (fb.flags & CORRUPTIBLE) { + factor.setTo(fb.reg, 32, CORRUPTIBLE, fb.offset_ebp); + fb.flags &= ~CORRUPTIBLE; + } else { + factor.setTo(scratches.obtain(), 32, CORRUPTIBLE); + mCurSp = mCurSp - 4; + factor.offset_ebp = mCurSp; + } + break; + } + + // XXX: doesn't work if size==1 + + switch(f) { + case GGL_ONE_MINUS_DST_COLOR: + case GGL_DST_COLOR: + factor.s = fb.s; + MOV_REG_TO_REG(fb.reg, factor.reg); + SHR(fb.s-1, factor.reg); + ADD_REG_TO_REG(fb.reg, factor.reg); + break; + case GGL_ONE_MINUS_SRC_COLOR: + case GGL_SRC_COLOR: + factor.s = fragment.s; + temp_reg = scratches.obtain(); + MOV_REG_TO_REG(fragment.reg, temp_reg); + SHR(fragment.s-1, fragment.reg); + ADD_REG_TO_REG(temp_reg, fragment.reg); + scratches.recycle(temp_reg); + break; + case GGL_ONE_MINUS_SRC_ALPHA: + case GGL_SRC_ALPHA: + factor.s = src_alpha.s; + if (mBlendFactorCached == f) { + //src_alpha == factor == mAlphaSource, we need a temp reg + if(scratches.countFreeRegs()) { + temp_reg = scratches.obtain(); + MOV_REG_TO_REG(factor.reg, temp_reg); + SHR(src_alpha.s-1, factor.reg); + ADD_REG_TO_REG(temp_reg, factor.reg); + scratches.recycle(temp_reg); + } + else { + SHR(src_alpha.s-1, factor.offset_ebp, EBP); + ADD_MEM_TO_REG(EBP, factor.offset_ebp, factor.reg); + } + } + else + { + MOV_REG_TO_REG(src_alpha.reg, factor.reg); + SHR(src_alpha.s-1, factor.reg); + ADD_REG_TO_REG(src_alpha.reg, factor.reg); + } + // we will store factor in the next switch for GGL_ONE_MINUS_SRC_ALPHA + if(f == GGL_SRC_ALPHA) + MOV_REG_TO_MEM(factor.reg, factor.offset_ebp, EBP); + break; + case GGL_ONE_MINUS_DST_ALPHA: + case GGL_DST_ALPHA: + // XXX: should be precomputed + extract(factor, dst_pixel, GGLFormat::ALPHA); + temp_reg = scratches.obtain(); + MOV_REG_TO_REG(factor.reg, temp_reg); + SHR(factor.s-1, factor.reg); + ADD_REG_TO_REG(temp_reg, factor.reg); + scratches.recycle(temp_reg); + break; + case GGL_SRC_ALPHA_SATURATE: + // XXX: should be precomputed + // XXX: f = min(As, 1-Ad) + // btw, we're guaranteed that Ad's size is <= 8, because + // it's extracted from the framebuffer + break; + } + + switch(f) { + case GGL_ONE_MINUS_DST_COLOR: + case GGL_ONE_MINUS_SRC_COLOR: + case GGL_ONE_MINUS_DST_ALPHA: + case GGL_ONE_MINUS_SRC_ALPHA: + NEG(factor.reg); + ADD_IMM_TO_REG(1<<factor.s, factor.reg); + MOV_REG_TO_MEM(factor.reg, factor.offset_ebp, EBP); + } + + // don't need more than 8-bits for the blend factor + // and this will prevent overflows in the multiplies later + if (factor.s > 8) { + SHR(factor.s-8, factor.reg); + factor.s = 8; + if(f == GGL_ONE_MINUS_SRC_ALPHA || f == GGL_SRC_ALPHA) + MOV_REG_TO_MEM(factor.reg, factor.offset_ebp, EBP); + } + //below will be triggered on CDK for surfaceflinger + if(fragment.offset_ebp == mAlphaSource.offset_ebp) + MOV_REG_TO_REG(factor.reg, fragment.reg); +} + +int GGLX86Assembler::blending_codes(int fs, int fd) +{ + int blending = 0; + switch(fs) { + case GGL_ONE: + blending |= BLEND_SRC; + break; + + case GGL_ONE_MINUS_DST_COLOR: + case GGL_DST_COLOR: + blending |= FACTOR_DST|BLEND_SRC; + break; + case GGL_ONE_MINUS_DST_ALPHA: + case GGL_DST_ALPHA: + // no need to extract 'component' from the destination + // for the blend factor, because we need ALPHA only. + blending |= BLEND_SRC; + break; + + case GGL_ONE_MINUS_SRC_COLOR: + case GGL_SRC_COLOR: + blending |= FACTOR_SRC|BLEND_SRC; + break; + case GGL_ONE_MINUS_SRC_ALPHA: + case GGL_SRC_ALPHA: + case GGL_SRC_ALPHA_SATURATE: + blending |= FACTOR_SRC|BLEND_SRC; + break; + } + switch(fd) { + case GGL_ONE: + blending |= BLEND_DST; + break; + + case GGL_ONE_MINUS_DST_COLOR: + case GGL_DST_COLOR: + blending |= FACTOR_DST|BLEND_DST; + break; + case GGL_ONE_MINUS_DST_ALPHA: + case GGL_DST_ALPHA: + blending |= FACTOR_DST|BLEND_DST; + break; + + case GGL_ONE_MINUS_SRC_COLOR: + case GGL_SRC_COLOR: + blending |= FACTOR_SRC|BLEND_DST; + break; + case GGL_ONE_MINUS_SRC_ALPHA: + case GGL_SRC_ALPHA: + // no need to extract 'component' from the source + // for the blend factor, because we need ALPHA only. + blending |= BLEND_DST; + break; + } + return blending; +} + +// --------------------------------------------------------------------------- + +void GGLX86Assembler::build_blendFOneMinusF( + component_t& temp, + const integer_t& factor, + const integer_t& fragment, + const integer_t& fb) +{ + // R = S*f + D*(1-f) = (S-D)*f + D + // compute S-D + Scratch scratches(registerFile()); + integer_t diff(fragment.flags & CORRUPTIBLE ? + fragment.reg : scratches.obtain(), fb.size(), CORRUPTIBLE); + const int shift = fragment.size() - fb.size(); + if (shift>0) { + MOV_REG_TO_REG(fragment.reg, diff.reg); + SHR(shift, diff.reg); + SUB_REG_TO_REG(fb.reg, diff.reg); + } else if (shift<0) { + MOV_REG_TO_REG(fragment.reg, diff.reg); + SHL(-shift, diff.reg); + SUB_REG_TO_REG(fb.reg, diff.reg); + } else { + MOV_REG_TO_REG(fragment.reg, diff.reg); + SUB_REG_TO_REG(fb.reg, diff.reg); + } + mul_factor_add(temp, diff, factor, component_t(fb)); + if(!(fragment.flags & CORRUPTIBLE)) + scratches.recycle(diff.reg); +} + +void GGLX86Assembler::build_blendOneMinusFF( + component_t& temp, + const integer_t& factor, + const integer_t& fragment, + const integer_t& fb) +{ + // R = S*f + D*(1-f) = (S-D)*f + D + Scratch scratches(registerFile()); + // compute D-S + integer_t diff(fb.flags & CORRUPTIBLE ? + fb.reg : scratches.obtain(), fb.size(), CORRUPTIBLE); + const int shift = fragment.size() - fb.size(); + if (shift>0) { + SHR(shift, fragment.reg); + MOV_REG_TO_REG(fb.reg, diff.reg); + SUB_REG_TO_REG(fragment.reg, diff.reg); + } + else if (shift<0) { + SHR(-shift, fragment.reg); + MOV_REG_TO_REG(fb.reg, diff.reg); + SUB_REG_TO_REG(fragment.reg, diff.reg); + } + else { + MOV_REG_TO_REG(fb.reg, diff.reg); + SUB_REG_TO_REG(fragment.reg, diff.reg); + } + + mul_factor_add(temp, diff, factor, component_t(fragment)); + if(!(fragment.flags & CORRUPTIBLE)) + scratches.recycle(diff.reg); +} + +// --------------------------------------------------------------------------- + +void GGLX86Assembler::mul_factor( component_t& d, + const integer_t& v, + const integer_t& f, Scratch& scratches) +{ + // f can be changed + // + int vs = v.size(); + int fs = f.size(); + int ms = vs+fs; + + // XXX: we could have special cases for 1 bit mul + + // all this code below to use the best multiply instruction + // wrt the parameters size. We take advantage of the fact + // that the 16-bits multiplies allow a 16-bit shift + // The trick is that we just make sure that we have at least 8-bits + // per component (which is enough for a 8 bits display). + + int xy = -1; + int vshift = 0; + int fshift = 0; + int smulw = 0; + + int xyBB = 0; + int xyTB = 1; + int xyTT = 2; + int xyBT = 3; + if (vs<16) { + if (fs<16) { + xy = xyBB; + } else if (GGL_BETWEEN(fs, 24, 31)) { + ms -= 16; + xy = xyTB; + } else { + // eg: 15 * 18 -> 15 * 15 + fshift = fs - 15; + ms -= fshift; + xy = xyBB; + } + } else if (GGL_BETWEEN(vs, 24, 31)) { + if (fs<16) { + ms -= 16; + xy = xyTB; + } else if (GGL_BETWEEN(fs, 24, 31)) { + ms -= 32; + xy = xyTT; + } else { + // eg: 24 * 18 -> 8 * 18 + fshift = fs - 15; + ms -= 16 + fshift; + xy = xyTB; + } + } else { + if (fs<16) { + // eg: 18 * 15 -> 15 * 15 + vshift = vs - 15; + ms -= vshift; + xy = xyBB; + } else if (GGL_BETWEEN(fs, 24, 31)) { + // eg: 18 * 24 -> 15 * 8 + vshift = vs - 15; + ms -= 16 + vshift; + xy = xyBT; + } else { + // eg: 18 * 18 -> (15 * 18)>>16 + fshift = fs - 15; + ms -= 16 + fshift; + //xy = yB; //XXX SMULWB + smulw = 1; + } + } + + ALOGE_IF(ms>=32, "mul_factor overflow vs=%d, fs=%d", vs, fs); + + int vreg = v.reg; + int freg = f.reg; + if (vshift) { + MOV_REG_TO_REG(vreg, d.reg); + SHR(vshift, d.reg); + vreg = d.reg; + } + if (fshift) { + MOV_REG_TO_REG(vreg, d.reg); + SHR(fshift, d.reg); + freg = d.reg; + } + MOV_REG_TO_REG(vreg, d.reg); + if (smulw) { + int flag_push_edx = 0; + int flag_reserve_edx = 0; + int temp_reg2 = -1; + int edx_offset_ebp = 0; + if(scratches.isUsed(EDX) == 1) { + if(d.reg != EDX) { + flag_push_edx = 1; + mCurSp = mCurSp - 4; + edx_offset_ebp = mCurSp; + MOV_REG_TO_MEM(EDX, edx_offset_ebp, EBP); + //PUSH(EDX); + } + } + else { + flag_reserve_edx = 1; + scratches.reserve(EDX); + } + if(scratches.isUsed(EAX)) { + if( freg == EAX || d.reg == EAX) { + MOVSX_REG_TO_REG(OpndSize_16, freg, freg); + if(freg == EAX) + IMUL(d.reg); + else + IMUL(freg); + SHL(16, EDX); + SHR(16, EAX); + MOV_REG_TO_REG(EAX, EDX, OpndSize_16); + MOV_REG_TO_REG(EDX, d.reg); + } + else { + int eax_offset_ebp = 0; + if(scratches.countFreeRegs() > 0) { + temp_reg2 = scratches.obtain(); + MOV_REG_TO_REG(EAX, temp_reg2); + } + else { + mCurSp = mCurSp - 4; + eax_offset_ebp = mCurSp; + MOV_REG_TO_MEM(EAX, eax_offset_ebp, EBP); + //PUSH(EAX); + } + MOV_REG_TO_REG(freg, EAX); + MOVSX_REG_TO_REG(OpndSize_16, EAX, EAX); + IMUL(d.reg); + SHL(16, EDX); + SHR(16, EAX); + MOV_REG_TO_REG(EAX, EDX, OpndSize_16); + MOV_REG_TO_REG(EDX, d.reg); + if(temp_reg2 > -1) { + MOV_REG_TO_REG(temp_reg2, EAX); + scratches.recycle(temp_reg2); + } + else { + MOV_MEM_TO_REG(eax_offset_ebp, EBP, EAX); + //POP(EAX); + } + } + } + else { + MOV_REG_TO_REG(freg, EAX); + MOVSX_REG_TO_REG(OpndSize_16, EAX, EAX); + IMUL(d.reg); + SHL(16, EDX); + SHR(16, EAX); + MOV_REG_TO_REG(EAX, EDX, OpndSize_16); + MOV_REG_TO_REG(EDX, d.reg); + } + if(flag_push_edx == 1) { + MOV_MEM_TO_REG(edx_offset_ebp, EBP, EDX); + //POP(EDX); + } + if(flag_reserve_edx ==1) + scratches.recycle(EDX); + } + else { + if(xy == xyBB) { + MOVSX_REG_TO_REG(OpndSize_16, d.reg, d.reg); + MOVSX_REG_TO_REG(OpndSize_16, freg, freg); + IMUL(freg, d.reg); + } + else if(xy == xyTB) { + SHR(16, d.reg); + MOVSX_REG_TO_REG(OpndSize_16, d.reg, d.reg); + MOVSX_REG_TO_REG(OpndSize_16, freg, freg); + IMUL(freg, d.reg); + } + else if(xy == xyBT) { + MOVSX_REG_TO_REG(OpndSize_16, d.reg, d.reg); + SHR(16, freg); + MOVSX_REG_TO_REG(OpndSize_16, freg, freg); + IMUL(freg, d.reg); + } + else if(xy == xyTT) { + SHR(16, d.reg); + MOVSX_REG_TO_REG(OpndSize_16, d.reg, d.reg); + SHR(16, freg); + MOVSX_REG_TO_REG(OpndSize_16, freg, freg); + IMUL(freg, d.reg); + } + } + + + d.h = ms; + if (mDithering) { + d.l = 0; + } else { + d.l = fs; + d.flags |= CLEAR_LO; + } +} + +void GGLX86Assembler::mul_factor_add( component_t& d, + const integer_t& v, + const integer_t& f, + const component_t& a) +{ + // XXX: we could have special cases for 1 bit mul + Scratch scratches(registerFile()); + + int vs = v.size(); + int fs = f.size(); + int as = a.h; + int ms = vs+fs; + + ALOGE_IF(ms>=32, "mul_factor_add overflow vs=%d, fs=%d, as=%d", vs, fs, as); + + integer_t add(a.reg, a.h, a.flags, a.offset_ebp); + + + // 'a' is a component_t but it is guaranteed to have + // its high bits set to 0. However in the dithering case, + // we can't get away with truncating the potentially bad bits + // so extraction is needed. + + if ((mDithering) && (a.size() < ms)) { + // we need to expand a + if (!(a.flags & CORRUPTIBLE)) { + // ... but it's not corruptible, so we need to pick a + // temporary register. + // Try to uses the destination register first (it's likely + // to be usable, unless it aliases an input). + if (d.reg!=a.reg && d.reg!=v.reg && d.reg!=f.reg) { + add.reg = d.reg; + } else { + add.reg = scratches.obtain(); + } + } + expand(add, a, ms); // extracts and expands + as = ms; + } + + if (ms == as) { + MOV_REG_TO_REG(v.reg, d.reg); + if (vs<16 && fs<16) { + MOVSX_REG_TO_REG(OpndSize_16, d.reg, d.reg); + MOVSX_REG_TO_REG(OpndSize_16, f.reg, f.reg); + IMUL(f.reg, d.reg); + } + else + IMUL(f.reg, d.reg); + ADD_REG_TO_REG(add.reg, d.reg); + } else { + //int temp = d.reg; + //if (temp == add.reg) { + // // the mul will modify add.reg, we need an intermediary reg + // if (v.flags & CORRUPTIBLE) temp = v.reg; + // else if (f.flags & CORRUPTIBLE) temp = f.reg; + // else temp = scratches.obtain(); + //} + + // below d.reg may override "temp" result, so we use a new register + int temp_reg; + int v_offset_ebp = 0; + if(scratches.countFreeRegs() == 0) { + temp_reg = v.reg; + mCurSp = mCurSp - 4; + v_offset_ebp = mCurSp; + MOV_REG_TO_MEM(v.reg, v_offset_ebp, EBP); + } + else { + temp_reg = scratches.obtain(); + MOV_REG_TO_REG(v.reg, temp_reg); + } + if (vs<16 && fs<16) { + MOVSX_REG_TO_REG(OpndSize_16, temp_reg, temp_reg); + MOVSX_REG_TO_REG(OpndSize_16, f.reg, f.reg); + IMUL(f.reg, temp_reg); + } + else + IMUL(f.reg, temp_reg); + + if (ms>as) { + MOV_REG_TO_REG(add.reg, d.reg); + SHL(ms-as, d.reg); + ADD_REG_TO_REG(temp_reg, d.reg); + } else if (ms<as) { + // not sure if we should expand the mul instead? + MOV_REG_TO_REG(add.reg, d.reg); + SHL(as-ms, d.reg); + ADD_REG_TO_REG(temp_reg, d.reg); + } + if(temp_reg == v.reg) + MOV_MEM_TO_REG(v_offset_ebp, EBP, v.reg); + else + scratches.recycle(temp_reg); + } + + d.h = ms; + if (mDithering) { + d.l = a.l; + } else { + d.l = fs>a.l ? fs : a.l; + d.flags |= CLEAR_LO; + } +} + +void GGLX86Assembler::component_add(component_t& d, + const integer_t& dst, const integer_t& src) +{ + // here we're guaranteed that fragment.size() >= fb.size() + const int shift = src.size() - dst.size(); + if (!shift) { + MOV_REG_TO_REG(src.reg, d.reg); + ADD_REG_TO_REG(dst.reg, d.reg); + } else { + MOV_REG_TO_REG(dst.reg, d.reg); + SHL(shift, d.reg); + ADD_REG_TO_REG(src.reg, d.reg); + } + + d.h = src.size(); + if (mDithering) { + d.l = 0; + } else { + d.l = shift; + d.flags |= CLEAR_LO; + } +} + +void GGLX86Assembler::component_sat(const component_t& v, const int temp_reg) +{ + const int32_t one = ((1<<v.size())-1)<<v.l; + MOV_IMM_TO_REG(one, temp_reg); + CMP_IMM_TO_REG(1<<v.h, v.reg); + CMOV_REG_TO_REG(Mnemonic_CMOVAE, temp_reg, v.reg); +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/libpixelflinger/codeflinger/x86/libenc/Android.mk b/libpixelflinger/codeflinger/x86/libenc/Android.mk new file mode 100644 index 0000000..445de06 --- /dev/null +++ b/libpixelflinger/codeflinger/x86/libenc/Android.mk @@ -0,0 +1,30 @@ +# +# Copyright (C) 2015 The Android-x86 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. +# + +LOCAL_PATH := $(call my-dir) + +enc_src_files := \ + dec_base.cpp \ + enc_base.cpp \ + enc_tabl.cpp \ + enc_wrapper.cpp + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(enc_src_files) +LOCAL_MODULE := libenc +LOCAL_MODULE_TAGS := optional +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) +include $(BUILD_STATIC_LIBRARY) diff --git a/libpixelflinger/codeflinger/x86/libenc/README.txt b/libpixelflinger/codeflinger/x86/libenc/README.txt new file mode 100644 index 0000000..a2e73ec --- /dev/null +++ b/libpixelflinger/codeflinger/x86/libenc/README.txt @@ -0,0 +1,21 @@ +Original source from Apache Harmony 5.0M15 (r991518 from 2010-09-01) at +http://harmony.apache.org/. + +The following files are from drlvm/vm/port/src/encoder/ia32_em64t. + + dec_base.cpp + dec_base.h + enc_base.cpp + enc_base.h + enc_defs.h + enc_prvt.h + enc_tabl.cpp + encoder.cpp + encoder.h + encoder.inl + +The following files are derived partially from the original Apache +Harmony files. + + enc_defs_ext.h -- derived from enc_defs.h + enc_wrapper.h -- derived from encoder.h diff --git a/libpixelflinger/codeflinger/x86/libenc/dec_base.cpp b/libpixelflinger/codeflinger/x86/libenc/dec_base.cpp new file mode 100644 index 0000000..ea85d10 --- /dev/null +++ b/libpixelflinger/codeflinger/x86/libenc/dec_base.cpp @@ -0,0 +1,541 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexander V. Astapchuk + */ + +/** + * @file + * @brief Main decoding (disassembling) routines implementation. + */ + +#include "dec_base.h" +#include "enc_prvt.h" +#include <stdio.h> +//#include "open/common.h" + +bool DecoderBase::is_prefix(const unsigned char * bytes) +{ + unsigned char b0 = *bytes; + unsigned char b1 = *(bytes+1); + if (b0 == 0xF0) { // LOCK + return true; + } + if (b0==0xF2 || b0==0xF3) { // REPNZ/REPZ prefixes + if (b1 == 0x0F) { // .... but may be a part of SIMD opcode + return false; + } + return true; + } + if (b0 == 0x2E || b0 == 0x36 || b0==0x3E || b0==0x26 || b0==0x64 || b0==0x3E) { + // branch hints, segment prefixes + return true; + } + if (b0==0x66) { // operand-size prefix + if (b1 == 0x0F) { // .... but may be a part of SIMD opcode + return false; + } + return false; //XXX - currently considered as part of opcode//true; + } + if (b0==0x67) { // address size prefix + return true; + } + return false; +} + +// Returns prefix count from 0 to 4, or ((unsigned int)-1) on error +unsigned int DecoderBase::fill_prefs(const unsigned char * bytes, Inst * pinst) +{ + const unsigned char * my_bytes = bytes; + + while( 1 ) + { + unsigned char by1 = *my_bytes; + unsigned char by2 = *(my_bytes + 1); + Inst::PrefGroups where; + + switch( by1 ) + { + case InstPrefix_REPNE: + case InstPrefix_REP: + { + if( 0x0F == by2) + { + return pinst->prefc; + } + } + case InstPrefix_LOCK: + { + where = Inst::Group1; + break; + } + case InstPrefix_CS: + case InstPrefix_SS: + case InstPrefix_DS: + case InstPrefix_ES: + case InstPrefix_FS: + case InstPrefix_GS: +// case InstPrefix_HintTaken: the same as CS override +// case InstPrefix_HintNotTaken: the same as DS override + { + where = Inst::Group2; + break; + } + case InstPrefix_OpndSize: + { +//NOTE: prefix does not work for JMP Sz16, the opcode is 0x66 0xe9 +// here 0x66 will be treated as prefix, try_mn will try to match the code starting at 0xe9 +// it will match JMP Sz32 ... +//HACK: assume it is the last prefix, return any way + if( 0x0F == by2) + { + return pinst->prefc; + } + return pinst->prefc; + where = Inst::Group3; + break; + } + case InstPrefix_AddrSize: + { + where = Inst::Group4; + break; + } + default: + { + return pinst->prefc; + } + } + // Assertions are not allowed here. + // Error situations should result in returning error status + if (InstPrefix_Null != pinst->pref[where]) //only one prefix in each group + return (unsigned int)-1; + + pinst->pref[where] = (InstPrefix)by1; + + if (pinst->prefc >= 4) //no more than 4 prefixes + return (unsigned int)-1; + + pinst->prefc++; + ++my_bytes; + } +} + + + +unsigned DecoderBase::decode(const void * addr, Inst * pinst) +{ + Inst tmp; + + //assert( *(unsigned char*)addr != 0x66); + + const unsigned char * bytes = (unsigned char*)addr; + + // Load up to 4 prefixes + // for each Mnemonic + unsigned int pref_count = fill_prefs(bytes, &tmp); + + if (pref_count == (unsigned int)-1) // Wrong prefix sequence, or >4 prefixes + return 0; // Error + + bytes += pref_count; + + // for each opcodedesc + // if (raw_len == 0) memcmp(, raw_len) + // else check the mixed state which is one of the following: + // /digit /i /rw /rd /rb + + bool found = false; + const unsigned char * saveBytes = bytes; + for (unsigned mn=1; mn<Mnemonic_Count; mn++) { + bytes = saveBytes; + found=try_mn((Mnemonic)mn, &bytes, &tmp); + if (found) { + tmp.mn = (Mnemonic)mn; + break; + } + } + if (!found) { + // Unknown opcode + return 0; + } + tmp.size = (unsigned)(bytes-(const unsigned char*)addr); + if (pinst) { + *pinst = tmp; + } + return tmp.size; +} + +#ifdef _EM64T_ +#define EXTEND_REG(reg, flag) \ + ((NULL == rex || 0 == rex->flag) ? reg : (reg + 8)) +#else +#define EXTEND_REG(reg, flag) (reg) +#endif + +//don't know the use of rex, seems not used when _EM64T_ is not enabled +bool DecoderBase::decode_aux(const EncoderBase::OpcodeDesc& odesc, unsigned aux, + const unsigned char ** pbuf, Inst * pinst +#ifdef _EM64T_ + , const Rex UNREF *rex +#endif + ) +{ + OpcodeByteKind kind = (OpcodeByteKind)(aux & OpcodeByteKind_KindMask); + unsigned byte = (aux & OpcodeByteKind_OpcodeMask); + unsigned data_byte = **pbuf; + EncoderBase::Operand& opnd = pinst->operands[pinst->argc]; + const EncoderBase::OpndDesc& opndDesc = odesc.opnds[pinst->argc]; + + switch (kind) { + case OpcodeByteKind_SlashR: + { + RegName reg; + OpndKind okind; + const ModRM& modrm = *(ModRM*)*pbuf; + if (opndDesc.kind & OpndKind_Mem) { // 1st operand is memory +#ifdef _EM64T_ + decodeModRM(odesc, pbuf, pinst, rex); +#else + decodeModRM(odesc, pbuf, pinst); +#endif + ++pinst->argc; + const EncoderBase::OpndDesc& opndDesc2 = odesc.opnds[pinst->argc]; + okind = ((opndDesc2.kind & OpndKind_XMMReg) || opndDesc2.size==OpndSize_64) ? OpndKind_XMMReg : OpndKind_GPReg; + EncoderBase::Operand& regOpnd = pinst->operands[pinst->argc]; + reg = getRegName(okind, opndDesc2.size, EXTEND_REG(modrm.reg, r)); + regOpnd = EncoderBase::Operand(reg); + } else { // 2nd operand is memory + okind = ((opndDesc.kind & OpndKind_XMMReg) || opndDesc.size==OpndSize_64) ? OpndKind_XMMReg : OpndKind_GPReg; + EncoderBase::Operand& regOpnd = pinst->operands[pinst->argc]; + reg = getRegName(okind, opndDesc.size, EXTEND_REG(modrm.reg, r)); + regOpnd = EncoderBase::Operand(reg); + ++pinst->argc; +#ifdef _EM64T_ + decodeModRM(odesc, pbuf, pinst, rex); +#else + decodeModRM(odesc, pbuf, pinst); +#endif + } + ++pinst->argc; + } + return true; + case OpcodeByteKind_rb: + case OpcodeByteKind_rw: + case OpcodeByteKind_rd: + { + // Gregory - + // Here we don't parse register because for current needs + // disassembler doesn't require to parse all operands + unsigned regid = data_byte - byte; + if (regid>7) { + return false; + } + OpndSize opnd_size; + switch(kind) + { + case OpcodeByteKind_rb: + { + opnd_size = OpndSize_8; + break; + } + case OpcodeByteKind_rw: + { + opnd_size = OpndSize_16; + break; + } + case OpcodeByteKind_rd: + { + opnd_size = OpndSize_32; + break; + } + default: + opnd_size = OpndSize_32; // so there is no compiler warning + assert( false ); + } + opnd = EncoderBase::Operand( getRegName(OpndKind_GPReg, opnd_size, regid) ); + + ++pinst->argc; + ++*pbuf; + return true; + } + case OpcodeByteKind_cb: + { + char offset = *(char*)*pbuf; + *pbuf += 1; + opnd = EncoderBase::Operand(offset); + ++pinst->argc; + //pinst->direct_addr = (void*)(pinst->offset + *pbuf); + } + return true; + case OpcodeByteKind_cw: + // not an error, but not expected in current env + // Android x86 + { + short offset = *(short*)*pbuf; + *pbuf += 2; + opnd = EncoderBase::Operand(offset); + ++pinst->argc; + } + return true; + //return false; + case OpcodeByteKind_cd: + { + int offset = *(int*)*pbuf; + *pbuf += 4; + opnd = EncoderBase::Operand(offset); + ++pinst->argc; + } + return true; + case OpcodeByteKind_SlashNum: + { + const ModRM& modrm = *(ModRM*)*pbuf; + if (modrm.reg != byte) { + return false; + } + decodeModRM(odesc, pbuf, pinst +#ifdef _EM64T_ + , rex +#endif + ); + ++pinst->argc; + } + return true; + case OpcodeByteKind_ib: + { + char ival = *(char*)*pbuf; + opnd = EncoderBase::Operand(ival); + ++pinst->argc; + *pbuf += 1; + } + return true; + case OpcodeByteKind_iw: + { + short ival = *(short*)*pbuf; + opnd = EncoderBase::Operand(ival); + ++pinst->argc; + *pbuf += 2; + } + return true; + case OpcodeByteKind_id: + { + int ival = *(int*)*pbuf; + opnd = EncoderBase::Operand(ival); + ++pinst->argc; + *pbuf += 4; + } + return true; +#ifdef _EM64T_ + case OpcodeByteKind_io: + { + long long int ival = *(long long int*)*pbuf; + opnd = EncoderBase::Operand(OpndSize_64, ival); + ++pinst->argc; + *pbuf += 8; + } + return true; +#endif + case OpcodeByteKind_plus_i: + { + unsigned regid = data_byte - byte; + if (regid>7) { + return false; + } + ++*pbuf; + return true; + } + case OpcodeByteKind_ZeroOpcodeByte: // cant be here + return false; + default: + // unknown kind ? how comes ? + break; + } + return false; +} + +bool DecoderBase::try_mn(Mnemonic mn, const unsigned char ** pbuf, Inst * pinst) { + const unsigned char * save_pbuf = *pbuf; + EncoderBase::OpcodeDesc * opcodes = EncoderBase::opcodes[mn]; + + for (unsigned i=0; !opcodes[i].last; i++) { + const EncoderBase::OpcodeDesc& odesc = opcodes[i]; + char *opcode_ptr = const_cast<char *>(odesc.opcode); + int opcode_len = odesc.opcode_len; +#ifdef _EM64T_ + Rex *prex = NULL; + Rex rex; +#endif + + *pbuf = save_pbuf; +#ifdef _EM64T_ + // Match REX prefixes + unsigned char rex_byte = (*pbuf)[0]; + if ((rex_byte & 0xf0) == 0x40) + { + if ((rex_byte & 0x08) != 0) + { + // Have REX.W + if (opcode_len > 0 && opcode_ptr[0] == 0x48) + { + // Have REX.W in opcode. All mnemonics that allow + // REX.W have to have specified it in opcode, + // otherwise it is not allowed + rex = *(Rex *)*pbuf; + prex = &rex; + (*pbuf)++; + opcode_ptr++; + opcode_len--; + } + } + else + { + // No REX.W, so it doesn't have to be in opcode. We + // have REX.B, REX.X, REX.R or their combination, but + // not in opcode, they may extend any part of the + // instruction + rex = *(Rex *)*pbuf; + prex = &rex; + (*pbuf)++; + } + } +#endif + if (opcode_len != 0) { + if (memcmp(*pbuf, opcode_ptr, opcode_len)) { + continue; + } + *pbuf += opcode_len; + } + if (odesc.aux0 != 0) { + + if (!decode_aux(odesc, odesc.aux0, pbuf, pinst +#ifdef _EM64T_ + , prex +#endif + )) { + continue; + } + if (odesc.aux1 != 0) { + if (!decode_aux(odesc, odesc.aux1, pbuf, pinst +#ifdef _EM64T_ + , prex +#endif + )) { + continue; + } + } + pinst->odesc = &opcodes[i]; + return true; + } + else { + // Can't have empty opcode + assert(opcode_len != 0); + pinst->odesc = &opcodes[i]; + return true; + } + } + return false; +} + +bool DecoderBase::decodeModRM(const EncoderBase::OpcodeDesc& odesc, + const unsigned char ** pbuf, Inst * pinst +#ifdef _EM64T_ + , const Rex *rex +#endif + ) +{ + EncoderBase::Operand& opnd = pinst->operands[pinst->argc]; + const EncoderBase::OpndDesc& opndDesc = odesc.opnds[pinst->argc]; + + //XXX debug ///assert(0x66 != *(*pbuf-2)); + const ModRM& modrm = *(ModRM*)*pbuf; + *pbuf += 1; + + RegName base = RegName_Null; + RegName index = RegName_Null; + int disp = 0; + unsigned scale = 0; + + // On x86_64 all mnemonics that allow REX.W have REX.W in opcode. + // Therefore REX.W is simply ignored, and opndDesc.size is used + + if (modrm.mod == 3) { + // we have only modrm. no sib, no disp. + // Android x86: Use XMMReg for 64b operand. + OpndKind okind = ((opndDesc.kind & OpndKind_XMMReg) || opndDesc.size == OpndSize_64) ? OpndKind_XMMReg : OpndKind_GPReg; + RegName reg = getRegName(okind, opndDesc.size, EXTEND_REG(modrm.rm, b)); + opnd = EncoderBase::Operand(reg); + return true; + } + //Android x86: m16, m32, m64: mean a byte[word|doubleword] operand in memory + //base and index should be 32 bits!!! + const SIB& sib = *(SIB*)*pbuf; + // check whether we have a sib + if (modrm.rm == 4) { + // yes, we have SIB + *pbuf += 1; + if (sib.index != 4) { + index = getRegName(OpndKind_GPReg, OpndSize_32, EXTEND_REG(sib.index, x)); //Android x86: OpndDesc.size + } else { + // (sib.index == 4) => no index + //%esp can't be sib.index + } + + // scale = sib.scale == 0 ? 0 : (1<<sib.scale); + // scale = (1<<sib.scale); + scale = (index == RegName_Null) ? 0 : (1<<sib.scale); + + if (sib.base != 5 || modrm.mod != 0) { + base = getRegName(OpndKind_GPReg, OpndSize_32, EXTEND_REG(sib.base, b)); //Android x86: OpndDesc.size + } else { + // (sib.base == 5 && modrm.mod == 0) => no base + } + } + else { + if (modrm.mod != 0 || modrm.rm != 5) { + base = getRegName(OpndKind_GPReg, OpndSize_32, EXTEND_REG(modrm.rm, b)); //Android x86: OpndDesc.size + } + else { + // mod=0 && rm == 5 => only disp32 + } + } + + //update disp and pbuf + if (modrm.mod == 2) { + // have disp32 + disp = *(int*)*pbuf; + *pbuf += 4; + } + else if (modrm.mod == 1) { + // have disp8 + disp = *(char*)*pbuf; + *pbuf += 1; + } + else { + assert(modrm.mod == 0); + if (modrm.rm == 5) { + // have disp32 w/o sib + disp = *(int*)*pbuf; + *pbuf += 4; + } + else if (modrm.rm == 4 && sib.base == 5) { + // have disp32 with SI in sib + disp = *(int*)*pbuf; + *pbuf += 4; + } + } + opnd = EncoderBase::Operand(opndDesc.size, base, index, scale, disp); + return true; +} diff --git a/libpixelflinger/codeflinger/x86/libenc/dec_base.h b/libpixelflinger/codeflinger/x86/libenc/dec_base.h new file mode 100644 index 0000000..f1fa123 --- /dev/null +++ b/libpixelflinger/codeflinger/x86/libenc/dec_base.h @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexander V. Astapchuk + */ + +/** + * @file + * @brief Main decoding (disassembling) routines and structures. + * + * @note Quick and rough implementation, subject for a change. + */ + +#ifndef __DEC_BASE_H_INCLUDED__ +#define __DEC_BASE_H_INCLUDED__ + + +#include "enc_base.h" +#include "enc_prvt.h" + +#ifdef ENCODER_ISOLATE +using namespace enc_ia32; +#endif + +#define IF_CONDITIONAL (0x00000000) +#define IF_SYMMETRIC (0x00000000) +#define IF_BRANCH (0x00000000) + +struct Inst { + Inst() { + mn = Mnemonic_Null; + prefc = 0; + size = 0; + flags = 0; + //offset = 0; + //direct_addr = NULL; + argc = 0; + for(int i = 0; i < 4; ++i) + { + pref[i] = InstPrefix_Null; + } + } + /** + * Mnemonic of the instruction.s + */ + Mnemonic mn; + /** + * Enumerating of indexes in the pref array. + */ + enum PrefGroups + { + Group1 = 0, + Group2, + Group3, + Group4 + }; + /** + * Number of prefixes (1 byte each). + */ + unsigned int prefc; + /** + * Instruction prefixes. Prefix should be placed here according to its group. + */ + InstPrefix pref[4]; + /** + * Size, in bytes, of the instruction. + */ + unsigned size; + /** + * Flags of the instruction. + * @see MF_ + */ + unsigned flags; + /** + * An offset of target address, in case of 'CALL offset', + * 'JMP/Jcc offset'. + */ + //int offset; + /** + * Direct address of the target (on Intel64/IA-32 is 'instruction IP' + + * 'instruction length' + offset). + */ + //void * direct_addr; + /** + * Number of arguments of the instruction. + */ + unsigned argc; + // + EncoderBase::Operand operands[3]; + // + const EncoderBase::OpcodeDesc * odesc; +}; + +inline bool is_jcc(Mnemonic mn) +{ + return Mnemonic_JO <= mn && mn<=Mnemonic_JG; +} + +class DecoderBase { +public: + static unsigned decode(const void * addr, Inst * pinst); +private: + static bool decodeModRM(const EncoderBase::OpcodeDesc& odesc, + const unsigned char ** pbuf, Inst * pinst +#ifdef _EM64T_ + , const Rex *rex +#endif + ); + static bool decode_aux(const EncoderBase::OpcodeDesc& odesc, + unsigned aux, const unsigned char ** pbuf, + Inst * pinst +#ifdef _EM64T_ + , const Rex *rex +#endif + ); + static bool try_mn(Mnemonic mn, const unsigned char ** pbuf, Inst * pinst); + static unsigned int fill_prefs( const unsigned char * bytes, Inst * pinst); + static bool is_prefix(const unsigned char * bytes); +}; + +#endif // ~ __DEC_BASE_H_INCLUDED__ diff --git a/libpixelflinger/codeflinger/x86/libenc/enc_base.cpp b/libpixelflinger/codeflinger/x86/libenc/enc_base.cpp new file mode 100644 index 0000000..0562ce8 --- /dev/null +++ b/libpixelflinger/codeflinger/x86/libenc/enc_base.cpp @@ -0,0 +1,1137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexander V. Astapchuk + */ +#include "enc_base.h" +//#include <climits> +#include <string.h> +#define USE_ENCODER_DEFINES +#include "enc_prvt.h" +#include <stdio.h> + +//#define JET_PROTO + +#ifdef JET_PROTO +#include "dec_base.h" +#include "jvmti_dasm.h" +#endif + +ENCODER_NAMESPACE_START + +/** + * @file + * @brief Main encoding routines and structures. + */ + +#ifndef _WIN32 + #define strcmpi strcasecmp +#endif + +int EncoderBase::dummy = EncoderBase::buildTable(); + +const unsigned char EncoderBase::size_hash[OpndSize_64+1] = { + // + 0xFF, // OpndSize_Null = 0, + 3, // OpndSize_8 = 0x1, + 2, // OpndSize_16 = 0x2, + 0xFF, // 0x3 + 1, // OpndSize_32 = 0x4, + 0xFF, // 0x5 + 0xFF, // 0x6 + 0xFF, // 0x7 + 0, // OpndSize_64 = 0x8, + // +}; + +const unsigned char EncoderBase::kind_hash[OpndKind_Mem+1] = { + // + //gp reg -> 000 = 0 + //memory -> 001 = 1 + //immediate -> 010 = 2 + //xmm reg -> 011 = 3 + //segment regs -> 100 = 4 + //fp reg -> 101 = 5 + //mmx reg -> 110 = 6 + // + 0xFF, // 0 OpndKind_Null=0, + 0<<2, // 1 OpndKind_GPReg = + // OpndKind_MinRegKind=0x1, + 4<<2, // 2 OpndKind_SReg=0x2, + +#ifdef _HAVE_MMX_ + 6<<2, // 3 +#else + 0xFF, // 3 +#endif + + 5<<2, // 4 OpndKind_FPReg=0x4, + 0xFF, 0xFF, 0xFF, // 5, 6, 7 + 3<<2, // OpndKind_XMMReg=0x8, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 9, 0xA, 0xB, 0xC, 0xD, + // 0xE, 0xF + 0xFF, // OpndKind_MaxRegKind = + // OpndKind_StatusReg = + // OpndKind_OtherReg=0x10, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x11-0x18 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x19-0x1F + 2<<2, // OpndKind_Immediate=0x20, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x21-0x28 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x29-0x30 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x31-0x38 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x39-0x3F + 1<<2, // OpndKind_Memory=0x40 +}; + +char * EncoderBase::curRelOpnd[3]; + +char* EncoderBase::encode_aux(char* stream, unsigned aux, + const Operands& opnds, const OpcodeDesc * odesc, + unsigned * pargsCount, Rex * prex) +{ + const unsigned byte = aux; + OpcodeByteKind kind = (OpcodeByteKind)(byte & OpcodeByteKind_KindMask); + // The '>>' here is to force the switch to be table-based) instead of + // set of CMP+Jcc. + if (*pargsCount >= COUNTOF(opnds)) { + assert(false); + return stream; + } + switch(kind>>8) { + case OpcodeByteKind_SlashR>>8: + // /r - Indicates that the ModR/M byte of the instruction contains + // both a register operand and an r/m operand. + { + assert(opnds.count() > 1); + // not true anymore for MOVQ xmm<->r + //assert((odesc->opnds[0].kind & OpndKind_Mem) || + // (odesc->opnds[1].kind & OpndKind_Mem)); + unsigned memidx = odesc->opnds[0].kind & OpndKind_Mem ? 0 : 1; + unsigned regidx = memidx == 0 ? 1 : 0; + memidx += *pargsCount; + regidx += *pargsCount; + ModRM& modrm = *(ModRM*)stream; + if (memidx >= COUNTOF(opnds) || regidx >= COUNTOF(opnds)) { + assert(false); + break; + } + if (opnds[memidx].is_mem()) { + stream = encodeModRM(stream, opnds, memidx, odesc, prex); + } + else { + modrm.mod = 3; // 11 + modrm.rm = getHWRegIndex(opnds[memidx].reg()); +#ifdef _EM64T_ + if (opnds[memidx].need_rex() && needs_rex_r(opnds[memidx].reg())) { + prex->b = 1; + } +#endif + ++stream; + } + modrm.reg = getHWRegIndex(opnds[regidx].reg()); +#ifdef _EM64T_ + if (opnds[regidx].need_rex() && needs_rex_r(opnds[regidx].reg())) { + prex->r = 1; + } +#endif + *pargsCount += 2; + } + break; + case OpcodeByteKind_SlashNum>>8: + // /digit - A digit between 0 and 7 indicates that the + // ModR/M byte of the instruction uses only the r/m + // (register or memory) operand. The reg field contains + // the digit that provides an extension to the instruction's + // opcode. + { + const unsigned lowByte = (byte & OpcodeByteKind_OpcodeMask); + assert(lowByte <= 7); + ModRM& modrm = *(ModRM*)stream; + unsigned idx = *pargsCount; + assert(opnds[idx].is_mem() || opnds[idx].is_reg()); + if (opnds[idx].is_mem()) { + stream = encodeModRM(stream, opnds, idx, odesc, prex); + } + else { + modrm.mod = 3; // 11 + modrm.rm = getHWRegIndex(opnds[idx].reg()); +#ifdef _EM64T_ + if (opnds[idx].need_rex() && needs_rex_r(opnds[idx].reg())) { + prex->b = 1; + } +#endif + ++stream; + } + modrm.reg = (char)lowByte; + *pargsCount += 1; + } + break; + case OpcodeByteKind_plus_i>>8: + // +i - A number used in floating-point instructions when one + // of the operands is ST(i) from the FPU register stack. The + // number i (which can range from 0 to 7) is added to the + // hexadecimal byte given at the left of the plus sign to form + // a single opcode byte. + { + unsigned idx = *pargsCount; + const unsigned lowByte = (byte & OpcodeByteKind_OpcodeMask); + *stream = (char)lowByte + getHWRegIndex(opnds[idx].reg()); + ++stream; + *pargsCount += 1; + } + break; + case OpcodeByteKind_ib>>8: + case OpcodeByteKind_iw>>8: + case OpcodeByteKind_id>>8: +#ifdef _EM64T_ + case OpcodeByteKind_io>>8: +#endif //_EM64T_ + // ib, iw, id - A 1-byte (ib), 2-byte (iw), or 4-byte (id) + // immediate operand to the instruction that follows the + // opcode, ModR/M bytes or scale-indexing bytes. The opcode + // determines if the operand is a signed value. All words + // and double words are given with the low-order byte first. + { + unsigned idx = *pargsCount; + *pargsCount += 1; + assert(opnds[idx].is_imm()); + if (kind == OpcodeByteKind_ib) { + *(unsigned char*)stream = (unsigned char)opnds[idx].imm(); + curRelOpnd[idx] = stream; + stream += 1; + } + else if (kind == OpcodeByteKind_iw) { + *(unsigned short*)stream = (unsigned short)opnds[idx].imm(); + curRelOpnd[idx] = stream; + stream += 2; + } + else if (kind == OpcodeByteKind_id) { + *(unsigned*)stream = (unsigned)opnds[idx].imm(); + curRelOpnd[idx] = stream; + stream += 4; + } +#ifdef _EM64T_ + else { + assert(kind == OpcodeByteKind_io); + *(long long*)stream = (long long)opnds[idx].imm(); + curRelOpnd[idx] = stream; + stream += 8; + } +#else + else { + assert(false); + } +#endif + } + break; + case OpcodeByteKind_cb>>8: + assert(opnds[*pargsCount].is_imm()); + *(unsigned char*)stream = (unsigned char)opnds[*pargsCount].imm(); + curRelOpnd[*pargsCount]= stream; + stream += 1; + *pargsCount += 1; + break; + case OpcodeByteKind_cw>>8: + assert(opnds[*pargsCount].is_imm()); + *(unsigned short*)stream = (unsigned short)opnds[*pargsCount].imm(); + curRelOpnd[*pargsCount]= stream; + stream += 2; + *pargsCount += 1; + break; + case OpcodeByteKind_cd>>8: + assert(opnds[*pargsCount].is_imm()); + *(unsigned*)stream = (unsigned)opnds[*pargsCount].imm(); + curRelOpnd[*pargsCount]= stream; + stream += 4; + *pargsCount += 1; + break; + //OpcodeByteKind_cp = 0x0B00, + //OpcodeByteKind_co = 0x0C00, + //OpcodeByteKind_ct = 0x0D00, + case OpcodeByteKind_rb>>8: + case OpcodeByteKind_rw>>8: + case OpcodeByteKind_rd>>8: + // +rb, +rw, +rd - A register code, from 0 through 7, + // added to the hexadecimal byte given at the left of + // the plus sign to form a single opcode byte. + assert(opnds.count() > 0); + assert(opnds[*pargsCount].is_reg()); + { + const unsigned lowByte = (byte & OpcodeByteKind_OpcodeMask); + *(unsigned char*)stream = (unsigned char)lowByte + + getHWRegIndex(opnds[*pargsCount].reg()); +#ifdef _EM64T_ + if (opnds[*pargsCount].need_rex() && needs_rex_r(opnds[*pargsCount].reg())) { + prex->b = 1; + } +#endif + ++stream; + *pargsCount += 1; + } + break; + default: + assert(false); + break; + } + return stream; +} + +char * EncoderBase::encode(char * stream, Mnemonic mn, const Operands& opnds) +{ +#ifdef _DEBUG + if (opnds.count() > 0) { + if (opnds[0].is_mem()) { + assert(getRegKind(opnds[0].base()) != OpndKind_SReg); + } + else if (opnds.count() >1 && opnds[1].is_mem()) { + assert(getRegKind(opnds[1].base()) != OpndKind_SReg); + } + } +#endif + +#ifdef JET_PROTO + char* saveStream = stream; +#endif + + const OpcodeDesc * odesc = lookup(mn, opnds); +#if !defined(_EM64T_) + bool copy_opcode = true; + Rex *prex = NULL; +#else + // We need rex if + // either of registers used as operand or address form is new extended register + // it's explicitly specified by opcode + // So, if we don't have REX in opcode but need_rex, then set rex here + // otherwise, wait until opcode is set, and then update REX + + bool copy_opcode = true; + unsigned char _1st = odesc->opcode[0]; + + Rex *prex = (Rex*)stream; + if (opnds.need_rex() && + ((_1st == 0x66) || (_1st == 0xF2 || _1st == 0xF3) && odesc->opcode[1] == 0x0F)) { + // Special processing + // + copy_opcode = false; + // + *(unsigned char*)stream = _1st; + ++stream; + // + prex = (Rex*)stream; + prex->dummy = 4; + prex->w = 0; + prex->b = 0; + prex->x = 0; + prex->r = 0; + ++stream; + // + memcpy(stream, &odesc->opcode[1], odesc->opcode_len-1); + stream += odesc->opcode_len-1; + } + else if (_1st != 0x48 && opnds.need_rex()) { + prex = (Rex*)stream; + prex->dummy = 4; + prex->w = 0; + prex->b = 0; + prex->x = 0; + prex->r = 0; + ++stream; + } +#endif // ifndef EM64T + + if (copy_opcode) { + if (odesc->opcode_len==1) { + unsigned char *dest = (unsigned char *) (stream); + unsigned char *src = (unsigned char *) (& (odesc->opcode)); + *dest = *src; + } + else if (odesc->opcode_len==2) { + short *dest = (short *) (stream); + void *ptr = (void *) (& (odesc->opcode)); + short *src = (short *) (ptr); + *dest = *src; + } + else if (odesc->opcode_len==3) { + unsigned short *dest = (unsigned short *) (stream); + void *ptr = (void *) (& (odesc->opcode)); + unsigned short *src = (unsigned short *) (ptr); + *dest = *src; + + //Now handle the last part + unsigned char *dest2 = (unsigned char *) (stream + 2); + *dest2 = odesc->opcode[2]; + } + else if (odesc->opcode_len==4) { + unsigned int *dest = (unsigned int *) (stream); + void *ptr = (void *) (& (odesc->opcode)); + unsigned int *src = (unsigned int *) (ptr); + *dest = *src; + } + stream += odesc->opcode_len; + } + + unsigned argsCount = odesc->first_opnd; + + if (odesc->aux0) { + stream = encode_aux(stream, odesc->aux0, opnds, odesc, &argsCount, prex); + if (odesc->aux1) { + stream = encode_aux(stream, odesc->aux1, opnds, odesc, &argsCount, prex); + } + } +#ifdef JET_PROTO + //saveStream + Inst inst; + unsigned len = DecoderBase::decode(saveStream, &inst); + assert(inst.mn == mn); + assert(len == (unsigned)(stream-saveStream)); + if (mn == Mnemonic_CALL || mn == Mnemonic_JMP || + Mnemonic_RET == mn || + (Mnemonic_JO<=mn && mn<=Mnemonic_JG)) { + assert(inst.argc == opnds.count()); + + InstructionDisassembler idi(saveStream); + + for (unsigned i=0; i<inst.argc; i++) { + const EncoderBase::Operand& original = opnds[i]; + const EncoderBase::Operand& decoded = inst.operands[i]; + assert(original.kind() == decoded.kind()); + assert(original.size() == decoded.size()); + if (original.is_imm()) { + assert(original.imm() == decoded.imm()); + assert(idi.get_opnd(0).kind == InstructionDisassembler::Kind_Imm); + if (mn == Mnemonic_CALL) { + assert(idi.get_type() == InstructionDisassembler::RELATIVE_CALL); + } + else if (mn == Mnemonic_JMP) { + assert(idi.get_type() == InstructionDisassembler::RELATIVE_JUMP); + } + else if (mn == Mnemonic_RET) { + assert(idi.get_type() == InstructionDisassembler::RET); + } + else { + assert(idi.get_type() == InstructionDisassembler::RELATIVE_COND_JUMP); + } + } + else if (original.is_mem()) { + assert(original.base() == decoded.base()); + assert(original.index() == decoded.index()); + assert(original.scale() == decoded.scale()); + assert(original.disp() == decoded.disp()); + assert(idi.get_opnd(0).kind == InstructionDisassembler::Kind_Mem); + if (mn == Mnemonic_CALL) { + assert(idi.get_type() == InstructionDisassembler::INDIRECT_CALL); + } + else if (mn == Mnemonic_JMP) { + assert(idi.get_type() == InstructionDisassembler::INDIRECT_JUMP); + } + else { + assert(false); + } + } + else { + assert(original.is_reg()); + assert(original.reg() == decoded.reg()); + assert(idi.get_opnd(0).kind == InstructionDisassembler::Kind_Reg); + if (mn == Mnemonic_CALL) { + assert(idi.get_type() == InstructionDisassembler::INDIRECT_CALL); + } + else if (mn == Mnemonic_JMP) { + assert(idi.get_type() == InstructionDisassembler::INDIRECT_JUMP); + } + else { + assert(false); + } + } + } + + Inst inst2; + len = DecoderBase::decode(saveStream, &inst2); + } + + // if(idi.get_length_with_prefix() != (int)len) { + //__asm { int 3 }; + // } +#endif + + return stream; +} + +char* EncoderBase::encodeModRM(char* stream, const Operands& opnds, + unsigned idx, const OpcodeDesc * odesc, + Rex * prex) +{ + const Operand& op = opnds[idx]; + assert(op.is_mem()); + assert(idx < COUNTOF(curRelOpnd)); + ModRM& modrm = *(ModRM*)stream; + ++stream; + SIB& sib = *(SIB*)stream; + + // we need SIB if + // we have index & scale (nb: having index w/o base and w/o scale + // treated as error) + // the base is EBP w/o disp, BUT let's use a fake disp8 + // the base is ESP (nb: cant have ESP as index) + + RegName base = op.base(); + // only disp ?.. + if (base == RegName_Null && op.index() == RegName_Null) { + assert(op.scale() == 0); // 'scale!=0' has no meaning without index + // ... yes - only have disp + // On EM64T, the simply [disp] addressing means 'RIP-based' one - + // must have to use SIB to encode 'DS: based' +#ifdef _EM64T_ + modrm.mod = 0; // 00 - .. + modrm.rm = 4; // 100 - have SIB + + sib.base = 5; // 101 - none + sib.index = 4; // 100 - none + sib.scale = 0; // + ++stream; // bypass SIB +#else + // ignore disp_fits8, always use disp32. + modrm.mod = 0; + modrm.rm = 5; +#endif + *(unsigned*)stream = (unsigned)op.disp(); + curRelOpnd[idx]= stream; + stream += 4; + return stream; + } + + //climits: error when targeting compal +#define CHAR_MIN -127 +#define CHAR_MAX 127 + const bool disp_fits8 = CHAR_MIN <= op.disp() && op.disp() <= CHAR_MAX; + /*&& op.base() != RegName_Null - just checked above*/ + if (op.index() == RegName_Null && getHWRegIndex(op.base()) != getHWRegIndex(REG_STACK)) { + assert(op.scale() == 0); // 'scale!=0' has no meaning without index + // ... luckily no SIB, only base and may be a disp + + // EBP base is a special case. Need to use [EBP] + disp8 form + if (op.disp() == 0 && getHWRegIndex(op.base()) != getHWRegIndex(RegName_EBP)) { + modrm.mod = 0; // mod=00, no disp et all + } + else if (disp_fits8) { + modrm.mod = 1; // mod=01, use disp8 + *(unsigned char*)stream = (unsigned char)op.disp(); + curRelOpnd[idx]= stream; + ++stream; + } + else { + modrm.mod = 2; // mod=10, use disp32 + *(unsigned*)stream = (unsigned)op.disp(); + curRelOpnd[idx]= stream; + stream += 4; + } + modrm.rm = getHWRegIndex(op.base()); + if (is_em64t_extra_reg(op.base())) { + prex->b = 1; + } + return stream; + } + + // cool, we do have SIB. + ++stream; // bypass SIB in stream + + // {E|R}SP cannot be scaled index, however, R12 which has the same index in modrm - can + assert(op.index() == RegName_Null || !equals(op.index(), REG_STACK)); + + // Only GPRegs can be encoded in the SIB + assert(op.base() == RegName_Null || + getRegKind(op.base()) == OpndKind_GPReg); + assert(op.index() == RegName_Null || + getRegKind(op.index()) == OpndKind_GPReg); + + modrm.rm = 4; // r/m = 100, means 'we have SIB here' + if (op.base() == RegName_Null) { + // no base. + // already checked above if + // the first if() //assert(op.index() != RegName_Null); + + modrm.mod = 0; // mod=00 - here it means 'no base, but disp32' + sib.base = 5; // 101 with mod=00 ^^^ + + // encode at least fake disp32 to avoid having [base=ebp] + *(unsigned*)stream = op.disp(); + curRelOpnd[idx]= stream; + stream += 4; + + unsigned sc = op.scale(); + if (sc == 1 || sc==0) { sib.scale = 0; } // SS=00 + else if (sc == 2) { sib.scale = 1; } // SS=01 + else if (sc == 4) { sib.scale = 2; } // SS=10 + else if (sc == 8) { sib.scale = 3; } // SS=11 + sib.index = getHWRegIndex(op.index()); + if (is_em64t_extra_reg(op.index())) { + prex->x = 1; + } + + return stream; + } + + if (op.disp() == 0 && getHWRegIndex(op.base()) != getHWRegIndex(RegName_EBP)) { + modrm.mod = 0; // mod=00, no disp + } + else if (disp_fits8) { + modrm.mod = 1; // mod=01, use disp8 + *(unsigned char*)stream = (unsigned char)op.disp(); + curRelOpnd[idx]= stream; + stream += 1; + } + else { + modrm.mod = 2; // mod=10, use disp32 + *(unsigned*)stream = (unsigned)op.disp(); + curRelOpnd[idx]= stream; + stream += 4; + } + + if (op.index() == RegName_Null) { + assert(op.scale() == 0); // 'scale!=0' has no meaning without index + // the only reason we're here without index, is that we have {E|R}SP + // or R12 as a base. Another possible reason - EBP without a disp - + // is handled above by adding a fake disp8 +#ifdef _EM64T_ + assert(op.base() != RegName_Null && (equals(op.base(), REG_STACK) || + equals(op.base(), RegName_R12))); +#else // _EM64T_ + assert(op.base() != RegName_Null && equals(op.base(), REG_STACK)); +#endif //_EM64T_ + sib.scale = 0; // SS = 00 + sib.index = 4; // SS + index=100 means 'no index' + } + else { + unsigned sc = op.scale(); + if (sc == 1 || sc==0) { sib.scale = 0; } // SS=00 + else if (sc == 2) { sib.scale = 1; } // SS=01 + else if (sc == 4) { sib.scale = 2; } // SS=10 + else if (sc == 8) { sib.scale = 3; } // SS=11 + sib.index = getHWRegIndex(op.index()); + if (is_em64t_extra_reg(op.index())) { + prex->x = 1; + } + // not an error by itself, but the usage of [index*1] instead + // of [base] is discouraged + assert(op.base() != RegName_Null || op.scale() != 1); + } + sib.base = getHWRegIndex(op.base()); + if (is_em64t_extra_reg(op.base())) { + prex->b = 1; + } + return stream; +} + +char * EncoderBase::nops(char * stream, unsigned howMany) +{ + // Recommended multi-byte NOPs from the Intel architecture manual + static const unsigned char nops[10][9] = { + { 0, }, // 0, this line is dummy and not used in the loop below + { 0x90, }, // 1-byte NOP + { 0x66, 0x90, }, // 2 + { 0x0F, 0x1F, 0x00, }, // 3 + { 0x0F, 0x1F, 0x40, 0x00, }, // 4 + { 0x0F, 0x1F, 0x44, 0x00, 0x00, }, // 5 + { 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00, }, // 6 + { 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, }, // 7 + { 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, }, // 8 + { 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 9-byte NOP + }; + + // Start from delivering the longest possible NOPs, then proceed with shorter ones + for (unsigned nopSize=9; nopSize!=0; nopSize--) { + while(howMany>=nopSize) { + const unsigned char* nopBytes = nops[nopSize]; + for (unsigned i=0; i<nopSize; i++) { + stream[i] = nopBytes[i]; + } + stream += nopSize; + howMany -= nopSize; + } + } + char* end = stream + howMany; + return end; +} + +char * EncoderBase::prefix(char* stream, InstPrefix pref) +{ + if (pref== InstPrefix_Null) { + // nothing to do + return stream; + } + *stream = (char)pref; + return stream + 1; +} + + +/** + * + */ +bool EncoderBase::extAllowed(OpndExt opndExt, OpndExt instExt) { + if (instExt == opndExt || instExt == OpndExt_Any || opndExt == OpndExt_Any) { + return true; + } +//asm("int3"); +assert(0); + return false; +} + +static bool try_match(const EncoderBase::OpcodeDesc& odesc, + const EncoderBase::Operands& opnds, bool strict) { + + assert(odesc.roles.count == opnds.count()); + + for(unsigned j=0; j<odesc.roles.count; j++) { + // - the location must match exactly + if ((odesc.opnds[j].kind & opnds[j].kind()) != opnds[j].kind()) { + return false; + } + if (strict) { + // the size must match exactly + if (odesc.opnds[j].size != opnds[j].size()) { + return false; + } + } + else { + // must match only for def operands, and dont care about use ones + // situations like 'mov r8, imm32/mov r32, imm8' so the + // destination operand defines the overall size + if (EncoderBase::getOpndRoles(odesc.roles, j) & OpndRole_Def) { + if (odesc.opnds[j].size != opnds[j].size()) { + return false; + } + } + } + } + return true; +} + +// +//Subhash implementaion - may be useful in case of many misses during fast +//opcode lookup. +// + +#ifdef ENCODER_USE_SUBHASH +static unsigned subHash[32]; + +static unsigned find(Mnemonic mn, unsigned hash) +{ + unsigned key = hash % COUNTOF(subHash); + unsigned pack = subHash[key]; + unsigned _hash = pack & 0xFFFF; + if (_hash != hash) { + stat.miss(mn); + return EncoderBase::NOHASH; + } + unsigned _mn = (pack >> 24)&0xFF; + if (_mn != _mn) { + stat.miss(mn); + return EncoderBase::NOHASH; + } + unsigned idx = (pack >> 16) & 0xFF; + stat.hit(mn); + return idx; +} + +static void put(Mnemonic mn, unsigned hash, unsigned idx) +{ + unsigned pack = hash | (idx<<16) | (mn << 24); + unsigned key = hash % COUNTOF(subHash); + subHash[key] = pack; +} +#endif + +const EncoderBase::OpcodeDesc * +EncoderBase::lookup(Mnemonic mn, const Operands& opnds) +{ + const unsigned hash = opnds.hash(); + unsigned opcodeIndex = opcodesHashMap[mn][hash]; +#ifdef ENCODER_USE_SUBHASH + if (opcodeIndex == NOHASH) { + opcodeIndex = find(mn, hash); + } +#endif + + if (opcodeIndex == NOHASH) { + // fast-path did no work. try to lookup sequentially + const OpcodeDesc * odesc = opcodes[mn]; + int idx = -1; + bool found = false; + for (idx=0; !odesc[idx].last; idx++) { + const OpcodeDesc& opcode = odesc[idx]; + if (opcode.platf == OpcodeInfo::decoder) { + continue; + } + if (opcode.roles.count != opnds.count()) { + continue; + } + if (try_match(opcode, opnds, true)) { + found = true; + break; + } + } + if (!found) { + for (idx=0; !odesc[idx].last; idx++) { + const OpcodeDesc& opcode = odesc[idx]; + if (opcode.platf == OpcodeInfo::decoder) { + continue; + } + if (opcode.roles.count != opnds.count()) { + continue; + } + if (try_match(opcode, opnds, false)) { + found = true; + break; + } + } + } + assert(found); + opcodeIndex = idx; +#ifdef ENCODER_USE_SUBHASH + put(mn, hash, opcodeIndex); +#endif + } + assert(opcodeIndex != NOHASH); + const OpcodeDesc * odesc = &opcodes[mn][opcodeIndex]; + assert(!odesc->last); + assert(odesc->roles.count == opnds.count()); + assert(odesc->platf != OpcodeInfo::decoder); +#if !defined(_EM64T_) + // tuning was done for IA32 only, so no size restriction on EM64T + //assert(sizeof(OpcodeDesc)==128); +#endif + return odesc; +} + +char* EncoderBase::getOpndLocation(int index) { + assert(index < 3); + return curRelOpnd[index]; +} + + +Mnemonic EncoderBase::str2mnemonic(const char * mn_name) +{ + for (unsigned m = 1; m<Mnemonic_Count; m++) { + if (!strcmpi(mnemonics[m].name, mn_name)) { + return (Mnemonic)m; + } + } + return Mnemonic_Null; +} + +static const char * conditionStrings[ConditionMnemonic_Count] = { + "O", + "NO", + "B", + "AE", + "Z", + "NZ", + "BE", + "A", + + "S", + "NS", + "P", + "NP", + "L", + "GE", + "LE", + "G", +}; + +const char * getConditionString(ConditionMnemonic cm) { + return conditionStrings[cm]; +} + +static const struct { + char sizeString[12]; + OpndSize size; +} +sizes[] = { + { "Sz8", OpndSize_8 }, + { "Sz16", OpndSize_16 }, + { "Sz32", OpndSize_32 }, + { "Sz64", OpndSize_64 }, +#if !defined(TESTING_ENCODER) + { "Sz80", OpndSize_80 }, + { "Sz128", OpndSize_128 }, +#endif + { "SzAny", OpndSize_Any }, +}; + + +OpndSize getOpndSize(const char * sizeString) +{ + assert(sizeString); + for (unsigned i = 0; i<COUNTOF(sizes); i++) { + if (!strcmpi(sizeString, sizes[i].sizeString)) { + return sizes[i].size; + } + } + return OpndSize_Null; +} + +const char * getOpndSizeString(OpndSize size) { + for( unsigned i = 0; i<COUNTOF(sizes); i++ ) { + if( sizes[i].size==size ) { + return sizes[i].sizeString; + } + } + return NULL; +} + +static const struct { + char kindString[16]; + OpndKind kind; +} +kinds[] = { + { "Null", OpndKind_Null }, + { "GPReg", OpndKind_GPReg }, + { "SReg", OpndKind_SReg }, + { "FPReg", OpndKind_FPReg }, + { "XMMReg", OpndKind_XMMReg }, +#ifdef _HAVE_MMX_ + { "MMXReg", OpndKind_MMXReg }, +#endif + { "StatusReg", OpndKind_StatusReg }, + { "Reg", OpndKind_Reg }, + { "Imm", OpndKind_Imm }, + { "Mem", OpndKind_Mem }, + { "Any", OpndKind_Any }, +}; + +const char * getOpndKindString(OpndKind kind) +{ + for (unsigned i = 0; i<COUNTOF(kinds); i++) { + if (kinds[i].kind==kind) { + return kinds[i].kindString; + } + } + return NULL; +} + +OpndKind getOpndKind(const char * kindString) +{ + assert(kindString); + for (unsigned i = 0; i<COUNTOF(kinds); i++) { + if (!strcmpi(kindString, kinds[i].kindString)) { + return kinds[i].kind; + } + } + return OpndKind_Null; +} + +/** + * A mapping between register string representation and its RegName constant. + */ +static const struct { + char regstring[7]; + RegName regname; +} + +registers[] = { +#ifdef _EM64T_ + {"RAX", RegName_RAX}, + {"RBX", RegName_RBX}, + {"RCX", RegName_RCX}, + {"RDX", RegName_RDX}, + {"RBP", RegName_RBP}, + {"RSI", RegName_RSI}, + {"RDI", RegName_RDI}, + {"RSP", RegName_RSP}, + {"R8", RegName_R8}, + {"R9", RegName_R9}, + {"R10", RegName_R10}, + {"R11", RegName_R11}, + {"R12", RegName_R12}, + {"R13", RegName_R13}, + {"R14", RegName_R14}, + {"R15", RegName_R15}, +#endif + + {"EAX", RegName_EAX}, + {"ECX", RegName_ECX}, + {"EDX", RegName_EDX}, + {"EBX", RegName_EBX}, + {"ESP", RegName_ESP}, + {"EBP", RegName_EBP}, + {"ESI", RegName_ESI}, + {"EDI", RegName_EDI}, +#ifdef _EM64T_ + {"R8D", RegName_R8D}, + {"R9D", RegName_R9D}, + {"R10D", RegName_R10D}, + {"R11D", RegName_R11D}, + {"R12D", RegName_R12D}, + {"R13D", RegName_R13D}, + {"R14D", RegName_R14D}, + {"R15D", RegName_R15D}, +#endif + + {"AX", RegName_AX}, + {"CX", RegName_CX}, + {"DX", RegName_DX}, + {"BX", RegName_BX}, + {"SP", RegName_SP}, + {"BP", RegName_BP}, + {"SI", RegName_SI}, + {"DI", RegName_DI}, + + {"AL", RegName_AL}, + {"CL", RegName_CL}, + {"DL", RegName_DL}, + {"BL", RegName_BL}, +#if !defined(_EM64T_) + {"AH", RegName_AH}, + {"CH", RegName_CH}, + {"DH", RegName_DH}, + {"BH", RegName_BH}, +#else + {"SPL", RegName_SPL}, + {"BPL", RegName_BPL}, + {"SIL", RegName_SIL}, + {"DIL", RegName_DIL}, + {"R8L", RegName_R8L}, + {"R9L", RegName_R9L}, + {"R10L", RegName_R10L}, + {"R11L", RegName_R11L}, + {"R12L", RegName_R12L}, + {"R13L", RegName_R13L}, + {"R14L", RegName_R14L}, + {"R15L", RegName_R15L}, +#endif + {"ES", RegName_ES}, + {"CS", RegName_CS}, + {"SS", RegName_SS}, + {"DS", RegName_DS}, + {"FS", RegName_FS}, + {"GS", RegName_GS}, + + {"FP0", RegName_FP0}, +/* + {"FP1", RegName_FP1}, + {"FP2", RegName_FP2}, + {"FP3", RegName_FP3}, + {"FP4", RegName_FP4}, + {"FP5", RegName_FP5}, + {"FP6", RegName_FP6}, + {"FP7", RegName_FP7}, +*/ + {"FP0S", RegName_FP0S}, + {"FP1S", RegName_FP1S}, + {"FP2S", RegName_FP2S}, + {"FP3S", RegName_FP3S}, + {"FP4S", RegName_FP4S}, + {"FP5S", RegName_FP5S}, + {"FP6S", RegName_FP6S}, + {"FP7S", RegName_FP7S}, + + {"FP0D", RegName_FP0D}, + {"FP1D", RegName_FP1D}, + {"FP2D", RegName_FP2D}, + {"FP3D", RegName_FP3D}, + {"FP4D", RegName_FP4D}, + {"FP5D", RegName_FP5D}, + {"FP6D", RegName_FP6D}, + {"FP7D", RegName_FP7D}, + + {"XMM0", RegName_XMM0}, + {"XMM1", RegName_XMM1}, + {"XMM2", RegName_XMM2}, + {"XMM3", RegName_XMM3}, + {"XMM4", RegName_XMM4}, + {"XMM5", RegName_XMM5}, + {"XMM6", RegName_XMM6}, + {"XMM7", RegName_XMM7}, +#ifdef _EM64T_ + {"XMM8", RegName_XMM8}, + {"XMM9", RegName_XMM9}, + {"XMM10", RegName_XMM10}, + {"XMM11", RegName_XMM11}, + {"XMM12", RegName_XMM12}, + {"XMM13", RegName_XMM13}, + {"XMM14", RegName_XMM14}, + {"XMM15", RegName_XMM15}, +#endif + + + {"XMM0S", RegName_XMM0S}, + {"XMM1S", RegName_XMM1S}, + {"XMM2S", RegName_XMM2S}, + {"XMM3S", RegName_XMM3S}, + {"XMM4S", RegName_XMM4S}, + {"XMM5S", RegName_XMM5S}, + {"XMM6S", RegName_XMM6S}, + {"XMM7S", RegName_XMM7S}, +#ifdef _EM64T_ + {"XMM8S", RegName_XMM8S}, + {"XMM9S", RegName_XMM9S}, + {"XMM10S", RegName_XMM10S}, + {"XMM11S", RegName_XMM11S}, + {"XMM12S", RegName_XMM12S}, + {"XMM13S", RegName_XMM13S}, + {"XMM14S", RegName_XMM14S}, + {"XMM15S", RegName_XMM15S}, +#endif + + {"XMM0D", RegName_XMM0D}, + {"XMM1D", RegName_XMM1D}, + {"XMM2D", RegName_XMM2D}, + {"XMM3D", RegName_XMM3D}, + {"XMM4D", RegName_XMM4D}, + {"XMM5D", RegName_XMM5D}, + {"XMM6D", RegName_XMM6D}, + {"XMM7D", RegName_XMM7D}, +#ifdef _EM64T_ + {"XMM8D", RegName_XMM8D}, + {"XMM9D", RegName_XMM9D}, + {"XMM10D", RegName_XMM10D}, + {"XMM11D", RegName_XMM11D}, + {"XMM12D", RegName_XMM12D}, + {"XMM13D", RegName_XMM13D}, + {"XMM14D", RegName_XMM14D}, + {"XMM15D", RegName_XMM15D}, +#endif + + {"EFLGS", RegName_EFLAGS}, +}; + + +const char * getRegNameString(RegName reg) +{ + for (unsigned i = 0; i<COUNTOF(registers); i++) { + if (registers[i].regname == reg) { + return registers[i].regstring; + } + } + return "(null)"; +} + +RegName getRegName(const char * regname) +{ + if (NULL == regname) { + return RegName_Null; + } + + for (unsigned i = 0; i<COUNTOF(registers); i++) { + if (!strcmpi(regname,registers[i].regstring)) { + return registers[i].regname; + } + } + return RegName_Null; +} + +ENCODER_NAMESPACE_END diff --git a/libpixelflinger/codeflinger/x86/libenc/enc_base.h b/libpixelflinger/codeflinger/x86/libenc/enc_base.h new file mode 100644 index 0000000..fa1062d --- /dev/null +++ b/libpixelflinger/codeflinger/x86/libenc/enc_base.h @@ -0,0 +1,748 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexander V. Astapchuk + */ + +/** + * @file + * @brief Main encoding routines and structures. + */ + +#ifndef __ENC_BASE_H_INCLUDED__ +#define __ENC_BASE_H_INCLUDED__ + +#include "enc_defs.h" + + +#include <stdlib.h> +#include <assert.h> +#include <memory.h> + +ENCODER_NAMESPACE_START +struct MnemonicInfo; +struct OpcodeInfo; +struct Rex; + +/** + * @brief Basic facilities for generation of processor's instructions. + * + * The class EncoderBase represents the basic facilities for the encoding of + * processor's instructions on IA32 and EM64T platforms. + * + * The class provides general interface to generate the instructions as well + * as to retrieve some static data about instructions (number of arguments, + * their roles, etc). + * + * Currently, the EncoderBase class is used for both LIL and Jitrino code + * generators. Each of these code generators has its own wrapper to adapt + * this general interface for specific needs - see encoder.h for LIL wrappers + * and Ia32Encoder.h for Jitrino's adapter. + * + * Interface is provided through static methods, no instances of EncoderBase + * to be created. + * + * @todo RIP-based addressing on EM64T - it's not yet supported currently. + */ +class EncoderBase { +public: + class Operands; + struct MnemonicDesc; + /** + * @brief Generates processor's instruction. + * + * @param stream - a buffer to generate into + * @param mn - \link Mnemonic mnemonic \endlink of the instruction + * @param opnds - operands for the instruction + * @returns (stream + length of the just generated instruction) + */ + static char * encode(char * stream, Mnemonic mn, const Operands& opnds); + static char * getOpndLocation(int index); + + /** + * @brief Generates the smallest possible number of NOP-s. + * + * Effectively generates the smallest possible number of instructions, + * which are NOP-s for CPU. Normally used to make a code alignment. + * + * The method inserts exactly number of bytes specified. It's a caller's + * responsibility to make sure the buffer is big enough. + * + * @param stream - buffer where to generate code into, can not be NULL + * @param howMany - how many bytes to fill with NOP-s + * @return \c (stream+howMany) + */ + static char * nops(char * stream, unsigned howMany); + + /** + * @brief Inserts a prefix into the code buffer. + * + * The method writes no more than one byte into the buffer. This is a + * caller's responsibility to make sure the buffer is big enough. + * + * @param stream - buffer where to insert the prefix + * @param pref - prefix to be inserted. If it's InstPrefix_Null, then + * no action performed and return value is \c stream. + * @return \c (stream+1) if pref is not InstPrefix_Null, or \c stream + * otherwise + */ + static char * prefix(char* stream, InstPrefix pref); + + /** + * @brief Determines if operand with opndExt suites the position with instExt. + */ + static bool extAllowed(OpndExt opndExt, OpndExt instExt); + + /** + * @brief Returns MnemonicDesc by the given Mnemonic. + */ + static const MnemonicDesc * getMnemonicDesc(Mnemonic mn) + { + assert(mn < Mnemonic_Count); + return mnemonics + mn; + } + + /** + * @brief Returns a Mnemonic for the given name. + * + * The lookup is case insensitive, if no mnemonic found for the given + * string, then Mnemonic_Null returned. + */ + static Mnemonic str2mnemonic(const char * mn_name); + + /** + * @brief Returns a string representation of the given Mnemonic. + * + * If invalid mnemonic passed, then the behavior is unpredictable. + */ + static const char * getMnemonicString(Mnemonic mn) + { + return getMnemonicDesc(mn)->name; + } + + static const char * toStr(Mnemonic mn) + { + return getMnemonicDesc(mn)->name; + } + + + /** + * @brief Description of operand. + * + * Description of an operand in opcode - its kind, size or RegName if + * operand must be a particular register. + */ + struct OpndDesc { + /** + * @brief Location of the operand. + * + * May be a mask, i.e. OpndKind_Imm|OpndKind_Mem. + */ + OpndKind kind; + /** + * @brief Size of the operand. + */ + OpndSize size; + /** + * @brief Extention of the operand. + */ + OpndExt ext; + /** + * @brief Appropriate RegName if operand must reside on a particular + * register (i.e. CWD/CDQ instructions), RegName_Null + * otherwise. + */ + RegName reg; + }; + + /** + * @brief Description of operands' roles in instruction. + */ + struct OpndRolesDesc { + /** + * @brief Total number of operands in the operation. + */ + unsigned count; + /** + * @brief Number of defs in the operation. + */ + unsigned defCount; + /** + * @brief Number of uses in the operation. + */ + unsigned useCount; + /** + * @brief Operand roles, bit-packed. + * + * A bit-packed info about operands' roles. Each operand's role is + * described by two bits, counted from right-to-left - the less + * significant bits (0,1) represent operand#0. + * + * The mask is build by ORing #OpndRole_Def and #OpndRole_Use + * appropriately and shifting left, i.e. operand#0's role would be + * - '(OpndRole_Def|OpndRole_Use)' + * - opnd#1's role would be 'OpndRole_Use<<2' + * - and operand#2's role would be, say, 'OpndRole_Def<<4'. + */ + unsigned roles; + }; + + /** + * @brief Extracts appropriate OpndRole for a given operand. + * + * The order of operands is left-to-right, i.e. for MOV, it + * would be 'MOV op0, op1' + */ + static OpndRole getOpndRoles(OpndRolesDesc ord, unsigned idx) + { + assert(idx < ord.count); + return (OpndRole)(ord.roles>>((ord.count-1-idx)*2) & 0x3); + } + + /** + * @brief Defines the maximum number of operands for an opcode. + * + * The 3 mostly comes from IDIV/IMUL which both may have up to + * 3 operands. + */ + static const unsigned int MAX_NUM_OPCODE_OPERANDS = 3; + + /** + * @brief Info about single opcode - its opcode bytes, operands, + * operands' roles. + */ + union OpcodeDesc { + char dummy[128]; // To make total size a power of 2 + + struct { + /** + * @brief Raw opcode bytes. + * + * 'Raw' opcode bytes which do not require any analysis and are + * independent from arguments/sizes/etc (may include opcode size + * prefix). + */ + char opcode[5]; + unsigned opcode_len; + unsigned aux0; + unsigned aux1; + /** + * @brief Info about opcode's operands. + */ + OpndDesc opnds[MAX_NUM_OPCODE_OPERANDS]; + unsigned first_opnd; + /** + * @brief Info about operands - total number, number of uses/defs, + * operands' roles. + */ + OpndRolesDesc roles; + /** + * @brief If not zero, then this is final OpcodeDesc structure in + * the list of opcodes for a given mnemonic. + */ + char last; + char platf; + }; + }; +public: + /** + * @brief General info about mnemonic. + */ + struct MnemonicDesc { + /** + * @brief The mnemonic itself. + */ + Mnemonic mn; + /** + * Various characteristics of mnemonic. + * @see MF_ + */ + unsigned flags; + /** + * @brief Operation's operand's count and roles. + * + * For the operations whose opcodes may use different number of + * operands (i.e. IMUL/SHL) either most common value used, or empty + * value left. + */ + OpndRolesDesc roles; + /** + * @brief Print name of the mnemonic. + */ + const char * name; + }; + + + /** + * @brief Magic number, shows a maximum value a hash code can take. + * + * For meaning and arithmetics see enc_tabl.cpp. + * + * The value was increased from '5155' to '8192' to make it aligned + * for faster access in EncoderBase::lookup(). + * + * It was further increased to 16384 as support for 3 operand opcodes + * with XMM registers were added + */ + static const unsigned int HASH_MAX = 16384; //5155; + /** + * @brief Empty value, used in hash-to-opcode map to show an empty slot. + */ + static const unsigned char NOHASH = 0xFF; + /** + * @brief The name says it all. + */ + static const unsigned char HASH_BITS_PER_OPERAND = 5; + + /** + * @brief Contains info about a single instructions's operand - its + * location, size and a value for immediate or RegName for + * register operands. + */ + class Operand { + public: + /** + * @brief Initializes the instance with empty size and kind. + */ + Operand() : m_kind(OpndKind_Null), m_size(OpndSize_Null), m_ext(OpndExt_None), m_need_rex(false) {} + /** + * @brief Creates register operand from given RegName. + */ + Operand(RegName reg, OpndExt ext = OpndExt_None) : m_kind(getRegKind(reg)), + m_size(getRegSize(reg)), + m_ext(ext), m_reg(reg) + { + hash_it(); + } + /** + * @brief Creates register operand from given RegName and with the + * specified size and kind. + * + * Used to speedup Operand creation as there is no need to extract + * size and kind from the RegName. + * The provided size and kind must match the RegName's ones though. + */ + Operand(OpndSize sz, OpndKind kind, RegName reg, OpndExt ext = OpndExt_None) : + m_kind(kind), m_size(sz), m_ext(ext), m_reg(reg) + { + assert(m_size == getRegSize(reg)); + assert(m_kind == getRegKind(reg)); + hash_it(); + } + /** + * @brief Creates immediate operand with the given size and value. + */ + Operand(OpndSize size, long long ival, OpndExt ext = OpndExt_None) : + m_kind(OpndKind_Imm), m_size(size), m_ext(ext), m_imm64(ival) + { + hash_it(); + } + /** + * @brief Creates immediate operand of OpndSize_32. + */ + Operand(int ival, OpndExt ext = OpndExt_None) : + m_kind(OpndKind_Imm), m_size(OpndSize_32), m_ext(ext), m_imm64(ival) + { + hash_it(); + } + /** + * @brief Creates immediate operand of OpndSize_16. + */ + Operand(short ival, OpndExt ext = OpndExt_None) : + m_kind(OpndKind_Imm), m_size(OpndSize_16), m_ext(ext), m_imm64(ival) + { + hash_it(); + } + + /** + * @brief Creates immediate operand of OpndSize_8. + */ + Operand(char ival, OpndExt ext = OpndExt_None) : + m_kind(OpndKind_Imm), m_size(OpndSize_8), m_ext(ext), m_imm64(ival) + { + hash_it(); + } + + /** + * @brief Creates memory operand. + */ + Operand(OpndSize size, RegName base, RegName index, unsigned scale, + int disp, OpndExt ext = OpndExt_None) : m_kind(OpndKind_Mem), m_size(size), m_ext(ext) + { + m_base = base; + m_index = index; + m_scale = scale; + m_disp = disp; + hash_it(); + } + + /** + * @brief Creates memory operand with only base and displacement. + */ + Operand(OpndSize size, RegName base, int disp, OpndExt ext = OpndExt_None) : + m_kind(OpndKind_Mem), m_size(size), m_ext(ext) + { + m_base = base; + m_index = RegName_Null; + m_scale = 0; + m_disp = disp; + hash_it(); + } + // + // general info + // + /** + * @brief Returns kind of the operand. + */ + OpndKind kind(void) const { return m_kind; } + /** + * @brief Returns size of the operand. + */ + OpndSize size(void) const { return m_size; } + /** + * @brief Returns extention of the operand. + */ + OpndExt ext(void) const { return m_ext; } + /** + * @brief Returns hash of the operand. + */ + unsigned hash(void) const { return m_hash; } + // +#ifdef _EM64T_ + bool need_rex(void) const { return m_need_rex; } +#else + bool need_rex(void) const { return false; } +#endif + /** + * @brief Tests whether operand is memory operand. + */ + bool is_mem(void) const { return is_placed_in(OpndKind_Mem); } + /** + * @brief Tests whether operand is immediate operand. + */ + bool is_imm(void) const { return is_placed_in(OpndKind_Imm); } + /** + * @brief Tests whether operand is register operand. + */ + bool is_reg(void) const { return is_placed_in(OpndKind_Reg); } + /** + * @brief Tests whether operand is general-purpose register operand. + */ + bool is_gpreg(void) const { return is_placed_in(OpndKind_GPReg); } + /** + * @brief Tests whether operand is float-point pseudo-register operand. + */ + bool is_fpreg(void) const { return is_placed_in(OpndKind_FPReg); } + /** + * @brief Tests whether operand is XMM register operand. + */ + bool is_xmmreg(void) const { return is_placed_in(OpndKind_XMMReg); } +#ifdef _HAVE_MMX_ + /** + * @brief Tests whether operand is MMX register operand. + */ + bool is_mmxreg(void) const { return is_placed_in(OpndKind_MMXReg); } +#endif + /** + * @brief Tests whether operand is signed immediate operand. + */ + //bool is_signed(void) const { assert(is_imm()); return m_is_signed; } + + /** + * @brief Returns base of memory operand (RegName_Null if not memory). + */ + RegName base(void) const { return is_mem() ? m_base : RegName_Null; } + /** + * @brief Returns index of memory operand (RegName_Null if not memory). + */ + RegName index(void) const { return is_mem() ? m_index : RegName_Null; } + /** + * @brief Returns scale of memory operand (0 if not memory). + */ + unsigned scale(void) const { return is_mem() ? m_scale : 0; } + /** + * @brief Returns displacement of memory operand (0 if not memory). + */ + int disp(void) const { return is_mem() ? m_disp : 0; } + /** + * @brief Returns RegName of register operand (RegName_Null if not + * register). + */ + RegName reg(void) const { return is_reg() ? m_reg : RegName_Null; } + /** + * @brief Returns value of immediate operand (0 if not immediate). + */ + long long imm(void) const { return is_imm() ? m_imm64 : 0; } + private: + bool is_placed_in(OpndKind kd) const + { + return kd == OpndKind_Reg ? + m_kind == OpndKind_GPReg || +#ifdef _HAVE_MMX_ + m_kind == OpndKind_MMXReg || +#endif + m_kind == OpndKind_FPReg || + m_kind == OpndKind_XMMReg + : kd == m_kind; + } + void hash_it(void) + { + m_hash = get_size_hash(m_size) | get_kind_hash(m_kind); +#ifdef _EM64T_ + m_need_rex = false; + if (is_reg() && is_em64t_extra_reg(m_reg)) { + m_need_rex = true; + } + else if (is_mem() && (is_em64t_extra_reg(m_base) || + is_em64t_extra_reg(m_index))) { + m_need_rex = true; + } +#endif + } + // general info + OpndKind m_kind; + OpndSize m_size; + OpndExt m_ext; + // complex address form support + RegName m_base; + RegName m_index; + unsigned m_scale; + union { + int m_disp; + RegName m_reg; + long long m_imm64; + }; + unsigned m_hash; + bool m_need_rex; + friend class EncoderBase::Operands; + }; + /** + * @brief Simple container for up to 3 Operand-s. + */ + class Operands { + public: + Operands(void) + { + clear(); + } + Operands(const Operand& op0) + { + clear(); + add(op0); + } + + Operands(const Operand& op0, const Operand& op1) + { + clear(); + add(op0); add(op1); + } + + Operands(const Operand& op0, const Operand& op1, const Operand& op2) + { + clear(); + add(op0); add(op1); add(op2); + } + + unsigned count(void) const { return m_count; } + unsigned hash(void) const { return m_hash; } + const Operand& operator[](unsigned idx) const + { + assert(idx<m_count); + return m_operands[idx]; + } + + void add(const Operand& op) + { + assert(m_count < COUNTOF(m_operands)); + m_hash = (m_hash<<HASH_BITS_PER_OPERAND) | op.hash(); + m_operands[m_count++] = op; + m_need_rex = m_need_rex || op.m_need_rex; + } +#ifdef _EM64T_ + bool need_rex(void) const { return m_need_rex; } +#else + bool need_rex(void) const { return false; } +#endif + void clear(void) + { + m_count = 0; m_hash = 0; m_need_rex = false; + } + private: + unsigned m_count; + Operand m_operands[COUNTOF( ((OpcodeDesc*)NULL)->opnds )]; + unsigned m_hash; + bool m_need_rex; + }; +public: +#ifdef _DEBUG + /** + * Verifies some presumptions about encoding data table. + * Called automaticaly during statics initialization. + */ + static int verify(void); +#endif + +private: + /** + * @brief Returns found OpcodeDesc by the given Mnemonic and operands. + */ + static const OpcodeDesc * lookup(Mnemonic mn, const Operands& opnds); + /** + * @brief Encodes mod/rm byte. + */ + static char* encodeModRM(char* stream, const Operands& opnds, + unsigned idx, const OpcodeDesc * odesc, Rex * prex); + /** + * @brief Encodes special things of opcode description - '/r', 'ib', etc. + */ + static char* encode_aux(char* stream, unsigned aux, + const Operands& opnds, const OpcodeDesc * odesc, + unsigned * pargsCount, Rex* prex); +#ifdef _EM64T_ + /** + * @brief Returns true if the 'reg' argument represents one of the new + * EM64T registers - R8(D)-R15(D). + * + * The 64 bits versions of 'old-fashion' registers, i.e. RAX are not + * considered as 'extra'. + */ + static bool is_em64t_extra_reg(const RegName reg) + { + if (needs_rex_r(reg)) { + return true; + } + if (RegName_SPL <= reg && reg <= RegName_R15L) { + return true; + } + return false; + } + static bool needs_rex_r(const RegName reg) + { + if (RegName_R8 <= reg && reg <= RegName_R15) { + return true; + } + if (RegName_R8D <= reg && reg <= RegName_R15D) { + return true; + } + if (RegName_R8S <= reg && reg <= RegName_R15S) { + return true; + } + if (RegName_R8L <= reg && reg <= RegName_R15L) { + return true; + } + if (RegName_XMM8 <= reg && reg <= RegName_XMM15) { + return true; + } + if (RegName_XMM8D <= reg && reg <= RegName_XMM15D) { + return true; + } + if (RegName_XMM8S <= reg && reg <= RegName_XMM15S) { + return true; + } + return false; + } + /** + * @brief Returns an 'processor's index' of the register - the index + * used to encode the register in ModRM/SIB bytes. + * + * For the new EM64T registers the 'HW index' differs from the index + * encoded in RegName. For old-fashion registers it's effectively the + * same as ::getRegIndex(RegName). + */ + static unsigned char getHWRegIndex(const RegName reg) + { + if (getRegKind(reg) != OpndKind_GPReg) { + return getRegIndex(reg); + } + if (RegName_SPL <= reg && reg<=RegName_DIL) { + return getRegIndex(reg); + } + if (RegName_R8L<= reg && reg<=RegName_R15L) { + return getRegIndex(reg) - getRegIndex(RegName_R8L); + } + return is_em64t_extra_reg(reg) ? + getRegIndex(reg)-getRegIndex(RegName_R8D) : getRegIndex(reg); + } +#else + static unsigned char getHWRegIndex(const RegName reg) + { + return getRegIndex(reg); + } + static bool is_em64t_extra_reg(const RegName reg) + { + return false; + } +#endif +public: + static unsigned char get_size_hash(OpndSize size) { + return (size <= OpndSize_64) ? size_hash[size] : 0xFF; + } + static unsigned char get_kind_hash(OpndKind kind) { + return (kind <= OpndKind_Mem) ? kind_hash[kind] : 0xFF; + } + + /** + * @brief A table used for the fast computation of hash value. + * + * A change must be strictly balanced with hash-related functions and data + * in enc_base.h/.cpp. + */ + static const unsigned char size_hash[OpndSize_64+1]; + /** + * @brief A table used for the fast computation of hash value. + * + * A change must be strictly balanced with hash-related functions and data + * in enc_base.h/.cpp. + */ + static const unsigned char kind_hash[OpndKind_Mem+1]; + /** + * @brief Maximum number of opcodes used for a single mnemonic. + * + * No arithmetics behind the number, simply estimated. + */ + static const unsigned int MAX_OPCODES = 32; //20; + /** + * @brief Mapping between operands hash code and operands. + */ + static unsigned char opcodesHashMap[Mnemonic_Count][HASH_MAX]; + /** + * @brief Array of mnemonics. + */ + static MnemonicDesc mnemonics[Mnemonic_Count]; + /** + * @brief Array of available opcodes. + */ + static OpcodeDesc opcodes[Mnemonic_Count][MAX_OPCODES]; + + static int buildTable(void); + static void buildMnemonicDesc(const MnemonicInfo * minfo); + /** + * @brief Computes hash value for the given operands. + */ + static unsigned short getHash(const OpcodeInfo* odesc); + /** + * @brief Dummy variable, for automatic invocation of buildTable() at + * startup. + */ + static int dummy; + + static char * curRelOpnd[3]; +}; + +ENCODER_NAMESPACE_END + +#endif // ifndef __ENC_BASE_H_INCLUDED__ diff --git a/libpixelflinger/codeflinger/x86/libenc/enc_defs.h b/libpixelflinger/codeflinger/x86/libenc/enc_defs.h new file mode 100644 index 0000000..10409d2 --- /dev/null +++ b/libpixelflinger/codeflinger/x86/libenc/enc_defs.h @@ -0,0 +1,786 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexander V. Astapchuk + */ +#ifndef _ENCODER_DEFS_H_ +#define _ENCODER_DEFS_H_ + + +// Used to isolate experimental or being tuned encoder into a separate +// namespace so it can coexist with a stable one in the same bundle. +#ifdef ENCODER_ISOLATE + #define ENCODER_NAMESPACE_START namespace enc_ia32 { + #define ENCODER_NAMESPACE_END }; +#else + #define ENCODER_NAMESPACE_START + #define ENCODER_NAMESPACE_END +#endif + +#include <assert.h> +#include "enc_defs_ext.h" + +#ifndef COUNTOF + /** + * Number of items in an array. + */ + #define COUNTOF(a) (sizeof(a)/sizeof(a[0])) +#endif + +#ifdef _EM64T_ + /** + * A stack pointer of default platform's size. + */ + #define REG_STACK RegName_RSP + /** + * A max GP register (with a highest index number) + */ + #define REG_MAX RegName_R15 + /** + * Total number of GP registers including stack pointer. + */ + #define MAX_REGS 15 +#else + #define REG_STACK RegName_ESP + #define REG_MAX RegName_EDI + #define MAX_REGS 8 +#endif + +ENCODER_NAMESPACE_START + +/** + * A number of bytes 'eaten' by an ordinary PUSH/POP. + */ +#define STACK_SLOT_SIZE (sizeof(void*)) + + +/** + * A recommended by Intel Arch Manual aligment for instructions that + * are targets for jmps. + */ +#define JMP_TARGET_ALIGMENT (16) +/** + * A maximum possible size of native instruction. + */ +#define MAX_NATIVE_INST_SIZE (15) +/** + * The enum OpndKind describes an operand's location - memory, immediate or a register. + * It can be used as a bit mask. + */ +typedef enum OpndKind { + /** + * A change must be balanced with at least the following places: + * Ia32::Constraint-s use the OpndKind as a mask + * encoder.cpp & encoder_master_info.cpp uses OpndKind as an index for hashing + * - perhaps there are much more places + * + * NOTE: an MMXReg kind is incompatible with the current constraints framework, + * as it's not encoded as a mask. + */ + OpndKind_Null=0, + OpndKind_GPReg = 0x01, OpndKind_MinRegKind = OpndKind_GPReg, + OpndKind_SReg = 0x02, +#ifdef _HAVE_MMX_ + OpndKind_MMXReg = 0x03, +#endif + OpndKind_FPReg = 0x04, + OpndKind_XMMReg = 0x08, + OpndKind_OtherReg = 0x10, + OpndKind_StatusReg = OpndKind_OtherReg, + OpndKind_MaxRegKind = OpndKind_StatusReg, // a max existing kind of register + OpndKind_MaxReg, // -'- + 1 to be used in array defs + // + OpndKind_Immediate = 0x20, OpndKind_Imm=OpndKind_Immediate, + OpndKind_Memory = 0x40, OpndKind_Mem=OpndKind_Memory, + // + OpndKind_Reg = 0x1F, + OpndKind_Any = 0x7F, + // syntetic constants. Normally not used anywhere, but are used for + // human-readable showing under the debugger + OpndKind_GPReg_Mem = OpndKind_GPReg|OpndKind_Mem, +#ifdef _HAVE_MMX_ + OpndKind_MMXReg_Mem = OpndKind_MMXReg|OpndKind_Mem, +#endif + OpndKind_XMMReg_Mem = OpndKind_XMMReg|OpndKind_Mem, +} OpndKind; + +/** + * Defines type of extention allowed for particular operand. + * For example imul r32,r_m32,imm8 sign extend imm8 before performing multiplication. + * To satisfy instruction constraints immediate operand should be either OpndExt_Signed + * or OpndExt_Any. + */ +typedef enum OpndExt { + OpndExt_None = 0x0, + OpndExt_Signed = 0x1, + OpndExt_Zero = 0x2, + OpndExt_Any = 0x3, +}OpndExt; + +/** + * enum OpndRole defines the role of an operand in an instruction + * Can be used as mask to combine def and use. The complete def+use + * info can be combined in 2 bits which is used, say in Encoder::OpndRole. + */ +//TODO: this duplicates an Role used in the Ia32::Inst. That duplicate enum should be removed. +typedef enum OpndRole { + OpndRole_Null=0, + OpndRole_Use=0x1, + OpndRole_Def=0x2, + OpndRole_UseDef=OpndRole_Use|OpndRole_Def, + OpndRole_All=0xffff, +} OpndRole; + + +#define REGNAME(k,s,i) ( ((k & OpndKind_Any)<<24) | ((s & OpndSize_Any)<<16) | (i&0xFF) ) + +// Gregory - +// It is critical that all register indexes (3rd number) inside of the +// following table go in ascending order. That is R8 goes after +// RDI. It is necessary for decoder when extending registers from RAX-RDI +// to R8-R15 by simply adding 8 to the index on EM64T architecture +typedef enum RegName { + + RegName_Null = 0, + +#ifdef _EM64T_ + /* + An index part of the RegName-s for RAX-RDI, EAX-ESI, AX-SI and AL-BH is + the same as the index used during instructions encoding. The same rule + applies for XMM regsters for IA32. + For new EM64T registers (both GP and XMM) the index need to be corrected to + obtain the index used in processor's instructions. + */ + RegName_RAX = REGNAME(OpndKind_GPReg,OpndSize_64,0), + RegName_RCX = REGNAME(OpndKind_GPReg,OpndSize_64,1), + RegName_RDX = REGNAME(OpndKind_GPReg,OpndSize_64,2), + RegName_RBX = REGNAME(OpndKind_GPReg,OpndSize_64,3), + RegName_RSP = REGNAME(OpndKind_GPReg,OpndSize_64,4), + RegName_RBP = REGNAME(OpndKind_GPReg,OpndSize_64,5), + RegName_RSI = REGNAME(OpndKind_GPReg,OpndSize_64,6), + RegName_RDI = REGNAME(OpndKind_GPReg,OpndSize_64,7), + + RegName_R8 = REGNAME(OpndKind_GPReg,OpndSize_64,8), + RegName_R9 = REGNAME(OpndKind_GPReg,OpndSize_64,9), + RegName_R10 = REGNAME(OpndKind_GPReg,OpndSize_64,10), + RegName_R11 = REGNAME(OpndKind_GPReg,OpndSize_64,11), + RegName_R12 = REGNAME(OpndKind_GPReg,OpndSize_64,12), + RegName_R13 = REGNAME(OpndKind_GPReg,OpndSize_64,13), + RegName_R14 = REGNAME(OpndKind_GPReg,OpndSize_64,14), + RegName_R15 = REGNAME(OpndKind_GPReg,OpndSize_64,15), +#endif //~_EM64T_ + + RegName_EAX=REGNAME(OpndKind_GPReg,OpndSize_32,0), + RegName_ECX=REGNAME(OpndKind_GPReg,OpndSize_32,1), + RegName_EDX=REGNAME(OpndKind_GPReg,OpndSize_32,2), + RegName_EBX=REGNAME(OpndKind_GPReg,OpndSize_32,3), + RegName_ESP=REGNAME(OpndKind_GPReg,OpndSize_32,4), + RegName_EBP=REGNAME(OpndKind_GPReg,OpndSize_32,5), + RegName_ESI=REGNAME(OpndKind_GPReg,OpndSize_32,6), + RegName_EDI=REGNAME(OpndKind_GPReg,OpndSize_32,7), + +#ifdef _EM64T_ + RegName_R8D = REGNAME(OpndKind_GPReg,OpndSize_32,8), + RegName_R9D = REGNAME(OpndKind_GPReg,OpndSize_32,9), + RegName_R10D = REGNAME(OpndKind_GPReg,OpndSize_32,10), + RegName_R11D = REGNAME(OpndKind_GPReg,OpndSize_32,11), + RegName_R12D = REGNAME(OpndKind_GPReg,OpndSize_32,12), + RegName_R13D = REGNAME(OpndKind_GPReg,OpndSize_32,13), + RegName_R14D = REGNAME(OpndKind_GPReg,OpndSize_32,14), + RegName_R15D = REGNAME(OpndKind_GPReg,OpndSize_32,15), +#endif //~_EM64T_ + + RegName_AX=REGNAME(OpndKind_GPReg,OpndSize_16,0), + RegName_CX=REGNAME(OpndKind_GPReg,OpndSize_16,1), + RegName_DX=REGNAME(OpndKind_GPReg,OpndSize_16,2), + RegName_BX=REGNAME(OpndKind_GPReg,OpndSize_16,3), + RegName_SP=REGNAME(OpndKind_GPReg,OpndSize_16,4), + RegName_BP=REGNAME(OpndKind_GPReg,OpndSize_16,5), + RegName_SI=REGNAME(OpndKind_GPReg,OpndSize_16,6), + RegName_DI=REGNAME(OpndKind_GPReg,OpndSize_16,7), + +#ifdef _EM64T_ + RegName_R8S = REGNAME(OpndKind_GPReg,OpndSize_16,8), + RegName_R9S = REGNAME(OpndKind_GPReg,OpndSize_16,9), + RegName_R10S = REGNAME(OpndKind_GPReg,OpndSize_16,10), + RegName_R11S = REGNAME(OpndKind_GPReg,OpndSize_16,11), + RegName_R12S = REGNAME(OpndKind_GPReg,OpndSize_16,12), + RegName_R13S = REGNAME(OpndKind_GPReg,OpndSize_16,13), + RegName_R14S = REGNAME(OpndKind_GPReg,OpndSize_16,14), + RegName_R15S = REGNAME(OpndKind_GPReg,OpndSize_16,15), +#endif //~_EM64T_ + + RegName_AL=REGNAME(OpndKind_GPReg,OpndSize_8,0), + RegName_CL=REGNAME(OpndKind_GPReg,OpndSize_8,1), + RegName_DL=REGNAME(OpndKind_GPReg,OpndSize_8,2), + RegName_BL=REGNAME(OpndKind_GPReg,OpndSize_8,3), + // FIXME: Used in enc_tabl.cpp + // AH is not accessible on EM64T, instead encoded register is SPL, so decoded + // register will return incorrect enum + RegName_AH=REGNAME(OpndKind_GPReg,OpndSize_8,4), +#if !defined(_EM64T_) + RegName_CH=REGNAME(OpndKind_GPReg,OpndSize_8,5), + RegName_DH=REGNAME(OpndKind_GPReg,OpndSize_8,6), + RegName_BH=REGNAME(OpndKind_GPReg,OpndSize_8,7), +#else + RegName_SPL=REGNAME(OpndKind_GPReg,OpndSize_8,4), + RegName_BPL=REGNAME(OpndKind_GPReg,OpndSize_8,5), + RegName_SIL=REGNAME(OpndKind_GPReg,OpndSize_8,6), + RegName_DIL=REGNAME(OpndKind_GPReg,OpndSize_8,7), + RegName_R8L=REGNAME(OpndKind_GPReg,OpndSize_8,8), + RegName_R9L=REGNAME(OpndKind_GPReg,OpndSize_8,9), + RegName_R10L=REGNAME(OpndKind_GPReg,OpndSize_8,10), + RegName_R11L=REGNAME(OpndKind_GPReg,OpndSize_8,11), + RegName_R12L=REGNAME(OpndKind_GPReg,OpndSize_8,12), + RegName_R13L=REGNAME(OpndKind_GPReg,OpndSize_8,13), + RegName_R14L=REGNAME(OpndKind_GPReg,OpndSize_8,14), + RegName_R15L=REGNAME(OpndKind_GPReg,OpndSize_8,15), +#endif + + RegName_ES=REGNAME(OpndKind_SReg,OpndSize_16,0), + RegName_CS=REGNAME(OpndKind_SReg,OpndSize_16,1), + RegName_SS=REGNAME(OpndKind_SReg,OpndSize_16,2), + RegName_DS=REGNAME(OpndKind_SReg,OpndSize_16,3), + RegName_FS=REGNAME(OpndKind_SReg,OpndSize_16,4), + RegName_GS=REGNAME(OpndKind_SReg,OpndSize_16,5), + + RegName_EFLAGS=REGNAME(OpndKind_StatusReg,OpndSize_32,0), + +#if !defined(TESTING_ENCODER) + RegName_FP0=REGNAME(OpndKind_FPReg,OpndSize_80,0), + RegName_FP1=REGNAME(OpndKind_FPReg,OpndSize_80,1), + RegName_FP2=REGNAME(OpndKind_FPReg,OpndSize_80,2), + RegName_FP3=REGNAME(OpndKind_FPReg,OpndSize_80,3), + RegName_FP4=REGNAME(OpndKind_FPReg,OpndSize_80,4), + RegName_FP5=REGNAME(OpndKind_FPReg,OpndSize_80,5), + RegName_FP6=REGNAME(OpndKind_FPReg,OpndSize_80,6), + RegName_FP7=REGNAME(OpndKind_FPReg,OpndSize_80,7), +#endif + RegName_FP0S=REGNAME(OpndKind_FPReg,OpndSize_32,0), + RegName_FP1S=REGNAME(OpndKind_FPReg,OpndSize_32,1), + RegName_FP2S=REGNAME(OpndKind_FPReg,OpndSize_32,2), + RegName_FP3S=REGNAME(OpndKind_FPReg,OpndSize_32,3), + RegName_FP4S=REGNAME(OpndKind_FPReg,OpndSize_32,4), + RegName_FP5S=REGNAME(OpndKind_FPReg,OpndSize_32,5), + RegName_FP6S=REGNAME(OpndKind_FPReg,OpndSize_32,6), + RegName_FP7S=REGNAME(OpndKind_FPReg,OpndSize_32,7), + + RegName_FP0D=REGNAME(OpndKind_FPReg,OpndSize_64,0), + RegName_FP1D=REGNAME(OpndKind_FPReg,OpndSize_64,1), + RegName_FP2D=REGNAME(OpndKind_FPReg,OpndSize_64,2), + RegName_FP3D=REGNAME(OpndKind_FPReg,OpndSize_64,3), + RegName_FP4D=REGNAME(OpndKind_FPReg,OpndSize_64,4), + RegName_FP5D=REGNAME(OpndKind_FPReg,OpndSize_64,5), + RegName_FP6D=REGNAME(OpndKind_FPReg,OpndSize_64,6), + RegName_FP7D=REGNAME(OpndKind_FPReg,OpndSize_64,7), + +#if !defined(TESTING_ENCODER) + RegName_XMM0=REGNAME(OpndKind_XMMReg,OpndSize_128,0), + RegName_XMM1=REGNAME(OpndKind_XMMReg,OpndSize_128,1), + RegName_XMM2=REGNAME(OpndKind_XMMReg,OpndSize_128,2), + RegName_XMM3=REGNAME(OpndKind_XMMReg,OpndSize_128,3), + RegName_XMM4=REGNAME(OpndKind_XMMReg,OpndSize_128,4), + RegName_XMM5=REGNAME(OpndKind_XMMReg,OpndSize_128,5), + RegName_XMM6=REGNAME(OpndKind_XMMReg,OpndSize_128,6), + RegName_XMM7=REGNAME(OpndKind_XMMReg,OpndSize_128,7), + +#ifdef _EM64T_ + RegName_XMM8 = REGNAME(OpndKind_XMMReg,OpndSize_128,0), + RegName_XMM9 = REGNAME(OpndKind_XMMReg,OpndSize_128,1), + RegName_XMM10 = REGNAME(OpndKind_XMMReg,OpndSize_128,2), + RegName_XMM11 = REGNAME(OpndKind_XMMReg,OpndSize_128,3), + RegName_XMM12 = REGNAME(OpndKind_XMMReg,OpndSize_128,4), + RegName_XMM13 = REGNAME(OpndKind_XMMReg,OpndSize_128,5), + RegName_XMM14 = REGNAME(OpndKind_XMMReg,OpndSize_128,6), + RegName_XMM15 = REGNAME(OpndKind_XMMReg,OpndSize_128,7), +#endif //~_EM64T_ + +#endif // ~TESTING_ENCODER + + RegName_XMM0S=REGNAME(OpndKind_XMMReg,OpndSize_32,0), + RegName_XMM1S=REGNAME(OpndKind_XMMReg,OpndSize_32,1), + RegName_XMM2S=REGNAME(OpndKind_XMMReg,OpndSize_32,2), + RegName_XMM3S=REGNAME(OpndKind_XMMReg,OpndSize_32,3), + RegName_XMM4S=REGNAME(OpndKind_XMMReg,OpndSize_32,4), + RegName_XMM5S=REGNAME(OpndKind_XMMReg,OpndSize_32,5), + RegName_XMM6S=REGNAME(OpndKind_XMMReg,OpndSize_32,6), + RegName_XMM7S=REGNAME(OpndKind_XMMReg,OpndSize_32,7), +#ifdef _EM64T_ + RegName_XMM8S=REGNAME(OpndKind_XMMReg,OpndSize_32,8), + RegName_XMM9S=REGNAME(OpndKind_XMMReg,OpndSize_32,9), + RegName_XMM10S=REGNAME(OpndKind_XMMReg,OpndSize_32,10), + RegName_XMM11S=REGNAME(OpndKind_XMMReg,OpndSize_32,11), + RegName_XMM12S=REGNAME(OpndKind_XMMReg,OpndSize_32,12), + RegName_XMM13S=REGNAME(OpndKind_XMMReg,OpndSize_32,13), + RegName_XMM14S=REGNAME(OpndKind_XMMReg,OpndSize_32,14), + RegName_XMM15S=REGNAME(OpndKind_XMMReg,OpndSize_32,15), +#endif // ifdef _EM64T_ + RegName_XMM0D=REGNAME(OpndKind_XMMReg,OpndSize_64,0), + RegName_XMM1D=REGNAME(OpndKind_XMMReg,OpndSize_64,1), + RegName_XMM2D=REGNAME(OpndKind_XMMReg,OpndSize_64,2), + RegName_XMM3D=REGNAME(OpndKind_XMMReg,OpndSize_64,3), + RegName_XMM4D=REGNAME(OpndKind_XMMReg,OpndSize_64,4), + RegName_XMM5D=REGNAME(OpndKind_XMMReg,OpndSize_64,5), + RegName_XMM6D=REGNAME(OpndKind_XMMReg,OpndSize_64,6), + RegName_XMM7D=REGNAME(OpndKind_XMMReg,OpndSize_64,7), +#ifdef _EM64T_ + RegName_XMM8D=REGNAME(OpndKind_XMMReg,OpndSize_64,8), + RegName_XMM9D=REGNAME(OpndKind_XMMReg,OpndSize_64,9), + RegName_XMM10D=REGNAME(OpndKind_XMMReg,OpndSize_64,10), + RegName_XMM11D=REGNAME(OpndKind_XMMReg,OpndSize_64,11), + RegName_XMM12D=REGNAME(OpndKind_XMMReg,OpndSize_64,12), + RegName_XMM13D=REGNAME(OpndKind_XMMReg,OpndSize_64,13), + RegName_XMM14D=REGNAME(OpndKind_XMMReg,OpndSize_64,14), + RegName_XMM15D=REGNAME(OpndKind_XMMReg,OpndSize_64,15), +#endif // ifdef _EM64T_ +#ifdef _HAVE_MMX_ + RegName_MMX0=REGNAME(OpndKind_MMXReg,OpndSize_64,0), + RegName_MMX1=REGNAME(OpndKind_MMXReg,OpndSize_64,1), + RegName_MMX2=REGNAME(OpndKind_MMXReg,OpndSize_64,2), + RegName_MMX3=REGNAME(OpndKind_MMXReg,OpndSize_64,3), + RegName_MMX4=REGNAME(OpndKind_MMXReg,OpndSize_64,4), + RegName_MMX5=REGNAME(OpndKind_MMXReg,OpndSize_64,5), + RegName_MMX6=REGNAME(OpndKind_MMXReg,OpndSize_64,6), + RegName_MMX7=REGNAME(OpndKind_MMXReg,OpndSize_64,7), +#endif // _HAVE_MMX_ +} RegName; + +#if 0 // Android x86: use mnemonics defined in enc_defs_ext.h +/** + * Conditional mnemonics. + * The values match the 'real' (==processor's) values of the appropriate + * condition values used in the opcodes. + */ +enum ConditionMnemonic { + + ConditionMnemonic_O=0, + ConditionMnemonic_NO=1, + ConditionMnemonic_B=2, ConditionMnemonic_NAE=ConditionMnemonic_B, ConditionMnemonic_C=ConditionMnemonic_B, + ConditionMnemonic_NB=3, ConditionMnemonic_AE=ConditionMnemonic_NB, ConditionMnemonic_NC=ConditionMnemonic_NB, + ConditionMnemonic_Z=4, ConditionMnemonic_E=ConditionMnemonic_Z, + ConditionMnemonic_NZ=5, ConditionMnemonic_NE=ConditionMnemonic_NZ, + ConditionMnemonic_BE=6, ConditionMnemonic_NA=ConditionMnemonic_BE, + ConditionMnemonic_NBE=7, ConditionMnemonic_A=ConditionMnemonic_NBE, + + ConditionMnemonic_S=8, + ConditionMnemonic_NS=9, + ConditionMnemonic_P=10, ConditionMnemonic_PE=ConditionMnemonic_P, + ConditionMnemonic_NP=11, ConditionMnemonic_PO=ConditionMnemonic_NP, + ConditionMnemonic_L=12, ConditionMnemonic_NGE=ConditionMnemonic_L, + ConditionMnemonic_NL=13, ConditionMnemonic_GE=ConditionMnemonic_NL, + ConditionMnemonic_LE=14, ConditionMnemonic_NG=ConditionMnemonic_LE, + ConditionMnemonic_NLE=15, ConditionMnemonic_G=ConditionMnemonic_NLE, + ConditionMnemonic_Count=16 +}; + + +#define CCM(prefix,cond) Mnemonic_##prefix##cond=Mnemonic_##prefix##cc+ConditionMnemonic_##cond + +//========================================================================================================= +enum Mnemonic { + +Mnemonic_NULL=0, Mnemonic_Null=Mnemonic_NULL, +Mnemonic_ADC, // Add with Carry +Mnemonic_ADD, // Add +Mnemonic_ADDSD, // Add Scalar Double-Precision Floating-Point Values +Mnemonic_ADDSS, // Add Scalar Single-Precision Floating-Point Values +Mnemonic_AND, // Logical AND + +Mnemonic_BSF, // Bit scan forward +Mnemonic_BSR, // Bit scan reverse + +Mnemonic_CALL, // Call Procedure +Mnemonic_CMC, // Complement Carry Flag +Mnemonic_CWD, Mnemonic_CDQ=Mnemonic_CWD,// Convert Word to Doubleword/Convert Doubleword to Qua T dword +Mnemonic_CMOVcc, // Conditional Move + CCM(CMOV,O), + CCM(CMOV,NO), + CCM(CMOV,B), CCM(CMOV,NAE), CCM(CMOV,C), + CCM(CMOV,NB), CCM(CMOV,AE), CCM(CMOV,NC), + CCM(CMOV,Z), CCM(CMOV,E), + CCM(CMOV,NZ), CCM(CMOV,NE), + CCM(CMOV,BE), CCM(CMOV,NA), + CCM(CMOV,NBE), CCM(CMOV,A), + + CCM(CMOV,S), + CCM(CMOV,NS), + CCM(CMOV,P), CCM(CMOV,PE), + CCM(CMOV,NP), CCM(CMOV,PO), + CCM(CMOV,L), CCM(CMOV,NGE), + CCM(CMOV,NL), CCM(CMOV,GE), + CCM(CMOV,LE), CCM(CMOV,NG), + CCM(CMOV,NLE), CCM(CMOV,G), + +Mnemonic_CMP, // Compare Two Operands +Mnemonic_CMPXCHG, // Compare and exchange +Mnemonic_CMPXCHG8B, // Compare and Exchange 8 Bytes +Mnemonic_CMPSB, // Compare Two Bytes at DS:ESI and ES:EDI +Mnemonic_CMPSW, // Compare Two Words at DS:ESI and ES:EDI +Mnemonic_CMPSD, // Compare Two Doublewords at DS:ESI and ES:EDI +// +// double -> float +Mnemonic_CVTSD2SS, // Convert Scalar Double-Precision Floating-Point Value to Scalar Single-Precision Floating-Point Value +// double -> I_32 +Mnemonic_CVTSD2SI, // Convert Scalar Double-Precision Floating-Point Value to Doubleword Integer +// double [truncated] -> I_32 +Mnemonic_CVTTSD2SI, // Convert with Truncation Scalar Double-Precision Floating-Point Value to Signed Doubleword Integer +// +// float -> double +Mnemonic_CVTSS2SD, // Convert Scalar Single-Precision Floating-Point Value to Scalar Double-Precision Floating-Point Value +// float -> I_32 +Mnemonic_CVTSS2SI, // Convert Scalar Single-Precision Floating-Point Value to Doubleword Integer +// float [truncated] -> I_32 +Mnemonic_CVTTSS2SI, // Convert with Truncation Scalar Single-Precision Floating-Point Value to Doubleword Integer +// +// I_32 -> double +Mnemonic_CVTSI2SD, // Convert Doubleword Integer to Scalar Double-Precision Floating-Point Value +// I_32 -> float +Mnemonic_CVTSI2SS, // Convert Doubleword Integer to Scalar Single-Precision Floating-Point Value + +Mnemonic_COMISD, // Compare Scalar Ordered Double-Precision Floating-Point Values and Set EFLAGS +Mnemonic_COMISS, // Compare Scalar Ordered Single-Precision Floating-Point Values and Set EFLAGS +Mnemonic_DEC, // Decrement by 1 +//Mnemonic_DIV, // Unsigned Divide +Mnemonic_DIVSD, // Divide Scalar Double-Precision Floating-Point Values +Mnemonic_DIVSS, // Divide Scalar Single-Precision Floating-Point Values + +#ifdef _HAVE_MMX_ +Mnemonic_EMMS, // Empty MMX Technology State +#endif + +Mnemonic_ENTER, // ENTER-Make Stack Frame for Procedure Parameters +Mnemonic_FLDCW, // Load FPU control word +Mnemonic_FADDP, +Mnemonic_FLDZ, +Mnemonic_FADD, +Mnemonic_FSUBP, +Mnemonic_FSUB, +Mnemonic_FISUB, +Mnemonic_FMUL, +Mnemonic_FMULP, +Mnemonic_FDIVP, +Mnemonic_FDIV, +Mnemonic_FUCOMPP, +Mnemonic_FRNDINT, +Mnemonic_FNSTCW, // Store FPU control word +Mnemonic_FSTSW, // Store FPU status word +Mnemonic_FNSTSW, // Store FPU status word +//Mnemonic_FDECSTP, // Decrement Stack-Top Pointer +Mnemonic_FILD, // Load Integer +Mnemonic_FLD, // Load Floating Point Value +Mnemonic_FLDLG2, +Mnemonic_FLDLN2, +Mnemonic_FLD1, + +Mnemonic_FCLEX, // Clear Exceptions +Mnemonic_FCHS, // Change sign of ST0 +Mnemonic_FNCLEX, // Clear Exceptions + +//Mnemonic_FINCSTP, // Increment Stack-Top Pointer +Mnemonic_FIST, // Store Integer +Mnemonic_FISTP, // Store Integer, pop FPU stack +Mnemonic_FISTTP, // Store Integer with Truncation +Mnemonic_FPREM, // Partial Remainder +Mnemonic_FPREM1, // Partial Remainder +Mnemonic_FST, // Store Floating Point Value +Mnemonic_FSTP, // Store Floating Point Value and pop the FP stack +Mnemonic_FSQRT, //Computes the square root of the source value in the stack and pop the FP stack +Mnemonic_FABS, //Computes the absolute value of the source value in the stack and pop the FP stack +Mnemonic_FSIN, //Computes the sine of the source value in the stack and pop the FP stack +Mnemonic_FCOS, //Computes the cosine of the source value in the stack and pop the FP stack +Mnemonic_FPTAN, //Computes the tangent of the source value in the stack and pop the FP stack +Mnemonic_FYL2X, +Mnemonic_FYL2XP1, +Mnemonic_F2XM1, +Mnemonic_FPATAN, +Mnemonic_FXCH, +Mnemonic_FSCALE, + +Mnemonic_XCHG, +Mnemonic_DIV, // Unsigned Divide +Mnemonic_IDIV, // Signed Divide +Mnemonic_MUL, // Unsigned Multiply +Mnemonic_IMUL, // Signed Multiply +Mnemonic_INC, // Increment by 1 +Mnemonic_INT3, // Call break point +Mnemonic_Jcc, // Jump if Condition Is Met + CCM(J,O), + CCM(J,NO), + CCM(J,B), CCM(J,NAE), CCM(J,C), + CCM(J,NB), CCM(J,AE), CCM(J,NC), + CCM(J,Z), CCM(J,E), + CCM(J,NZ), CCM(J,NE), + CCM(J,BE), CCM(J,NA), + CCM(J,NBE), CCM(J,A), + CCM(J,S), + CCM(J,NS), + CCM(J,P), CCM(J,PE), + CCM(J,NP), CCM(J,PO), + CCM(J,L), CCM(J,NGE), + CCM(J,NL), CCM(J,GE), + CCM(J,LE), CCM(J,NG), + CCM(J,NLE), CCM(J,G), +Mnemonic_JMP, // Jump +Mnemonic_LEA, // Load Effective Address +Mnemonic_LEAVE, // High Level Procedure Exit +Mnemonic_LOOP, // Loop according to ECX counter +Mnemonic_LOOPE, // Loop according to ECX counter +Mnemonic_LOOPNE, Mnemonic_LOOPNZ = Mnemonic_LOOPNE, // Loop according to ECX +Mnemonic_LAHF, // Load Flags into AH +Mnemonic_MOV, // Move +Mnemonic_MOVD, // Move Double word +Mnemonic_MOVQ, // Move Quadword +/*Mnemonic_MOVS, // Move Data from String to String*/ +// MOVS is a special case: see encoding table for more details, +Mnemonic_MOVS8, Mnemonic_MOVS16, Mnemonic_MOVS32, Mnemonic_MOVS64, +// +Mnemonic_MOVAPD, // Move Scalar Double-Precision Floating-Point Value +Mnemonic_MOVSD, // Move Scalar Double-Precision Floating-Point Value +Mnemonic_MOVSS, // Move Scalar Single-Precision Floating-Point Values +Mnemonic_MOVSX, // Move with Sign-Extension +Mnemonic_MOVZX, // Move with Zero-Extend +//Mnemonic_MUL, // Unsigned Multiply +Mnemonic_MULSD, // Multiply Scalar Double-Precision Floating-Point Values +Mnemonic_MULSS, // Multiply Scalar Single-Precision Floating-Point Values +Mnemonic_NEG, // Two's Complement Negation +Mnemonic_NOP, // No Operation +Mnemonic_NOT, // One's Complement Negation +Mnemonic_OR, // Logical Inclusive OR +Mnemonic_PREFETCH, // prefetch + +#ifdef _HAVE_MMX_ + Mnemonic_PADDQ, // Add Packed Quadword Integers + Mnemonic_PAND, // Logical AND + Mnemonic_POR, // Bitwise Logical OR + Mnemonic_PSUBQ, // Subtract Packed Quadword Integers +#endif + +Mnemonic_PXOR, // Logical Exclusive OR +Mnemonic_POP, // Pop a Value from the Stack +Mnemonic_POPFD, // Pop a Value of EFLAGS register from the Stack +Mnemonic_PUSH, // Push Word or Doubleword Onto the Stack +Mnemonic_PUSHFD, // Push EFLAGS Doubleword Onto the Stack +Mnemonic_RET, // Return from Procedure + +Mnemonic_SETcc, // Set Byte on Condition + CCM(SET,O), + CCM(SET,NO), + CCM(SET,B), CCM(SET,NAE), CCM(SET,C), + CCM(SET,NB), CCM(SET,AE), CCM(SET,NC), + CCM(SET,Z), CCM(SET,E), + CCM(SET,NZ), CCM(SET,NE), + CCM(SET,BE), CCM(SET,NA), + CCM(SET,NBE), CCM(SET,A), + CCM(SET,S), + CCM(SET,NS), + CCM(SET,P), CCM(SET,PE), + CCM(SET,NP), CCM(SET,PO), + CCM(SET,L), CCM(SET,NGE), + CCM(SET,NL), CCM(SET,GE), + CCM(SET,LE), CCM(SET,NG), + CCM(SET,NLE), CCM(SET,G), + +Mnemonic_SAL, Mnemonic_SHL=Mnemonic_SAL,// Shift left +Mnemonic_SAR, // Shift right +Mnemonic_ROR, // Rotate right +Mnemonic_RCR, // Rotate right through CARRY flag +Mnemonic_ROL, // Rotate left +Mnemonic_RCL, // Rotate left through CARRY flag +Mnemonic_SHR, // Unsigned shift right +Mnemonic_SHRD, // Double Precision Shift Right +Mnemonic_SHLD, // Double Precision Shift Left + +Mnemonic_SBB, // Integer Subtraction with Borrow +Mnemonic_SUB, // Subtract +Mnemonic_SUBSD, // Subtract Scalar Double-Precision Floating-Point Values +Mnemonic_SUBSS, // Subtract Scalar Single-Precision Floating-Point Values + +Mnemonic_TEST, // Logical Compare + +Mnemonic_UCOMISD, // Unordered Compare Scalar Double-Precision Floating-Point Values and Set EFLAGS +Mnemonic_UCOMISS, // Unordered Compare Scalar Single-Precision Floating-Point Values and Set EFLAGS + +Mnemonic_XOR, // Logical Exclusive OR +// +// packed things, +// +Mnemonic_XORPD, // Bitwise Logical XOR for Double-Precision Floating-Point Values +Mnemonic_XORPS, // Bitwise Logical XOR for Single-Precision Floating-Point Values + +Mnemonic_CVTDQ2PD, // Convert Packed Doubleword Integers to Packed Double-Precision Floating-Point Values +Mnemonic_CVTTPD2DQ, // Convert with Truncation Packed Double-Precision Floating-Point Values to Packed Doubleword Integers + +Mnemonic_CVTDQ2PS, // Convert Packed Doubleword Integers to Packed Single-Precision Floating-Point Values +Mnemonic_CVTTPS2DQ, // Convert with Truncation Packed Single-Precision Floating-Point Values to Packed Doubleword Integers +// +// String operations +// +Mnemonic_STD, // Set direction flag +Mnemonic_CLD, // Clear direction flag +Mnemonic_SCAS, // Scan string +Mnemonic_STOS, // Store string + +// +Mnemonic_WAIT, // Check pending pending unmasked floating-point exception +// +Mnemonic_Count +}; + +#undef CCM +#endif + +/** + * @brief Instruction prefixes, according to arch manual. + */ +typedef enum InstPrefix { + InstPrefix_Null = 0, + // Group 1 + InstPrefix_LOCK = 0xF0, + InstPrefix_REPNE = 0xF2, + InstPrefix_REPNZ = InstPrefix_REPNE, + InstPrefix_REP = 0xF3, InstPrefix_REPZ = InstPrefix_REP, + // Group 2 + InstPrefix_CS = 0x2E, + InstPrefix_SS = 0x36, + InstPrefix_DS = 0x3E, + InstPrefix_ES = 0x26, + InstPrefix_FS = 0x64, + InstPrefix_GS = 0x65, + // + InstPrefix_HintTaken = 0x3E, + InstPrefix_HintNotTaken = 0x2E, + // Group 3 + InstPrefix_OpndSize = 0x66, + // Group 4 + InstPrefix_AddrSize = 0x67 +} InstPrefix; + +inline unsigned getSizeBytes(OpndSize sz) +{ + if (sz==OpndSize_64) { return 8; } + if (sz==OpndSize_32) { return 4; } + if (sz==OpndSize_16) { return 2; } + if (sz==OpndSize_8) { return 1; } + assert(false); + return 0; +} + +inline bool isRegKind(OpndKind kind) +{ + return OpndKind_GPReg<= kind && kind<=OpndKind_MaxRegKind; +} + +/** + * @brief Returns RegName for a given name. + * + * Name is case-insensitive. + * @param regname - string name of a register + * @return RegName for the given name, or RegName_Null if name is invalid + */ +RegName getRegName(const char * regname); +/** + * Constructs RegName from the given OpndKind, size and index. + */ +inline RegName getRegName(OpndKind k, OpndSize s, int idx) +{ + return (RegName)REGNAME(k,s,idx); +} +/** + * Extracts a bit mask with a bit set at the position of the register's index. + */ +inline unsigned getRegMask(RegName reg) +{ + return 1<<(reg&0xff); +} +/** + * @brief Extracts OpndKind from the RegName. + */ +inline OpndKind getRegKind(RegName reg) +{ + return (OpndKind)(reg>>24); +} +/** + * @brief Extracts OpndSize from RegName. + */ +inline OpndSize getRegSize(RegName reg) +{ + return (OpndSize)((reg>>16)&0xFF); +} +/** + * Extracts an index from the given RegName. + */ +inline unsigned char getRegIndex(RegName reg) +{ + return (unsigned char)(reg&0xFF); +} +/** + * Returns a string name of the given RegName. The name returned is in upper-case. + * Returns NULL if invalid RegName specified. + */ +const char * getRegNameString(RegName reg); +/** + * Returns string name of a given OpndSize. + * Returns NULL if invalid OpndSize passed. + */ +const char * getOpndSizeString(OpndSize size); +/** + * Returns OpndSize passed by its string representation (case insensitive). + * Returns OpndSize_Null if invalid string specified. + * The 'sizeString' can not be NULL. + */ +OpndSize getOpndSize(const char * sizeString); +/** + * Returns string name of a given OpndKind. + * Returns NULL if the passed kind is invalid. + */ +const char * getOpndKindString(OpndKind kind); +/** + * Returns OpndKind found by its string representation (case insensitive). + * Returns OpndKind_Null if the name is invalid. + * The 'kindString' can not be NULL. + */ +OpndKind getOpndKind(const char * kindString); +/** + * + */ +const char * getConditionString(ConditionMnemonic cm); + +/** + * Constructs an RegName with the same index and kind, but with a different size from + * the given RegName (i.e. getRegAlias(EAX, OpndSize_16) => AX; getRegAlias(BL, OpndSize_32) => EBX). + * The constructed RegName is not checked in any way and thus may be invalid. + * Note, that the aliasing does not work for at least AH,BH,CH,DH, ESI, EDI, ESP and EBP regs. + */ +inline RegName getAliasReg(RegName reg, OpndSize sz) +{ + return (RegName)REGNAME(getRegKind(reg), sz, getRegIndex(reg)); +} + +/** + * brief Tests two RegName-s of the same kind for equality. + * + * @note Does work for 8 bit general purpose registers (AH, AL, BH, BL, etc). + */ +inline bool equals(RegName r0, RegName r1) +{ + return getRegKind(r0) == getRegKind(r1) && + getRegIndex(r0) == getRegIndex(r1); +} + +ENCODER_NAMESPACE_END + +#endif // ifndef _ENCODER_DEFS_H_ diff --git a/libpixelflinger/codeflinger/x86/libenc/enc_defs_ext.h b/libpixelflinger/codeflinger/x86/libenc/enc_defs_ext.h new file mode 100644 index 0000000..53f6d44 --- /dev/null +++ b/libpixelflinger/codeflinger/x86/libenc/enc_defs_ext.h @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2012 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 _ENCODER_DEFS_EXT_H_ +#define _ENCODER_DEFS_EXT_H_ + + +// Used to isolate experimental or being tuned encoder into a separate +// namespace so it can coexist with a stable one in the same bundle. +#ifdef ENCODER_ISOLATE + #define ENCODER_NAMESPACE_START namespace enc_ia32 { + #define ENCODER_NAMESPACE_END }; +#else + #define ENCODER_NAMESPACE_START + #define ENCODER_NAMESPACE_END +#endif + +ENCODER_NAMESPACE_START +typedef enum OpndSize { + /** + * A change must be balanced with at least the following places: + * Ia32IRConstants.h :: getByteSize() uses some presumptions about OpndSize_ values + * Ia32::Constraint-s use the OpndSize as a mask + * encoder.cpp & encoder_master_info.cpp uses OpndSize as an index for hashing + * - perhaps there are much more places + */ + OpndSize_Null = 0, + OpndSize_8 = 0x01, + OpndSize_16 = 0x02, + OpndSize_32 = 0x04, + OpndSize_64 = 0x08, +#if !defined(TESTING_ENCODER) + OpndSize_80 = 0x10, + OpndSize_128 = 0x20, +#endif + OpndSize_Max, + OpndSize_Any = 0x3F, + OpndSize_Default = OpndSize_Any +} OpndSize; + +/** + * Conditional mnemonics. + * The values match the 'real' (==processor's) values of the appropriate + * condition values used in the opcodes. + */ +typedef enum ConditionMnemonic { + + ConditionMnemonic_O=0, + ConditionMnemonic_NO=1, + ConditionMnemonic_B=2, ConditionMnemonic_NAE=ConditionMnemonic_B, ConditionMnemonic_C=ConditionMnemonic_B, + ConditionMnemonic_NB=3, ConditionMnemonic_AE=ConditionMnemonic_NB, ConditionMnemonic_NC=ConditionMnemonic_NB, + ConditionMnemonic_Z=4, ConditionMnemonic_E=ConditionMnemonic_Z, + ConditionMnemonic_NZ=5, ConditionMnemonic_NE=ConditionMnemonic_NZ, + ConditionMnemonic_BE=6, ConditionMnemonic_NA=ConditionMnemonic_BE, + ConditionMnemonic_NBE=7, ConditionMnemonic_A=ConditionMnemonic_NBE, + + ConditionMnemonic_S=8, + ConditionMnemonic_NS=9, + ConditionMnemonic_P=10, ConditionMnemonic_PE=ConditionMnemonic_P, + ConditionMnemonic_NP=11, ConditionMnemonic_PO=ConditionMnemonic_NP, + ConditionMnemonic_L=12, ConditionMnemonic_NGE=ConditionMnemonic_L, + ConditionMnemonic_NL=13, ConditionMnemonic_GE=ConditionMnemonic_NL, + ConditionMnemonic_LE=14, ConditionMnemonic_NG=ConditionMnemonic_LE, + ConditionMnemonic_NLE=15, ConditionMnemonic_G=ConditionMnemonic_NLE, + ConditionMnemonic_Count=16 +} ConditionMnemonic; + + +#define CCM(prefix,cond) Mnemonic_##prefix##cond=Mnemonic_##prefix##cc+ConditionMnemonic_##cond + +//========================================================================================================= +typedef enum Mnemonic { + +Mnemonic_NULL=0, Mnemonic_Null=Mnemonic_NULL, +Mnemonic_JMP, // Jump +Mnemonic_MOV, // Move +Mnemonic_Jcc, // Jump if Condition Is Met + CCM(J,O), + CCM(J,NO), + CCM(J,B), CCM(J,NAE), CCM(J,C), + CCM(J,NB), CCM(J,AE), CCM(J,NC), + CCM(J,Z), CCM(J,E), + CCM(J,NZ), CCM(J,NE), + CCM(J,BE), CCM(J,NA), + CCM(J,NBE), CCM(J,A), + CCM(J,S), + CCM(J,NS), + CCM(J,P), CCM(J,PE), + CCM(J,NP), CCM(J,PO), + CCM(J,L), CCM(J,NGE), + CCM(J,NL), CCM(J,GE), + CCM(J,LE), CCM(J,NG), + CCM(J,NLE), CCM(J,G), +Mnemonic_CALL, // Call Procedure + +Mnemonic_ADC, // Add with Carry +Mnemonic_ADD, // Add +Mnemonic_ADDSD, // Add Scalar Double-Precision Floating-Point Values +Mnemonic_ADDSS, // Add Scalar Single-Precision Floating-Point Values +Mnemonic_AND, // Logical AND + +Mnemonic_BSF, // Bit scan forward +Mnemonic_BSR, // Bit scan reverse + +Mnemonic_CMC, // Complement Carry Flag +Mnemonic_CWD, Mnemonic_CDQ=Mnemonic_CWD,// Convert Word to Doubleword/Convert Doubleword to Qua T dword +Mnemonic_CMOVcc, // Conditional Move + CCM(CMOV,O), + CCM(CMOV,NO), + CCM(CMOV,B), CCM(CMOV,NAE), CCM(CMOV,C), + CCM(CMOV,NB), CCM(CMOV,AE), CCM(CMOV,NC), + CCM(CMOV,Z), CCM(CMOV,E), + CCM(CMOV,NZ), CCM(CMOV,NE), + CCM(CMOV,BE), CCM(CMOV,NA), + CCM(CMOV,NBE), CCM(CMOV,A), + + CCM(CMOV,S), + CCM(CMOV,NS), + CCM(CMOV,P), CCM(CMOV,PE), + CCM(CMOV,NP), CCM(CMOV,PO), + CCM(CMOV,L), CCM(CMOV,NGE), + CCM(CMOV,NL), CCM(CMOV,GE), + CCM(CMOV,LE), CCM(CMOV,NG), + CCM(CMOV,NLE), CCM(CMOV,G), + +Mnemonic_CMP, // Compare Two Operands +Mnemonic_CMPXCHG, // Compare and exchange +Mnemonic_CMPXCHG8B, // Compare and Exchange 8 Bytes +Mnemonic_CMPSB, // Compare Two Bytes at DS:ESI and ES:EDI +Mnemonic_CMPSW, // Compare Two Words at DS:ESI and ES:EDI +Mnemonic_CMPSD, // Compare Two Doublewords at DS:ESI and ES:EDI +// +// double -> float +Mnemonic_CVTSD2SS, // Convert Scalar Double-Precision Floating-Point Value to Scalar Single-Precision Floating-Point Value +// double -> I_32 +Mnemonic_CVTSD2SI, // Convert Scalar Double-Precision Floating-Point Value to Doubleword Integer +// double [truncated] -> I_32 +Mnemonic_CVTTSD2SI, // Convert with Truncation Scalar Double-Precision Floating-Point Value to Signed Doubleword Integer +// +// float -> double +Mnemonic_CVTSS2SD, // Convert Scalar Single-Precision Floating-Point Value to Scalar Double-Precision Floating-Point Value +// float -> I_32 +Mnemonic_CVTSS2SI, // Convert Scalar Single-Precision Floating-Point Value to Doubleword Integer +// float [truncated] -> I_32 +Mnemonic_CVTTSS2SI, // Convert with Truncation Scalar Single-Precision Floating-Point Value to Doubleword Integer +// +// I_32 -> double +Mnemonic_CVTSI2SD, // Convert Doubleword Integer to Scalar Double-Precision Floating-Point Value +// I_32 -> float +Mnemonic_CVTSI2SS, // Convert Doubleword Integer to Scalar Single-Precision Floating-Point Value + +Mnemonic_COMISD, // Compare Scalar Ordered Double-Precision Floating-Point Values and Set EFLAGS +Mnemonic_COMISS, // Compare Scalar Ordered Single-Precision Floating-Point Values and Set EFLAGS +Mnemonic_DEC, // Decrement by 1 +Mnemonic_DIVSD, // Divide Scalar Double-Precision Floating-Point Values +Mnemonic_DIVSS, // Divide Scalar Single-Precision Floating-Point Values +Mnemonic_ENTER, // ENTER-Make Stack Frame for Procedure Parameters +Mnemonic_FLDCW, // Load FPU control word +Mnemonic_FADDP, +Mnemonic_FLDZ, +Mnemonic_FADD, +Mnemonic_FSUBP, +Mnemonic_FSUB, +Mnemonic_FISUB, +Mnemonic_FMUL, +Mnemonic_FMULP, +Mnemonic_FDIVP, +Mnemonic_FDIV, +Mnemonic_FUCOM, +Mnemonic_FUCOMI, +Mnemonic_FUCOMP, +Mnemonic_FUCOMIP, +Mnemonic_FUCOMPP, +Mnemonic_FRNDINT, +Mnemonic_FNSTCW, // Store FPU control word +Mnemonic_FSTSW, // Store FPU status word +Mnemonic_FNSTSW, // Store FPU status word +Mnemonic_FILD, // Load Integer +Mnemonic_FLD, // Load Floating Point Value +Mnemonic_FLDLG2, +Mnemonic_FLDLN2, +Mnemonic_FLD1, + +Mnemonic_FCLEX, // Clear Exceptions +Mnemonic_FCHS, // Change sign of ST0 +Mnemonic_FNCLEX, // Clear Exceptions +Mnemonic_FIST, // Store Integer +Mnemonic_FISTP, // Store Integer, pop FPU stack +Mnemonic_FISTTP, // Store Integer with Truncation +Mnemonic_FPREM, // Partial Remainder +Mnemonic_FPREM1, // Partial Remainder +Mnemonic_FST, // Store Floating Point Value +Mnemonic_FSTP, // Store Floating Point Value and pop the FP stack +Mnemonic_FSQRT, //Computes the square root of the source value in the stack and pop the FP stack +Mnemonic_FABS, //Computes the absolute value of the source value in the stack and pop the FP stack +Mnemonic_FSIN, //Computes the sine of the source value in the stack and pop the FP stack +Mnemonic_FCOS, //Computes the cosine of the source value in the stack and pop the FP stack +Mnemonic_FPTAN, //Computes the tangent of the source value in the stack and pop the FP stack +Mnemonic_FYL2X, +Mnemonic_FYL2XP1, +Mnemonic_F2XM1, +Mnemonic_FPATAN, +Mnemonic_FXCH, +Mnemonic_FSCALE, + +Mnemonic_XCHG, +Mnemonic_DIV, // Unsigned Divide +Mnemonic_IDIV, // Signed Divide +Mnemonic_MUL, // Unsigned Multiply +Mnemonic_IMUL, // Signed Multiply +Mnemonic_INC, // Increment by 1 +Mnemonic_INT3, // Call break point + +Mnemonic_LEA, // Load Effective Address +Mnemonic_LEAVE, // High Level Procedure Exit +Mnemonic_LOOP, // Loop according to ECX counter +Mnemonic_LOOPE, // Loop according to ECX counter +Mnemonic_LOOPNE, Mnemonic_LOOPNZ = Mnemonic_LOOPNE, // Loop according to ECX +Mnemonic_LAHF, // Load Flags into AH +Mnemonic_MOVD, // Move Double word +Mnemonic_MOVQ, // Move Quadword +Mnemonic_MOVS8, +Mnemonic_MOVS16, +Mnemonic_MOVS32, +Mnemonic_MOVS64, +Mnemonic_MOVAPD, // Move Scalar Double-Precision Floating-Point Value +Mnemonic_MOVSD, // Move Scalar Double-Precision Floating-Point Value +Mnemonic_MOVSS, // Move Scalar Single-Precision Floating-Point Values +Mnemonic_MOVSX, // Move with Sign-Extension +Mnemonic_MOVZX, // Move with Zero-Extend +Mnemonic_MULSD, // Multiply Scalar Double-Precision Floating-Point Values +Mnemonic_MULSS, // Multiply Scalar Single-Precision Floating-Point Values +Mnemonic_NEG, // Two's Complement Negation +Mnemonic_NOP, // No Operation +Mnemonic_NOT, // One's Complement Negation +Mnemonic_OR, // Logical Inclusive OR +Mnemonic_PREFETCH, // prefetch +Mnemonic_PADDQ, // Add Packed Quadword Integers +Mnemonic_PAND, // Logical AND +Mnemonic_POR, // Bitwise Logical OR +Mnemonic_PSUBQ, // Subtract Packed Quadword Integers +Mnemonic_PANDN, +Mnemonic_PSLLQ, +Mnemonic_PSRLQ, +Mnemonic_PXOR, // Logical Exclusive OR +Mnemonic_POP, // Pop a Value from the Stack +Mnemonic_POPFD, // Pop a Value of EFLAGS register from the Stack +Mnemonic_PUSH, // Push Word or Doubleword Onto the Stack +Mnemonic_PUSHFD, // Push EFLAGS Doubleword Onto the Stack +Mnemonic_RET, // Return from Procedure + +Mnemonic_SETcc, // Set Byte on Condition + CCM(SET,O), + CCM(SET,NO), + CCM(SET,B), CCM(SET,NAE), CCM(SET,C), + CCM(SET,NB), CCM(SET,AE), CCM(SET,NC), + CCM(SET,Z), CCM(SET,E), + CCM(SET,NZ), CCM(SET,NE), + CCM(SET,BE), CCM(SET,NA), + CCM(SET,NBE), CCM(SET,A), + CCM(SET,S), + CCM(SET,NS), + CCM(SET,P), CCM(SET,PE), + CCM(SET,NP), CCM(SET,PO), + CCM(SET,L), CCM(SET,NGE), + CCM(SET,NL), CCM(SET,GE), + CCM(SET,LE), CCM(SET,NG), + CCM(SET,NLE), CCM(SET,G), + +Mnemonic_SAL, Mnemonic_SHL=Mnemonic_SAL,// Shift left +Mnemonic_SAR, // Unsigned shift right +Mnemonic_ROR, // Rotate right +Mnemonic_RCR, // Rotate right through CARRY flag +Mnemonic_ROL, // Rotate left +Mnemonic_RCL, // Rotate left through CARRY flag +Mnemonic_SHR, // Signed shift right +Mnemonic_SHRD, // Double Precision Shift Right +Mnemonic_SHLD, // Double Precision Shift Left + +Mnemonic_SBB, // Integer Subtraction with Borrow +Mnemonic_SUB, // Subtract +Mnemonic_SUBSD, // Subtract Scalar Double-Precision Floating-Point Values +Mnemonic_SUBSS, // Subtract Scalar Single-Precision Floating-Point Values + +Mnemonic_TEST, // Logical Compare + +Mnemonic_UCOMISD, // Unordered Compare Scalar Double-Precision Floating-Point Values and Set EFLAGS +Mnemonic_UCOMISS, // Unordered Compare Scalar Single-Precision Floating-Point Values and Set EFLAGS + +Mnemonic_XOR, // Logical Exclusive OR +// +// packed things, +// +Mnemonic_XORPD, // Bitwise Logical XOR for Double-Precision Floating-Point Values +Mnemonic_XORPS, // Bitwise Logical XOR for Single-Precision Floating-Point Values + +Mnemonic_CVTDQ2PD, // Convert Packed Doubleword Integers to Packed Double-Precision Floating-Point Values +Mnemonic_CVTTPD2DQ, // Convert with Truncation Packed Double-Precision Floating-Point Values to Packed Doubleword Integers + +Mnemonic_CVTDQ2PS, // Convert Packed Doubleword Integers to Packed Single-Precision Floating-Point Values +Mnemonic_CVTTPS2DQ, // Convert with Truncation Packed Single-Precision Floating-Point Values to Packed Doubleword Integers +// +// String operations +// +Mnemonic_STD, // Set direction flag +Mnemonic_CLD, // Clear direction flag +Mnemonic_SCAS, // Scan string +Mnemonic_STOS, // Store string + +// +Mnemonic_WAIT, // Check pending pending unmasked floating-point exception +Mnemonic_PADDB, //!< Add packed byte integers +Mnemonic_PADDW, //!< Add packed word integers +Mnemonic_PADDD, //!< Add packed doubleword integers +Mnemonic_PSUBB, //!< Subtract packed byte integers +Mnemonic_PSUBW, //!< Subtract packed word integers +Mnemonic_PSUBD, //!< Subtract packed doubleword integers +Mnemonic_PMULLW, //!< Multiply packed word integers +Mnemonic_PMULLD, //!< Multiply packed doubleword integers +Mnemonic_PSLLW, //!< Shift words left and shift in 0s +Mnemonic_PSLLD, //!< Shift doublewords left and shift in 0s +Mnemonic_PSRAW, //!< Shift words right and shift in sign bits +Mnemonic_PSRAD, //!< Shift doublewords right and shift in sign bits +Mnemonic_PSRLW, //!< Shift words right and shift in 0s +Mnemonic_PSRLD, //!< Shift doublewords right and shift in 0s +Mnemonic_PMOVSXBW, //!< Sign extend 8 packed signed 8-bit integers in the low 8 bytes to 8 packed signed 16-bit integers +Mnemonic_PSHUFB, //!< Shuffle bytes +Mnemonic_PSHUFD, //!< Shuffle doublewords +Mnemonic_PSHUFLW, //!< Shuffle packed low words +Mnemonic_PSHUFHW, //!< Shuffle packed high words +Mnemonic_PHADDSW, //!< Add 16-bit signed integers horizontally, then pack saturated integers +Mnemonic_PHADDW, //!< Add 16-bit signed integers horizontally, then pack +Mnemonic_PHADDD, //!< Add 32-bit signed integers horizontally, then pack +Mnemonic_PHSUBSW, //!< Subtract 16-bit signed integers horizontally, then pack saturated integers +Mnemonic_PHSUBW, //!< Subtract 16-bit signed integers horizontally, then pack +Mnemonic_PHSUBD, //!< Subtract 32-bit signed integers horizontally, then pack +Mnemonic_PEXTRB, //!< Extract a byte integer value from xmm +Mnemonic_PEXTRW, //!< Extract a word integer value from xmm +Mnemonic_PEXTRD, //!< Extract a doubleword integer value from xmm +Mnemonic_MOVDQA, //!< Move aligned double quadword +Mnemonic_SHUFPS, //!< Shuffle single words +Mnemonic_MOVAPS, //!< Move aligned single word + +// +Mnemonic_Count +} Mnemonic; + +#undef CCM + +ENCODER_NAMESPACE_END + +#endif // ifndef _ENCODER_DEFS_EXT_H_ diff --git a/libpixelflinger/codeflinger/x86/libenc/enc_prvt.h b/libpixelflinger/codeflinger/x86/libenc/enc_prvt.h new file mode 100644 index 0000000..343b161 --- /dev/null +++ b/libpixelflinger/codeflinger/x86/libenc/enc_prvt.h @@ -0,0 +1,382 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexander V. Astapchuk + */ +#ifndef __ENC_PRVT_H_INCLUDED__ +#define __ENC_PRVT_H_INCLUDED__ + +#include "enc_base.h" + +ENCODER_NAMESPACE_START +/* + * @file + * @brief Contains some definitions/constants and other stuff used by the + * Encoder internally. + */ + +enum OpcodeByteKind { + //OpcodeByteKind_Opcode = 0x0000, + OpcodeByteKind_ZeroOpcodeByte = 0x0100, + // + // The names _SlashR, _SlahsNum, _ib, _iw, etc + // represent the appropriate abbreviations used + // in the mnemonic descriptions in the Intel's arch manual. + // + OpcodeByteKind_SlashR = 0x0200, + OpcodeByteKind_SlashNum = 0x0300, + OpcodeByteKind_ib = 0x0400, + OpcodeByteKind_iw = 0x0500, + OpcodeByteKind_id = 0x0600, +#ifdef _EM64T_ + OpcodeByteKind_io = 0x0700, +#endif + OpcodeByteKind_cb = 0x0800, + OpcodeByteKind_cw = 0x0900, + OpcodeByteKind_cd = 0x0A00, + //OpcodeByteKind_cp = 0x0B00, + //OpcodeByteKind_co = 0x0C00, + //OpcodeByteKind_ct = 0x0D00, + + OpcodeByteKind_rb = 0x0E00, + OpcodeByteKind_rw = 0x0F00, + OpcodeByteKind_rd = 0x1000, +#ifdef _EM64T_ + OpcodeByteKind_ro = 0x1100, + //OpcodeByteKind_REX = 0x1200, + OpcodeByteKind_REX_W = 0x1300, +#endif + OpcodeByteKind_plus_i = 0x1400, + /** + * a special marker, means 'no opcode on the given position' + * used in opcodes array, to specify the empty slot, say + * to fill an em64t-specific opcode on ia32. + * last 'e' made lowercase to avoid a mess with 'F' in + * OpcodeByteKind_LAST . + */ + OpcodeByteKind_EMPTY = 0xFFFE, + /** + * a special marker, means 'no more opcodes in the array' + * used in in opcodes array to show that there are no more + * opcodes in the array for a given mnemonic. + */ + OpcodeByteKind_LAST = 0xFFFF, + /** + * a mask to extract the OpcodeByteKind + */ + OpcodeByteKind_KindMask = 0xFF00, + /** + * a mask to extract the opcode byte when presented + */ + OpcodeByteKind_OpcodeMask = 0x00FF +}; + +#ifdef USE_ENCODER_DEFINES + +#define N {0, 0, 0, 0 } +#define U {1, 0, 1, OpndRole_Use } +#define D {1, 1, 0, OpndRole_Def } +#define DU {1, 1, 1, OpndRole_Def|OpndRole_Use } + +#define U_U {2, 0, 2, OpndRole_Use<<2 | OpndRole_Use } +#define D_U {2, 1, 1, OpndRole_Def<<2 | OpndRole_Use } +#define D_DU {2, 2, 1, OpndRole_Def<<2 | (OpndRole_Def|OpndRole_Use) } +#define DU_U {2, 1, 2, ((OpndRole_Def|OpndRole_Use)<<2 | OpndRole_Use) } +#define DU_DU {2, 2, 2, ((OpndRole_Def|OpndRole_Use)<<2 | (OpndRole_Def|OpndRole_Use)) } + +#define DU_DU_DU {3, 3, 3, ((OpndRole_Def|OpndRole_Use)<<4) | ((OpndRole_Def|OpndRole_Use)<<2) | (OpndRole_Def|OpndRole_Use) } +#define DU_DU_U {3, 2, 3, (((OpndRole_Def|OpndRole_Use)<<4) | ((OpndRole_Def|OpndRole_Use)<<2) | OpndRole_Use) } +#define D_DU_U {3, 2, 2, (((OpndRole_Def)<<4) | ((OpndRole_Def|OpndRole_Use)<<2) | OpndRole_Use) } +#define D_U_U {3, 1, 2, (((OpndRole_Def)<<4) | ((OpndRole_Use)<<2) | OpndRole_Use) } + +// Special encoding of 0x00 opcode byte. Note: it's all O-s, not zeros. +#define OxOO OpcodeByteKind_ZeroOpcodeByte + +#define Size16 InstPrefix_OpndSize + +#define _r OpcodeByteKind_SlashR + +#define _0 OpcodeByteKind_SlashNum|0 +#define _1 OpcodeByteKind_SlashNum|1 +#define _2 OpcodeByteKind_SlashNum|2 +#define _3 OpcodeByteKind_SlashNum|3 +#define _4 OpcodeByteKind_SlashNum|4 +#define _5 OpcodeByteKind_SlashNum|5 +#define _6 OpcodeByteKind_SlashNum|6 +#define _7 OpcodeByteKind_SlashNum|7 + +// '+i' for floating-point instructions +#define _i OpcodeByteKind_plus_i + + +#define ib OpcodeByteKind_ib +#define iw OpcodeByteKind_iw +#define id OpcodeByteKind_id + +#define cb OpcodeByteKind_cb +#define cw OpcodeByteKind_cw +#define cd OpcodeByteKind_cd + +#define rb OpcodeByteKind_rb +#define rw OpcodeByteKind_rw +#define rd OpcodeByteKind_rd + +#define AL {OpndKind_GPReg, OpndSize_8, OpndExt_Any, RegName_AL} +#define AH {OpndKind_GPReg, OpndSize_8, OpndExt_Any, RegName_AH} +#define AX {OpndKind_GPReg, OpndSize_16, OpndExt_Any, RegName_AX} +#define EAX {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_EAX} +#ifdef _EM64T_ + #define RAX {OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RAX } +#endif + +#define CL {OpndKind_GPReg, OpndSize_8, OpndExt_Any, RegName_CL} +#define ECX {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_ECX} +#ifdef _EM64T_ + #define RCX {OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RCX} +#endif + +#define DX {OpndKind_GPReg, OpndSize_16, OpndExt_Any, RegName_DX} +#define EDX {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_EDX} +#ifdef _EM64T_ + #define RDX { OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RDX } +#endif + +#define ESI {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_ESI} +#ifdef _EM64T_ + #define RSI { OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RSI } +#endif + +#define EDI {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_EDI} +#ifdef _EM64T_ + #define RDI { OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_RDI } +#endif + +#define r8 {OpndKind_GPReg, OpndSize_8, OpndExt_Any, RegName_Null} +#define r16 {OpndKind_GPReg, OpndSize_16, OpndExt_Any, RegName_Null} +#define r32 {OpndKind_GPReg, OpndSize_32, OpndExt_Any, RegName_Null} +#ifdef _EM64T_ + #define r64 { OpndKind_GPReg, OpndSize_64, OpndExt_Any, RegName_Null } +#endif + +#define r_m8 {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_8, OpndExt_Any, RegName_Null} +#define r_m16 {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_16, OpndExt_Any, RegName_Null} +#define r_m32 {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_32, OpndExt_Any, RegName_Null} + +#define r_m8s {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_8, OpndExt_Signed, RegName_Null} +#define r_m16s {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_16, OpndExt_Signed, RegName_Null} +#define r_m32s {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_32, OpndExt_Signed, RegName_Null} + +#define r_m8u {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_8, OpndExt_Zero, RegName_Null} +#define r_m16u {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_16, OpndExt_Zero, RegName_Null} +#define r_m32u {(OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_32, OpndExt_Zero, RegName_Null} + +//'m' was only used in LEA mnemonic, but is replaced with +// set of exact sizes. See more comments for LEA instruction in TheTable. +//#define m {OpndKind_Mem, OpndSize_Null, RegName_Null} +#define m8 {OpndKind_Mem, OpndSize_8, OpndExt_Any, RegName_Null} +#define m16 {OpndKind_Mem, OpndSize_16, OpndExt_Any, RegName_Null} +#define m32 {OpndKind_Mem, OpndSize_32, OpndExt_Any, RegName_Null} +#define m64 {OpndKind_Mem, OpndSize_64, OpndExt_Any, RegName_Null} +#ifdef _EM64T_ + #define r_m64 { (OpndKind)(OpndKind_GPReg|OpndKind_Mem), OpndSize_64, OpndExt_Any, RegName_Null } +#endif + +#define imm8 {OpndKind_Imm, OpndSize_8, OpndExt_Any, RegName_Null} +#define imm16 {OpndKind_Imm, OpndSize_16, OpndExt_Any, RegName_Null} +#define imm32 {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null} + +#define imm8s {OpndKind_Imm, OpndSize_8, OpndExt_Signed, RegName_Null} +#define imm16s {OpndKind_Imm, OpndSize_16, OpndExt_Signed, RegName_Null} +#define imm32s {OpndKind_Imm, OpndSize_32, OpndExt_Signed, RegName_Null} + +#define imm8u {OpndKind_Imm, OpndSize_8, OpndExt_Zero, RegName_Null} +#define imm16u {OpndKind_Imm, OpndSize_16, OpndExt_Zero, RegName_Null} +#define imm32u {OpndKind_Imm, OpndSize_32, OpndExt_Zero, RegName_Null} + +#ifdef _EM64T_ + #define imm64 {OpndKind_Imm, OpndSize_64, OpndExt_Any, RegName_Null } +#endif + +//FIXME: moff-s are in fact memory refs, but presented as immediate. +// Need to specify this in OpndDesc. +#define moff8 {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null} +#define moff16 {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null} +#define moff32 {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null} +#ifdef _EM64T_ + #define moff64 {OpndKind_Imm, OpndSize_64, OpndExt_Any, RegName_Null} +#endif + + +#define rel8 {OpndKind_Imm, OpndSize_8, OpndExt_Any, RegName_Null} +#define rel16 {OpndKind_Imm, OpndSize_16, OpndExt_Any, RegName_Null} +#define rel32 {OpndKind_Imm, OpndSize_32, OpndExt_Any, RegName_Null} + +#define mm64 {OpndKind_MMXReg, OpndSize_64, OpndExt_Any, RegName_Null} +#define mm_m64 {(OpndKind)(OpndKind_MMXReg|OpndKind_Mem), OpndSize_64, OpndExt_Any, RegName_Null} + +#define xmm64 {OpndKind_XMMReg, OpndSize_64, OpndExt_Any, RegName_Null} +#define xmm_m64 {(OpndKind)(OpndKind_XMMReg|OpndKind_Mem), OpndSize_64, OpndExt_Any, RegName_Null} + +#define xmm32 {OpndKind_XMMReg, OpndSize_32, OpndExt_Any, RegName_Null} +#define xmm_m32 {(OpndKind)(OpndKind_XMMReg|OpndKind_Mem), OpndSize_32, OpndExt_Any, RegName_Null} + +#define FP0S {OpndKind_FPReg, OpndSize_32, OpndExt_Any, RegName_FP0S} +#define FP0D {OpndKind_FPReg, OpndSize_64, OpndExt_Any, RegName_FP0D} +#define FP1S {OpndKind_FPReg, OpndSize_32, OpndExt_Any, RegName_FP1S} +#define FP1D {OpndKind_FPReg, OpndSize_64, OpndExt_Any, RegName_FP1D} +#define fp32 {OpndKind_FPReg, OpndSize_32, OpndExt_Any, RegName_Null} +#define fp64 {OpndKind_FPReg, OpndSize_64, OpndExt_Any, RegName_Null} + +#ifdef _EM64T_ + #define io OpcodeByteKind_io + #define REX_W OpcodeByteKind_REX_W + +#endif + +#endif // USE_ENCODER_DEFINES + +/** + * @brief Represents the REX part of instruction. + */ +struct Rex { + unsigned char b : 1; + unsigned char x : 1; + unsigned char r : 1; + unsigned char w : 1; + unsigned char dummy : 4; // must be '0100'b + unsigned int :24; +}; + +/** + * @brief Describes SIB (scale,index,base) byte. + */ +struct SIB { + unsigned char base:3; + unsigned char index:3; + unsigned char scale:2; + unsigned int padding:24; +}; +/** + * @brief Describes ModRM byte. + */ +struct ModRM +{ + unsigned char rm:3; + unsigned char reg:3; + unsigned char mod:2; + unsigned int padding:24; +}; + + + +/** +* exactly the same as EncoderBase::OpcodeDesc, but also holds info about +* platform on which the opcode is applicable. +*/ +struct OpcodeInfo { + enum platform { + /// an opcode is valid on all platforms + all, + // opcode is valid on IA-32 only + em64t, + // opcode is valid on Intel64 only + ia32, + // opcode is added for the sake of disassembling, should not be used in encoding + decoder, + // only appears in master table, replaced with 'decoder' in hashed version + decoder32, + // only appears in master table, replaced with 'decoder' in hashed version + decoder64, + }; + platform platf; + unsigned opcode[4+1+1]; + EncoderBase::OpndDesc opnds[EncoderBase::MAX_NUM_OPCODE_OPERANDS]; + EncoderBase::OpndRolesDesc roles; +}; + +/** + * @defgroup MF_ Mnemonic flags +*/ + + /** + * Operation has no special properties. + */ +#define MF_NONE (0x00000000) + /** + * Operation affects flags + */ +#define MF_AFFECTS_FLAGS (0x00000001) + /** + * Operation uses flags - conditional operations, ADC/SBB/ETC + */ +#define MF_USES_FLAGS (0x00000002) + /** + * Operation is conditional - MOVcc/SETcc/Jcc/ETC + */ +#define MF_CONDITIONAL (0x00000004) +/** + * Operation is symmetric - its args can be swapped (ADD/MUL/etc). + */ +#define MF_SYMMETRIC (0x00000008) +/** + * Operation is XOR-like - XOR, SUB - operations of 'arg,arg' is pure def, + * without use. + */ +#define MF_SAME_ARG_NO_USE (0x00000010) + +///@} // ~MNF + +/** + * @see same structure as EncoderBase::MnemonicDesc, but carries + * MnemonicInfo::OpcodeInfo[] instead of OpcodeDesc[]. + * Only used during prebuilding the encoding tables, thus it's hidden under + * the appropriate define. + */ +struct MnemonicInfo { + /** + * The mnemonic itself + */ + Mnemonic mn; + /** + * Various characteristics of mnemonic. + * @see MF_ + */ + unsigned flags; + /** + * Number of args/des/uses/roles for the operation. For the operations + * which may use different number of operands (i.e. IMUL/SHL) use the + * most common value, or leave '0' if you are sure this info is not + * required. + */ + EncoderBase::OpndRolesDesc roles; + /** + * Print name of the mnemonic + */ + const char * name; + /** + * Array of opcodes. + * The terminating opcode description always have OpcodeByteKind_LAST + * at the opcodes[i].opcode[0]. + * The size of '25' has nothing behind it, just counted the max + * number of opcodes currently used (MOV instruction). + */ + OpcodeInfo opcodes[25]; +}; + +ENCODER_NAMESPACE_END + +#endif // ~__ENC_PRVT_H_INCLUDED__ diff --git a/libpixelflinger/codeflinger/x86/libenc/enc_tabl.cpp b/libpixelflinger/codeflinger/x86/libenc/enc_tabl.cpp new file mode 100644 index 0000000..b60d6b7 --- /dev/null +++ b/libpixelflinger/codeflinger/x86/libenc/enc_tabl.cpp @@ -0,0 +1,2164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexander V. Astapchuk + */ + + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> //qsort +#include <string.h> +#include <memory.h> +#include <errno.h> +#include <stdlib.h> + + +// need to use EM64T-specifics - new registers, defines from enc_prvt, etc... +#if !defined(_EM64T_) + #define UNDEF_EM64T + #define _EM64T_ +#endif + +#define USE_ENCODER_DEFINES +#include "enc_prvt.h" +#include "enc_defs.h" + +#ifdef UNDEF_EM64T + #undef _EM64T_ +#endif + +//Android x86 +#if 0 //!defined(_HAVE_MMX_) + #define Mnemonic_PADDQ Mnemonic_Null + #define Mnemonic_PAND Mnemonic_Null + #define Mnemonic_POR Mnemonic_Null + #define Mnemonic_PSUBQ Mnemonic_Null +#endif + +ENCODER_NAMESPACE_START + + +EncoderBase::MnemonicDesc EncoderBase::mnemonics[Mnemonic_Count]; +EncoderBase::OpcodeDesc EncoderBase::opcodes[Mnemonic_Count][MAX_OPCODES]; +unsigned char EncoderBase::opcodesHashMap[Mnemonic_Count][HASH_MAX]; + + +/** + * @file + * @brief 'Master' copy of encoding data. + */ + +/* +This file contains a 'master copy' of encoding table - this is the info used +by both generator of native instructions (EncoderBase class) and by +disassembling routines. The first one uses an info how to encode the +instruction, and the second does an opposite - several separate tables are +built at runtime from this main table. + +============================================================================= + +The table was designed for easy support and maintenance. Thus, it was made as +much close as possible to the Intel's IA32 Architecture Manual descriptions. +The info is based on the latest (at the moment of writing) revision which is +June 2005, order number 253666-016. + +Normally, almost all of opcodes in the 'master' table represented exactly as +they are shown in the Intel's Architecture manual (well, with slashes +replaced with underscore). There are several exclusions especially marked. + +Normally, to add an opcode/instruction, one only need to copy the whole +string from the manual, and simply replace '/' with '_'. + +I.e., TheManual reads for DEC: + (1) FE /1 DEC r/m8 Valid Valid Decrement r/m8 by 1. + (2) REX + FE /1 DEC r/m8* Valid N.E. Decrement r/m8 by 1. + (3) REX.W + FF /1 DEC r/m64 Valid N.E. Decrement r/m64 by 1. + +1. Note, that there is no need to explicitly specify REX-based opcodes for + instruction to handle additional registers on EM64T: + + (1) FE /1 DEC r/m8 Valid Valid Decrement r/m8 by 1. + (3) REX.W + FF /1 DEC r/m64 Valid N.E. Decrement r/m64 by 1. + +2. Copy the string, strip off the text comments, replace '/'=>'_'. Note, that + the second line is for EM64T only + + (1) FE /1 DEC r/m8 + (3) REX.W + FF /1 DEC r/m64 + +3. Fill out the mnemonic, opcode parameters parts + + BEGIN_MNEMONIC(DEC, MF_AFFECTS_FLAGS, DU) + BEGIN_OPCODES() + {OpcodeInfo::all, {0xFE, _1}, {r_m8}, DU }, + {OpcodeInfo::em64t, {REX_W, 0xFF, _1}, {r_m64}, DU }, + + DU here - one argument, it's used and defined + +4. That's it, that simple ! + +The operand roles (DU here) are used by Jitrino's optimizing engine to +perform data flow analysis. It also used to store/obtain number of operands. + +Special cases are (see the table for details): +LEA +Some FPU operations (i.e. FSTP) +packed things (XORPD, XORPS, CVTDQ2PD, CVTTPD2DQ) + +Also, the Jitrino's needs require to specify all operands - including +implicit ones (see IMUL). + +The master table iself does not need to be ordered - it's get sorted before +processing. It's recommended (though it's not a law) to group similar +instructions together - i.e. FPU instructions, MMX, etc. + +============================================================================= + +The encoding engine builds several tables basing on the 'master' one (here +'mnemonic' is a kind of synonim for 'instruction'): + +- list of mnemonics which holds general info about instructions + (EncoderBase::mnemonics) +- an array of opcodes descriptions (EncodeBase::opcodes) +- a mapping between a hash value and an opcode description record for a given + mnemonic (EncoderBase::opcodesHashMap) + +The EncoderBase::mnemonics holds general info about instructions. +The EncoderBase::opcodesHashMap is used for fast opcode selection basing on +a hash value. +The EncodeBase::opcodes is used for the encoding itself. + +============================================================================= +The hash value is calculated and used as follows: + +JIT-ted code uses the following operand sizes: 8-, 16-, 32- and 64-bits and +size for an operand can be encoded in just 2 bits. + +The following operand locations are available: one of registers - GP, FP, +MMX, XMM (not taking segment registers), a memory and an immediate, which +gives us 6 variants and can be enumerated in 3 bits. + +As a grand total, the the whole operand's info needed for opcode selection +can be packed in 5 bits. Taking into account the IMUL mnemonic with its 3 +operands (including implicit ones), we're getting 15 bits per instruction and +the complete table is about 32768 items per single instruction. + +Seems too many, but luckily, the 15 bit limit will never be reached: the +worst case is IMUL with its 3 operands: +(IMUL r64, r/m64, imm32)/(IMUL r32, r/m32, imm32). +So, assigning lowest value to GP register, the max value of hash can be +reduced. + +The hash values to use are: +sizes: + 8 -> 11 + 16 -> 10 + 32 -> 01 + 64 -> 00 +locations: + gp reg -> 000 + memory -> 001 + fp reg -> 010 + mmx reg -> 011 + xmm reg -> 100 + immediate -> 101 +and the grand total for the worst case would be +[ GP 32] [GP 32] [Imm 32] +[000-01] [000-01] [101 01] = 1077 + +However, the implicit operands adds additional value, and the worstest case +is 'SHLD r_m32, r32, CL=r8'. This gives us the maximum number of: + +[mem 32] [GP 32] [GP 8b] +[001-01] [000-01] [000-11] = 5155. + +The max number is pretty big and the hash functions is quite rare, thus it +is not resonable to use a direct addressing i.e. +OpcodeDesc[mnemonic][hash_code] - there would be a huge waste of space. + +Instead, we use a kind of mapping: the opcodes info is stored in packed +(here: non rare) array. The max number of opcodes will not exceed 255 for +each instruction. And we have an index array in which we store a mapping +between a hash code value and opcode position for each given instruction. + +Sounds a bit sophisticated, but in real is simple, the opcode gets selected +in 2 simple steps: + +1. Select [hash,mnemonic] => 'n'. + +The array is pretty rare - many cells contain 0xFF which +means 'invalid hash - no opcode with given characteristics' + +char EnbcoderBase::opcodesHashMap[Mnemonic_Count][HASH_MAX] = + ++----+----+----+----+----+----+ +| 00 | 05 | FF | FF | 03 | 12 | ... +|---------+-------------------+ +| 12 | FF | FF | n | 04 | 25 | ... <- Mnemonic +|-----------------------------+ +| FF | 11 | FF | 10 | 13 | .. | ... ++-----------------------------+ + ... ^ + | + hash + +2. Select [n,mnemonic] => 'opcode_desc11' + +OpcodeDesc EncoderBase::opcodes[Mnemonic_Count][MAX_OPCODES] = + ++---------------+---------------+---------------+---------------+ +| opcode_desc00 | opcode_desc01 | opcode_desc02 | last_opcode | ... ++---------------+---------------+---------------+---------------+ +| opcode_desc10 | opcode_desc11 | last_opcode | xxx | <- Mnemonic ++---------------+---------------+---------------+---------------+ +| opcode_desc20 | opcode_desc21 | opcode_desc22 | opcode_desc23 | ... ++---------------+---------------+---------------+---------------+ + ... + ^ + | + n + +Now, use 'opcode_desc11'. + +============================================================================= +The array of opcodes descriptions (EncodeBase::opcodes) is specially prepared +to maximize performance - the EncoderBase::encode() is quite hot on client +applications for the Jitrino/Jitrino.JET. +The preparation is that opcode descriptions from the 'master' encoding table +are preprocessed and a special set of OpcodeDesc prepared: +First, the 'raw' opcode bytes are extracted. Here, 'raw' means the bytes that +do not depened on any operands values, do not require any analysis and can be +simply copied into the output buffer during encoding. Also, number of these +'raw' bytes is counted. The fields are OpcodeDesc::opcode and +OpcodeDesc::opcode_len. + +Then the fisrt non-implicit operand found and its index is stored in +OpcodeDesc::first_opnd. + +The bytes that require processing and analysis ('/r', '+i', etc) are +extracted and stored in OpcodeDesc::aux0 and OpcodeDesc::aux1 fields. + +Here, a special trick is performed: + Some opcodes have register/memory operand, but this is not reflected in + opcode column - for example, (MOVQ xmm64, xmm_m64). In this case, a fake + '_r' added to OpcodeDesc::aux field. + Some other opcodes have immediate operands, but this is again not + reflected in opcode column - for example, CALL cd or PUSH imm32. + In this case, a fake '/cd' or fake '/id' added to appropriate + OpcodeDesc::aux field. + +The OpcodeDesc::last is non-zero for the final OpcodeDesc record (which does +not have valid data itself). +*/ + +// TODO: To extend flexibility, replace bool fields in MnemonicDesc & +// MnemonicInfo with a set of flags packed into integer field. + +unsigned short EncoderBase::getHash(const OpcodeInfo* odesc) +{ + /* + NOTE: any changes in the hash computation must be stricty balanced with + EncoderBase::Operand::hash_it and EncoderBase::Operands() + */ + unsigned short hash = 0; + // The hash computation, uses fast way - table selection instead of if-s. + if (odesc->roles.count > 0) { + OpndKind kind = odesc->opnds[0].kind; + OpndSize size = odesc->opnds[0].size; + assert(kind<COUNTOF(kind_hash)); + assert(size<COUNTOF(size_hash)); + hash = get_kind_hash(kind) | get_size_hash(size); + } + + if (odesc->roles.count > 1) { + OpndKind kind = odesc->opnds[1].kind; + OpndSize size = odesc->opnds[1].size; + assert(kind<COUNTOF(kind_hash)); + assert(size<COUNTOF(size_hash)); + hash = (hash<<HASH_BITS_PER_OPERAND) | + (get_kind_hash(kind) | get_size_hash(size)); + } + + if (odesc->roles.count > 2) { + OpndKind kind = odesc->opnds[2].kind; + OpndSize size = odesc->opnds[2].size; + assert(kind<COUNTOF(kind_hash)); + assert(size<COUNTOF(size_hash)); + hash = (hash<<HASH_BITS_PER_OPERAND) | + (get_kind_hash(kind) | get_size_hash(size)); + } + assert(hash <= HASH_MAX); + return hash; +} + + +#define BEGIN_MNEMONIC(mn, flags, roles) \ + { Mnemonic_##mn, flags, roles, #mn, +#define END_MNEMONIC() }, +#define BEGIN_OPCODES() { +#define END_OPCODES() { OpcodeInfo::all, {OpcodeByteKind_LAST}, {}, {0, 0, 0, 0}}} + + +static MnemonicInfo masterEncodingTable[] = { +// +// Null +// +BEGIN_MNEMONIC(Null, MF_NONE, N) +BEGIN_OPCODES() +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(LAHF, MF_USES_FLAGS, D) +BEGIN_OPCODES() +// TheManual says it's not always supported in em64t mode, thus excluding it + {OpcodeInfo::ia32, {0x9F}, {EAX}, D }, +END_OPCODES() +END_MNEMONIC() +// +// ALU mnemonics - add, adc, or, xor, and, cmp, sub, sbb +// as they differ only in the opcode extention (/digit) number and +// in which number the opcode start from, the opcode definitions +// for those instructions are packed together +// +// The 'opcode_starts_from' and 'opcode_ext' in DEFINE_ALU_OPCODES() +// are enough to define OpcodeInfo::all opcodes and the 'first_opcode' +// parameter is only due to ADD instruction, which requires an zero opcode +// byte which, in turn, is coded especially in the current coding scheme. +// + +#define DEFINE_ALU_OPCODES( opc_ext, opcode_starts_from, first_opcode, def_use ) \ +\ + {OpcodeInfo::decoder, {opcode_starts_from + 4, ib}, {AL, imm8}, DU_U },\ + {OpcodeInfo::decoder, {Size16, opcode_starts_from + 5, iw}, {AX, imm16}, DU_U },\ + {OpcodeInfo::decoder, {opcode_starts_from + 5, id}, {EAX, imm32}, DU_U },\ + {OpcodeInfo::decoder64, {REX_W, opcode_starts_from+5, id}, {RAX, imm32s},DU_U },\ +\ + {OpcodeInfo::all, {0x80, opc_ext, ib}, {r_m8, imm8}, def_use },\ + {OpcodeInfo::all, {Size16, 0x81, opc_ext, iw}, {r_m16, imm16}, def_use },\ + {OpcodeInfo::all, {0x81, opc_ext, id}, {r_m32, imm32}, def_use },\ + {OpcodeInfo::em64t, {REX_W, 0x81, opc_ext, id}, {r_m64, imm32s}, def_use },\ +\ + {OpcodeInfo::all, {Size16, 0x83, opc_ext, ib}, {r_m16, imm8s}, def_use },\ + {OpcodeInfo::all, {0x83, opc_ext, ib}, {r_m32, imm8s}, def_use },\ + {OpcodeInfo::em64t, {REX_W, 0x83, opc_ext, ib}, {r_m64, imm8s}, def_use },\ +\ + {OpcodeInfo::all, {first_opcode, _r}, {r_m8, r8}, def_use },\ +\ + {OpcodeInfo::all, {Size16, opcode_starts_from+1, _r}, {r_m16, r16}, def_use },\ + {OpcodeInfo::all, {opcode_starts_from+1, _r}, {r_m32, r32}, def_use },\ + {OpcodeInfo::em64t, {REX_W, opcode_starts_from+1, _r}, {r_m64, r64}, def_use },\ +\ + {OpcodeInfo::all, {opcode_starts_from+2, _r}, {r8, r_m8}, def_use },\ +\ + {OpcodeInfo::all, {Size16, opcode_starts_from+3, _r}, {r16, r_m16}, def_use },\ + {OpcodeInfo::all, {opcode_starts_from+3, _r}, {r32, r_m32}, def_use },\ + {OpcodeInfo::em64t, {REX_W, opcode_starts_from+3, _r}, {r64, r_m64}, def_use }, + +BEGIN_MNEMONIC(ADD, MF_AFFECTS_FLAGS|MF_SYMMETRIC, DU_U) +BEGIN_OPCODES() + DEFINE_ALU_OPCODES(_0, 0x00, OxOO, DU_U ) +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(OR, MF_AFFECTS_FLAGS|MF_SYMMETRIC, DU_U) +BEGIN_OPCODES() + DEFINE_ALU_OPCODES(_1, 0x08, 0x08, DU_U ) +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(ADC, MF_AFFECTS_FLAGS|MF_USES_FLAGS|MF_SYMMETRIC, DU_U) +BEGIN_OPCODES() + DEFINE_ALU_OPCODES(_2, 0x10, 0x10, DU_U ) +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(SBB, MF_AFFECTS_FLAGS|MF_USES_FLAGS, DU_U) +BEGIN_OPCODES() + DEFINE_ALU_OPCODES(_3, 0x18, 0x18, DU_U ) +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(AND, MF_AFFECTS_FLAGS|MF_SYMMETRIC, DU_U) +BEGIN_OPCODES() + DEFINE_ALU_OPCODES(_4, 0x20, 0x20, DU_U ) +END_OPCODES() +END_MNEMONIC() + + +BEGIN_MNEMONIC(SUB, MF_AFFECTS_FLAGS|MF_SAME_ARG_NO_USE, DU_U) +BEGIN_OPCODES() + DEFINE_ALU_OPCODES(_5, 0x28, 0x28, DU_U ) +END_OPCODES() +END_MNEMONIC() + + +BEGIN_MNEMONIC(XOR, MF_AFFECTS_FLAGS|MF_SYMMETRIC|MF_SAME_ARG_NO_USE, DU_U) +BEGIN_OPCODES() + DEFINE_ALU_OPCODES( _6, 0x30, 0x30, DU_U ) +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(CMP, MF_AFFECTS_FLAGS, U_U) +BEGIN_OPCODES() + DEFINE_ALU_OPCODES( _7, 0x38, 0x38, U_U ) +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(CMPXCHG, MF_AFFECTS_FLAGS, N) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x0F, 0xB0, _r}, {r_m8, r8, AL}, DU_DU_DU }, + {OpcodeInfo::all, {Size16, 0x0F, 0xB1, _r}, {r_m16, r16, AX}, DU_DU_DU }, + {OpcodeInfo::all, {0x0F, 0xB1, _r}, {r_m32, r32, EAX}, DU_DU_DU}, + {OpcodeInfo::em64t, {REX_W, 0x0F, 0xB1, _r}, {r_m64, r64, RAX}, DU_DU_DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(CMPXCHG8B, MF_AFFECTS_FLAGS, D) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x0F, 0xC7, _1}, {m64}, DU }, +END_OPCODES() +END_MNEMONIC() + +#undef DEFINE_ALU_OPCODES +// +// +// +BEGIN_MNEMONIC(ADDSD, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF2, 0x0F, 0x58, _r}, {xmm64, xmm_m64}, DU_U}, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(ADDSS, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF3, 0x0F, 0x58, _r}, {xmm32, xmm_m32}, DU_U}, +END_OPCODES() +END_MNEMONIC() + + +BEGIN_MNEMONIC(BSF, MF_AFFECTS_FLAGS, N) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x0F, 0xBC}, {r32, r_m32}, D_U}, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(BSR, MF_AFFECTS_FLAGS, N) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x0F, 0xBD}, {r32, r_m32}, D_U}, +END_OPCODES() +END_MNEMONIC() + + +BEGIN_MNEMONIC(CALL, MF_NONE, U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xE8, cd}, {rel32}, U }, + {OpcodeInfo::ia32, {Size16, 0xE8, cw}, {rel16}, U }, + {OpcodeInfo::ia32, {0xFF, _2}, {r_m32}, U }, + {OpcodeInfo::em64t, {0xFF, _2}, {r_m64}, U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(CMC, MF_USES_FLAGS|MF_AFFECTS_FLAGS, N) +BEGIN_OPCODES() + {OpcodeInfo::decoder, {0xF5}, {}, N }, +END_OPCODES() +END_MNEMONIC() + +//TODO: Workaround. Actually, it's D_DU, but Jitrino's CG thinks it's D_U +BEGIN_MNEMONIC(CDQ, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x99}, {DX, AX}, D_U }, + {OpcodeInfo::all, {0x99}, {EDX, EAX}, D_U }, + {OpcodeInfo::em64t, {REX_W, 0x99}, {RDX, RAX}, D_U }, +END_OPCODES() +END_MNEMONIC() + +#define DEFINE_CMOVcc_MNEMONIC( cc ) \ + BEGIN_MNEMONIC(CMOV##cc, MF_USES_FLAGS|MF_CONDITIONAL, DU_U ) \ +BEGIN_OPCODES() \ + {OpcodeInfo::all, {Size16, 0x0F, 0x40 + ConditionMnemonic_##cc, _r}, {r16, r_m16}, DU_U }, \ + {OpcodeInfo::all, {0x0F, 0x40 + ConditionMnemonic_##cc, _r}, {r32, r_m32}, DU_U }, \ + {OpcodeInfo::em64t, {REX_W, 0x0F, 0x40 + ConditionMnemonic_##cc, _r}, {r64, r_m64}, DU_U }, \ +END_OPCODES() \ +END_MNEMONIC() + +DEFINE_CMOVcc_MNEMONIC(O) +DEFINE_CMOVcc_MNEMONIC(NO) +DEFINE_CMOVcc_MNEMONIC(B) +DEFINE_CMOVcc_MNEMONIC(NB) +DEFINE_CMOVcc_MNEMONIC(Z) +DEFINE_CMOVcc_MNEMONIC(NZ) +DEFINE_CMOVcc_MNEMONIC(BE) +DEFINE_CMOVcc_MNEMONIC(NBE) +DEFINE_CMOVcc_MNEMONIC(S) +DEFINE_CMOVcc_MNEMONIC(NS) +DEFINE_CMOVcc_MNEMONIC(P) +DEFINE_CMOVcc_MNEMONIC(NP) +DEFINE_CMOVcc_MNEMONIC(L) +DEFINE_CMOVcc_MNEMONIC(NL) +DEFINE_CMOVcc_MNEMONIC(LE) +DEFINE_CMOVcc_MNEMONIC(NLE) + +#undef DEFINE_CMOVcc_MNEMONIC + +/***************************************************************************** + ***** SSE conversion routines ***** +*****************************************************************************/ +// +// double -> float +BEGIN_MNEMONIC(CVTSD2SS, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF2, 0x0F, 0x5A, _r}, {xmm32, xmm_m64}, D_U }, +END_OPCODES() +END_MNEMONIC() + +// double -> I_32 +BEGIN_MNEMONIC(CVTSD2SI, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF2, 0x0F, 0x2D, _r}, {r32, xmm_m64}, D_U }, + {OpcodeInfo::em64t, {REX_W, 0xF2, 0x0F, 0x2D, _r}, {r64, xmm_m64}, D_U }, +END_OPCODES() +END_MNEMONIC() + +// double [truncated] -> I_32 +BEGIN_MNEMONIC(CVTTSD2SI, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF2, 0x0F, 0x2C, _r}, {r32, xmm_m64}, D_U }, + {OpcodeInfo::em64t, {REX_W, 0xF2, 0x0F, 0x2C, _r}, {r64, xmm_m64}, D_U }, +END_OPCODES() +END_MNEMONIC() + +// float -> double +BEGIN_MNEMONIC(CVTSS2SD, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF3, 0x0F, 0x5A, _r}, {xmm64, xmm_m32}, D_U }, +END_OPCODES() +END_MNEMONIC() + +// float -> I_32 +BEGIN_MNEMONIC(CVTSS2SI, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF3, 0x0F, 0x2D, _r}, {r32, xmm_m32}, D_U}, + {OpcodeInfo::em64t, {REX_W, 0xF3, 0x0F, 0x2D, _r}, {r64, xmm_m32}, D_U}, +END_OPCODES() +END_MNEMONIC() + +// float [truncated] -> I_32 +BEGIN_MNEMONIC(CVTTSS2SI, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF3, 0x0F, 0x2C, _r}, {r32, xmm_m32}, D_U}, + {OpcodeInfo::em64t, {REX_W, 0xF3, 0x0F, 0x2C, _r}, {r64, xmm_m32}, D_U}, +END_OPCODES() +END_MNEMONIC() + +// I_32 -> double +BEGIN_MNEMONIC(CVTSI2SD, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF2, 0x0F, 0x2A, _r}, {xmm64, r_m32}, D_U}, + {OpcodeInfo::em64t, {REX_W, 0xF2, 0x0F, 0x2A, _r}, {xmm64, r_m64}, D_U}, +END_OPCODES() +END_MNEMONIC() + +// I_32 -> float +BEGIN_MNEMONIC(CVTSI2SS, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF3, 0x0F, 0x2A, _r}, {xmm32, r_m32}, D_U}, + {OpcodeInfo::em64t, {REX_W, 0xF3, 0x0F, 0x2A, _r}, {xmm32, r_m64}, D_U}, +END_OPCODES() +END_MNEMONIC() + +// +// ~ SSE conversions +// + +BEGIN_MNEMONIC(DEC, MF_AFFECTS_FLAGS, DU ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xFE, _1}, {r_m8}, DU }, + + {OpcodeInfo::all, {Size16, 0xFF, _1}, {r_m16}, DU }, + {OpcodeInfo::all, {0xFF, _1}, {r_m32}, DU }, + {OpcodeInfo::em64t, {REX_W, 0xFF, _1}, {r_m64}, DU }, + + {OpcodeInfo::ia32, {Size16, 0x48|rw}, {r16}, DU }, + {OpcodeInfo::ia32, {0x48|rd}, {r32}, DU }, +END_OPCODES() +END_MNEMONIC() + + +BEGIN_MNEMONIC(DIVSD, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF2, 0x0F, 0x5E, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + + +BEGIN_MNEMONIC(DIVSS, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF3, 0x0F, 0x5E, _r}, {xmm32, xmm_m32}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +/**************************************************************************** + ***** FPU operations ***** +****************************************************************************/ + +BEGIN_MNEMONIC(FADDP, MF_NONE, DU ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xDE, 0xC1}, {FP0D}, DU }, + {OpcodeInfo::all, {0xDE, 0xC1}, {FP0S}, DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FLDZ, MF_NONE, U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, 0xEE}, {FP0D}, D }, + {OpcodeInfo::all, {0xD9, 0xEE}, {FP0S}, D }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FADD, MF_NONE, U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xDC, _0}, {FP0D, m64}, DU_U }, + {OpcodeInfo::all, {0xD8, _0}, {FP0S, m32}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FSUBP, MF_NONE, DU ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xDE, 0xE9}, {FP0D}, DU }, + {OpcodeInfo::all, {0xDE, 0xE9}, {FP0S}, DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FSUB, MF_NONE, U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xDC, _4}, {FP0D, m64}, DU_U }, + {OpcodeInfo::all, {0xD8, _4}, {FP0S, m32}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FISUB, MF_NONE, U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xDA, _4}, {FP0S, m32}, DU_U }, +// {OpcodeInfo::all, {0xDE, _4}, {FP0S, m16}, DU_U }, +END_OPCODES() +END_MNEMONIC() + + + +BEGIN_MNEMONIC(FMUL, MF_NONE, DU_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xD8, _1}, {FP0S, m32}, DU_U }, + {OpcodeInfo::all, {0xDC, _1}, {FP0D, m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FMULP, MF_NONE, DU ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xDE, 0xC9}, {FP0D}, DU }, + {OpcodeInfo::all, {0xDE, 0xC9}, {FP0S}, DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FDIVP, MF_NONE, DU ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xDE, 0xF9}, {FP0D}, DU }, + {OpcodeInfo::all, {0xDE, 0xF9}, {FP0S}, DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FDIV, MF_NONE, U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xDC, _6}, {FP0D, m64}, DU_U }, + {OpcodeInfo::all, {0xD8, _6}, {FP0S, m32}, DU_U }, +END_OPCODES() +END_MNEMONIC() + + +BEGIN_MNEMONIC(FUCOM, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xDD, 0xE1}, {FP0D, FP1D}, DU_U }, + {OpcodeInfo::all, {0xDD, 0xE1}, {FP0S, FP1S}, DU_U }, + // A little trick: actually, these 2 opcodes take only index of the + // needed register. To make the things similar to other instructions + // we encode here as if they took FPREG. + {OpcodeInfo::all, {0xDD, 0xE0|_i}, {fp32}, DU }, + {OpcodeInfo::all, {0xDD, 0xE0|_i}, {fp64}, DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FUCOMI, MF_NONE, D_U ) +BEGIN_OPCODES() + // A little trick: actually, these 2 opcodes take only index of the + // needed register. To make the things similar to other instructions + // we encode here as if they took FPREG. + {OpcodeInfo::all, {0xDB, 0xE8|_i}, {fp32}, DU }, + {OpcodeInfo::all, {0xDB, 0xE8|_i}, {fp64}, DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FUCOMP, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xDD, 0xE9}, {FP0D, FP1D}, DU_U }, + {OpcodeInfo::all, {0xDD, 0xE9}, {FP0S, FP1S}, DU_U }, + // A little trick: actually, these 2 opcodes take only index of the + // needed register. To make the things similar to other instructions + // we encode here as if they took FPREG. + {OpcodeInfo::all, {0xDD, 0xE8|_i}, {fp32}, DU }, + {OpcodeInfo::all, {0xDD, 0xE8|_i}, {fp64}, DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FUCOMIP, MF_NONE, D_U ) +BEGIN_OPCODES() + // A little trick: actually, these 2 opcodes take only index of the + // needed register. To make the things similar to other instructions + // we encode here as if they took FPREG. + {OpcodeInfo::all, {0xDF, 0xE8|_i}, {fp32}, DU }, + {OpcodeInfo::all, {0xDF, 0xE8|_i}, {fp64}, DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FUCOMPP, MF_NONE, U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xDA, 0xE9}, {FP0D, FP1D}, DU_U }, + {OpcodeInfo::all, {0xDA, 0xE9}, {FP0S, FP1S}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FLDCW, MF_NONE, U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, _5}, {m16}, U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FNSTCW, MF_NONE, D) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, _7}, {m16}, D }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FSTSW, MF_NONE, D) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x9B, 0xDF, 0xE0}, {EAX}, D }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FNSTSW, MF_NONE, D) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xDF, 0xE0}, {EAX}, D }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FCHS, MF_NONE, DU ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, 0xE0}, {FP0D}, DU }, + {OpcodeInfo::all, {0xD9, 0xE0}, {FP0S}, DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FCLEX, MF_NONE, N) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x9B, 0xDB, 0xE2}, {}, N }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FNCLEX, MF_NONE, N) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xDB, 0xE2}, {}, N }, +END_OPCODES() +END_MNEMONIC() + +//BEGIN_MNEMONIC(FDECSTP, MF_NONE, N) +// BEGIN_OPCODES() +// {OpcodeInfo::all, {0xD9, 0xF6}, {}, N }, +// END_OPCODES() +//END_MNEMONIC() + +BEGIN_MNEMONIC(FILD, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xDB, _0}, {FP0S, m32}, D_U }, + {OpcodeInfo::all, {0xDF, _5}, {FP0D, m64}, D_U }, + {OpcodeInfo::all, {0xDB, _0}, {FP0S, m32}, D_U }, +END_OPCODES() +END_MNEMONIC() + +//BEGIN_MNEMONIC(FINCSTP, MF_NONE, N) +// BEGIN_OPCODES() +// {OpcodeInfo::all, {0xD9, 0xF7}, {}, N }, +// END_OPCODES() +//END_MNEMONIC() + +BEGIN_MNEMONIC(FIST, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xDB, _2}, {m32, FP0S}, D_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FISTP, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xDB, _3}, {m32, FP0S}, D_U }, + {OpcodeInfo::all, {0xDF, _7}, {m64, FP0D}, D_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FISTTP, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xDD, _1}, {m64, FP0D}, D_U }, + {OpcodeInfo::all, {0xDB, _1}, {m32, FP0S}, D_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FRNDINT, MF_NONE, DU ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, 0xFC}, {FP0S}, DU }, + {OpcodeInfo::all, {0xD9, 0xFC}, {FP0D}, DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FLD, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, _0}, {FP0S, m32}, D_U }, + {OpcodeInfo::all, {0xDD, _0}, {FP0D, m64}, D_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FLDLG2, MF_NONE, U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, 0xEC}, {FP0S}, D }, + {OpcodeInfo::all, {0xD9, 0xEC}, {FP0D}, D }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FLDLN2, MF_NONE, U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, 0xED}, {FP0S}, D }, + {OpcodeInfo::all, {0xD9, 0xED}, {FP0D}, D }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FLD1, MF_NONE, U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, 0xE8}, {FP0S}, D }, + {OpcodeInfo::all, {0xD9, 0xE8}, {FP0D}, D }, +END_OPCODES() +END_MNEMONIC() + + +BEGIN_MNEMONIC(FPREM, MF_NONE, N) + BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, 0xF8}, {}, N }, + END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FPREM1, MF_NONE, N) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, 0xF5}, {}, N }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FST, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, _2}, {m32, FP0S}, D_U }, + {OpcodeInfo::all, {0xDD, _2}, {m64, FP0D}, D_U }, + // A little trick: actually, these 2 opcodes take only index of the + // needed register. To make the things similar to other instructions + // we encode here as if they took FPREG. + {OpcodeInfo::all, {0xDD, 0xD0|_i}, {fp32}, D }, + {OpcodeInfo::all, {0xDD, 0xD0|_i}, {fp64}, D }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FSTP, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, _3}, {m32, FP0S}, D_U }, + {OpcodeInfo::all, {0xDD, _3}, {m64, FP0D}, D_U }, + // A little trick: actually, these 2 opcodes take only index of the + // needed register. To make the things similar to other instructions + // we encode here as if they took FPREG. + {OpcodeInfo::all, {0xDD, 0xD8|_i}, {fp32}, D }, + {OpcodeInfo::all, {0xDD, 0xD8|_i}, {fp64}, D }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FSQRT, MF_NONE, DU) + BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, 0xFA}, {FP0S}, DU }, + {OpcodeInfo::all, {0xD9, 0xFA}, {FP0D}, DU }, + END_OPCODES() +END_MNEMONIC() + + +BEGIN_MNEMONIC(FYL2X, MF_NONE, DU) + BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, 0xF1}, {FP0S}, DU }, + {OpcodeInfo::all, {0xD9, 0xF1}, {FP0D}, DU }, + END_OPCODES() +END_MNEMONIC() + + +BEGIN_MNEMONIC(FYL2XP1, MF_NONE, DU) + BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, 0xF9}, {FP0S}, DU }, + {OpcodeInfo::all, {0xD9, 0xF9}, {FP0D}, DU }, + END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(F2XM1, MF_NONE, DU) + BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, 0xF0}, {FP0S}, DU }, + {OpcodeInfo::all, {0xD9, 0xF0}, {FP0D}, DU }, + END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FPATAN, MF_NONE, DU) + BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, 0xF3}, {FP0S}, DU }, + {OpcodeInfo::all, {0xD9, 0xF3}, {FP0D}, DU }, + END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FXCH, MF_NONE, DU) + BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, 0xC9}, {FP0S}, DU }, + {OpcodeInfo::all, {0xD9, 0xC9}, {FP0D}, DU }, + END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FSCALE, MF_NONE, DU) + BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, 0xFD}, {FP0S}, DU }, + {OpcodeInfo::all, {0xD9, 0xFD}, {FP0D}, DU }, + END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FABS, MF_NONE, DU) + BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, 0xE1}, {FP0S}, DU }, + {OpcodeInfo::all, {0xD9, 0xE1}, {FP0D}, DU }, + END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FSIN, MF_NONE, DU) + BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, 0xFE}, {FP0S}, DU }, + {OpcodeInfo::all, {0xD9, 0xFE}, {FP0D}, DU }, + END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FCOS, MF_NONE, DU) + BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, 0xFF}, {FP0S}, DU }, + {OpcodeInfo::all, {0xD9, 0xFF}, {FP0D}, DU }, + END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(FPTAN, MF_NONE, DU) + BEGIN_OPCODES() + {OpcodeInfo::all, {0xD9, 0xF2}, {FP0S}, DU }, + {OpcodeInfo::all, {0xD9, 0xF2}, {FP0D}, DU }, + END_OPCODES() +END_MNEMONIC() + +// +// ~ FPU +// + +BEGIN_MNEMONIC(DIV, MF_AFFECTS_FLAGS, DU_DU_U) +BEGIN_OPCODES() +#if !defined(_EM64T_) + {OpcodeInfo::all, {0xF6, _6}, {AH, AL, r_m8}, DU_DU_U }, + {OpcodeInfo::all, {Size16, 0xF7, _6}, {DX, AX, r_m16}, DU_DU_U }, +#endif + {OpcodeInfo::all, {0xF7, _6}, {EDX, EAX, r_m32}, DU_DU_U }, + {OpcodeInfo::em64t, {REX_W, 0xF7, _6}, {RDX, RAX, r_m64}, DU_DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(IDIV, MF_AFFECTS_FLAGS, DU_DU_U) +BEGIN_OPCODES() +#if !defined(_EM64T_) + {OpcodeInfo::all, {0xF6, _7}, {AH, AL, r_m8}, DU_DU_U }, + {OpcodeInfo::all, {Size16, 0xF7, _7}, {DX, AX, r_m16}, DU_DU_U }, +#endif + {OpcodeInfo::all, {0xF7, _7}, {EDX, EAX, r_m32}, DU_DU_U }, + {OpcodeInfo::em64t, {REX_W, 0xF7, _7}, {RDX, RAX, r_m64}, DU_DU_U }, +END_OPCODES() +END_MNEMONIC() + + +BEGIN_MNEMONIC(IMUL, MF_AFFECTS_FLAGS, D_DU_U) +BEGIN_OPCODES() + /*{OpcodeInfo::all, {0xF6, _5}, {AH, AL, r_m8}, D_DU_U }, + {OpcodeInfo::all, {Size16, 0xF7, _5}, {DX, AX, r_m16}, D_DU_U }, + */ + // + {OpcodeInfo::all, {0xF7, _5}, {EDX, EAX, r_m32}, D_DU_U }, + //todo: this opcode's hash conflicts with IMUL r64,r_m64 - they're both 0. + // this particular is not currently used, so we may safely drop it, but need to + // revisit the hash implementation + // {OpcodeInfo::em64t, {REX_W, 0xF7, _5}, {RDX, RAX, r_m64}, D_DU_U }, + // + {OpcodeInfo::all, {Size16, 0x0F, 0xAF, _r}, {r16,r_m16}, DU_U }, + {OpcodeInfo::all, {0x0F, 0xAF, _r}, {r32,r_m32}, DU_U }, + {OpcodeInfo::em64t, {REX_W, 0x0F, 0xAF, _r}, {r64,r_m64}, DU_U }, + {OpcodeInfo::all, {Size16, 0x6B, _r, ib}, {r16,r_m16,imm8s}, D_DU_U }, + {OpcodeInfo::all, {0x6B, _r, ib}, {r32,r_m32,imm8s}, D_DU_U }, + {OpcodeInfo::em64t, {REX_W, 0x6B, _r, ib}, {r64,r_m64,imm8s}, D_DU_U }, + {OpcodeInfo::all, {Size16, 0x6B, _r, ib}, {r16,imm8s}, DU_U }, + {OpcodeInfo::all, {0x6B, _r, ib}, {r32,imm8s}, DU_U }, + {OpcodeInfo::em64t, {REX_W, 0x6B, _r, ib}, {r64,imm8s}, DU_U }, + {OpcodeInfo::all, {Size16, 0x69, _r, iw}, {r16,r_m16,imm16}, D_U_U }, + {OpcodeInfo::all, {0x69, _r, id}, {r32,r_m32,imm32}, D_U_U }, + {OpcodeInfo::em64t, {REX_W, 0x69, _r, id}, {r64,r_m64,imm32s}, D_U_U }, + {OpcodeInfo::all, {Size16, 0x69, _r, iw}, {r16,imm16}, DU_U }, + {OpcodeInfo::all, {0x69, _r, id}, {r32,imm32}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(MUL, MF_AFFECTS_FLAGS, U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF6, _4}, {AX, AL, r_m8}, D_DU_U }, + {OpcodeInfo::all, {Size16, 0xF7, _4}, {DX, AX, r_m16}, D_DU_U }, + {OpcodeInfo::all, {0xF7, _4}, {EDX, EAX, r_m32}, D_DU_U }, + {OpcodeInfo::em64t, {REX_W, 0xF7, _4}, {RDX, RAX, r_m64}, D_DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(INC, MF_AFFECTS_FLAGS, DU ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xFE, _0}, {r_m8}, DU }, + {OpcodeInfo::all, {Size16, 0xFF, _0}, {r_m16}, DU }, + {OpcodeInfo::all, {0xFF, _0}, {r_m32}, DU }, + {OpcodeInfo::em64t, {REX_W, 0xFF, _0}, {r_m64}, DU }, + {OpcodeInfo::ia32, {Size16, 0x40|rw}, {r16}, DU }, + {OpcodeInfo::ia32, {0x40|rd}, {r32}, DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(INT3, MF_NONE, N) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xCC}, {}, N }, +END_OPCODES() +END_MNEMONIC() + +#define DEFINE_Jcc_MNEMONIC( cc ) \ + BEGIN_MNEMONIC(J##cc, MF_USES_FLAGS|MF_CONDITIONAL, U ) \ +BEGIN_OPCODES() \ + {OpcodeInfo::all, {0x70 + ConditionMnemonic_##cc, cb }, { rel8 }, U }, \ + {OpcodeInfo::ia32, {Size16, 0x0F, 0x80 + ConditionMnemonic_##cc, cw}, { rel16 }, U }, \ + {OpcodeInfo::all, {0x0F, 0x80 + ConditionMnemonic_##cc, cd}, { rel32 }, U }, \ +END_OPCODES() \ +END_MNEMONIC() + + +DEFINE_Jcc_MNEMONIC(O) +DEFINE_Jcc_MNEMONIC(NO) +DEFINE_Jcc_MNEMONIC(B) +DEFINE_Jcc_MNEMONIC(NB) +DEFINE_Jcc_MNEMONIC(Z) +DEFINE_Jcc_MNEMONIC(NZ) +DEFINE_Jcc_MNEMONIC(BE) +DEFINE_Jcc_MNEMONIC(NBE) + +DEFINE_Jcc_MNEMONIC(S) +DEFINE_Jcc_MNEMONIC(NS) +DEFINE_Jcc_MNEMONIC(P) +DEFINE_Jcc_MNEMONIC(NP) +DEFINE_Jcc_MNEMONIC(L) +DEFINE_Jcc_MNEMONIC(NL) +DEFINE_Jcc_MNEMONIC(LE) +DEFINE_Jcc_MNEMONIC(NLE) + +#undef DEFINE_Jcc_MNEMONIC + +BEGIN_MNEMONIC(JMP, MF_NONE, U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xEB, cb}, {rel8}, U }, + {OpcodeInfo::ia32, {Size16, 0xE9, cw}, {rel16}, U }, + {OpcodeInfo::all, {0xE9, cd}, {rel32}, U }, + {OpcodeInfo::ia32, {Size16, 0xFF, _4}, {r_m16}, U }, + {OpcodeInfo::ia32, {0xFF, _4}, {r_m32}, U }, + {OpcodeInfo::em64t, {0xFF, _4}, {r_m64}, U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(LEA, MF_NONE, D_U ) +BEGIN_OPCODES() + /* + A special case: the LEA instruction itself does not care about size of + second operand. This is obviuos why it is, and thus in The Manual, a + simple 'm' without size is used. + However, in the Jitrino's instrucitons we'll have an operand with a size. + Also, the hashing scheme is not supposed to handle OpndSize_Null, and + making it to do so will lead to unnecessary complication of hashing + scheme. Thus, instead of handling it as a special case, we simply make + copies of the opcodes with sizes set. + {OpcodeInfo::all, {0x8D, _r}, {r32, m}, D_U }, + {OpcodeInfo::em64t, {0x8D, _r}, {r64, m}, D_U }, + */ + //Android x86: keep r32, m32 only, otherwise, will have decoding error + //{OpcodeInfo::all, {0x8D, _r}, {r32, m8}, D_U }, + {OpcodeInfo::em64t, {REX_W, 0x8D, _r}, {r64, m8}, D_U }, + //{OpcodeInfo::all, {0x8D, _r}, {r32, m16}, D_U }, + {OpcodeInfo::em64t, {REX_W, 0x8D, _r}, {r64, m16}, D_U }, + {OpcodeInfo::all, {0x8D, _r}, {r32, m32}, D_U }, + {OpcodeInfo::em64t, {REX_W, 0x8D, _r}, {r64, m32}, D_U }, + {OpcodeInfo::all, {0x8D, _r}, {r32, m64}, D_U }, + {OpcodeInfo::em64t, {REX_W, 0x8D, _r}, {r64, m64}, D_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(LOOP, MF_AFFECTS_FLAGS|MF_USES_FLAGS, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xE2, cb}, {ECX, rel8}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(LOOPE, MF_AFFECTS_FLAGS|MF_USES_FLAGS, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xE1, cb}, {ECX, rel8}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(LOOPNE, MF_AFFECTS_FLAGS|MF_USES_FLAGS, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xE0, cb}, {ECX, rel8}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(MOV, MF_NONE, D_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x88, _r}, {r_m8,r8}, D_U }, + + {OpcodeInfo::all, {Size16, 0x89, _r}, {r_m16,r16}, D_U }, + {OpcodeInfo::all, {0x89, _r}, {r_m32,r32}, D_U }, + {OpcodeInfo::em64t, {REX_W, 0x89, _r}, {r_m64,r64}, D_U }, + {OpcodeInfo::all, {0x8A, _r}, {r8,r_m8}, D_U }, + + {OpcodeInfo::all, {Size16, 0x8B, _r}, {r16,r_m16}, D_U }, + {OpcodeInfo::all, {0x8B, _r}, {r32,r_m32}, D_U }, + {OpcodeInfo::em64t, {REX_W, 0x8B, _r}, {r64,r_m64}, D_U }, + + {OpcodeInfo::all, {0xB0|rb}, {r8,imm8}, D_U }, + + {OpcodeInfo::all, {Size16, 0xB8|rw}, {r16,imm16}, D_U }, + {OpcodeInfo::all, {0xB8|rd}, {r32,imm32}, D_U }, + {OpcodeInfo::em64t, {REX_W, 0xB8|rd}, {r64,imm64}, D_U }, + {OpcodeInfo::all, {0xC6, _0}, {r_m8,imm8}, D_U }, + + {OpcodeInfo::all, {Size16, 0xC7, _0}, {r_m16,imm16}, D_U }, + {OpcodeInfo::all, {0xC7, _0}, {r_m32,imm32}, D_U }, + {OpcodeInfo::em64t, {REX_W, 0xC7, _0}, {r_m64,imm32s}, D_U }, + + {OpcodeInfo::decoder, {0xA0}, {AL, moff8}, D_U }, + {OpcodeInfo::decoder, {Size16, 0xA1}, {AX, moff16}, D_U }, + {OpcodeInfo::decoder, {0xA1}, {EAX, moff32}, D_U }, + //{OpcodeInfo::decoder64, {REX_W, 0xA1}, {RAX, moff64}, D_U }, + + {OpcodeInfo::decoder, {0xA2}, {moff8, AL}, D_U }, + {OpcodeInfo::decoder, {Size16, 0xA3}, {moff16, AX}, D_U }, + {OpcodeInfo::decoder, {0xA3}, {moff32, EAX}, D_U }, + //{OpcodeInfo::decoder64, {REX_W, 0xA3}, {moff64, RAX}, D_U }, +END_OPCODES() +END_MNEMONIC() + + + +BEGIN_MNEMONIC(XCHG, MF_NONE, DU_DU ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x87, _r}, {r_m32,r32}, DU_DU }, +END_OPCODES() +END_MNEMONIC() + + +BEGIN_MNEMONIC(MOVQ, MF_NONE, D_U ) +BEGIN_OPCODES() +#ifdef _HAVE_MMX_ + {OpcodeInfo::all, {0x0F, 0x6F, _r}, {mm64, mm_m64}, D_U }, + {OpcodeInfo::all, {0x0F, 0x7F, _r}, {mm_m64, mm64}, D_U }, +#endif + {OpcodeInfo::all, {0xF3, 0x0F, 0x7E }, {xmm64, xmm_m64}, D_U }, + {OpcodeInfo::all, {0x66, 0x0F, 0xD6 }, {xmm_m64, xmm64}, D_U }, +// {OpcodeInfo::em64t, {REX_W, 0x66, 0x0F, 0x6E, _r}, {xmm64, r_m64}, D_U }, +// {OpcodeInfo::em64t, {REX_W, 0x66, 0x0F, 0x7E, _r}, {r_m64, xmm64}, D_U }, + {OpcodeInfo::em64t, {REX_W, 0x66, 0x0F, 0x6E, _r}, {xmm64, r64}, D_U }, + {OpcodeInfo::em64t, {REX_W, 0x66, 0x0F, 0x7E, _r}, {r64, xmm64}, D_U }, +END_OPCODES() +END_MNEMONIC() + + +BEGIN_MNEMONIC(MOVD, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0x6E, _r}, {xmm32, r_m32}, D_U }, + {OpcodeInfo::all, {0x66, 0x0F, 0x7E, _r}, {r_m32, xmm32}, D_U }, +END_OPCODES() +END_MNEMONIC() + +// +// A bunch of MMX instructions +// +#ifdef _HAVE_MMX_ + +BEGIN_MNEMONIC(EMMS, MF_NONE, N) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x0F, 0x77}, {}, N }, +END_OPCODES() +END_MNEMONIC() + +#endif + +BEGIN_MNEMONIC(PADDQ, MF_NONE, DU_U) +BEGIN_OPCODES() +#ifdef _HAVE_MMX_ + {OpcodeInfo::all, {0x0F, 0xD4, _r}, {mm64, mm_m64}, DU_U }, +#endif + {OpcodeInfo::all, {0x66, 0x0F, 0xD4, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PAND, MF_NONE, DU_U) +BEGIN_OPCODES() +#ifdef _HAVE_MMX_ + {OpcodeInfo::all, {0x0F, 0xDB, _r}, {mm64, mm_m64}, DU_U }, +#endif + {OpcodeInfo::all, {0x66, 0x0F, 0xDB, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(POR, MF_NONE, DU_U) +BEGIN_OPCODES() +#ifdef _HAVE_MMX_ + {OpcodeInfo::all, {0x0F, 0xEB, _r}, {mm64, mm_m64}, DU_U }, +#endif + {OpcodeInfo::all, {0x66, 0x0F, 0xEB, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PSUBQ, MF_NONE, DU_U) +BEGIN_OPCODES() +#ifdef _HAVE_MMX_ + {OpcodeInfo::all, {0x0F, 0xFB, _r}, {mm64, mm_m64}, DU_U }, +#endif + {OpcodeInfo::all, {0x66, 0x0F, 0xFB, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PANDN, MF_NONE, DU_U) +BEGIN_OPCODES() +#ifdef _HAVE_MMX_ + {OpcodeInfo::all, {0x0F, 0xDF, _r}, {mm64, mm_m64}, DU_U }, +#endif + {OpcodeInfo::all, {0x66, 0x0F, 0xDF, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() +BEGIN_MNEMONIC(PSLLQ, MF_NONE, DU_U) +BEGIN_OPCODES() +#ifdef _HAVE_MMX_ + {OpcodeInfo::all, {0x0F, 0xF3, _r}, {mm64, mm_m64}, DU_U }, +#endif + {OpcodeInfo::all, {0x66, 0x0F, 0xF3, _r}, {xmm64, xmm_m64}, DU_U }, + {OpcodeInfo::all, {0x66, 0x0F, 0x73, _6, ib}, {xmm64, imm8}, DU_U }, +END_OPCODES() +END_MNEMONIC() +BEGIN_MNEMONIC(PSRLQ, MF_NONE, DU_U) +BEGIN_OPCODES() +#ifdef _HAVE_MMX_ + {OpcodeInfo::all, {0x0F, 0xD3, _r}, {mm64, mm_m64}, DU_U }, +#endif + {OpcodeInfo::all, {0x66, 0x0F, 0xD3, _r}, {xmm64, xmm_m64}, DU_U }, + {OpcodeInfo::all, {0x66, 0x0F, 0x73, _2, ib}, {xmm64, imm8}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PXOR, MF_NONE, DU_U) +BEGIN_OPCODES() +#ifdef _HAVE_MMX_ + {OpcodeInfo::all, {0x0F, 0xEF, _r}, {mm64, mm_m64}, DU_U }, +#endif + {OpcodeInfo::all, {0x66, 0x0F, 0xEF, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + + +BEGIN_MNEMONIC(MOVAPD, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0x28, _r}, {xmm64, xmm_m64}, D_U }, + {OpcodeInfo::all, {0x66, 0x0F, 0x29, _r}, {xmm_m64, xmm64}, D_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(MOVAPS, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x0F, 0x28, _r}, {xmm64, xmm_m64}, D_U }, + {OpcodeInfo::all, {0x0F, 0x29, _r}, {xmm_m64, xmm64}, D_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(SHUFPS, MF_NONE, D_U_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x0F, 0xC6, _r, ib}, {xmm64, xmm_m64, imm8}, D_U_U }, +END_OPCODES() +END_MNEMONIC() + + +BEGIN_MNEMONIC(MOVSD, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF2, 0x0F, 0x10, _r}, {xmm64, xmm_m64}, D_U }, + {OpcodeInfo::all, {0xF2, 0x0F, 0x11, _r}, {xmm_m64, xmm64}, D_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(MOVSS, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF3, 0x0F, 0x10, _r}, {xmm32, xmm_m32}, D_U }, + {OpcodeInfo::all, {0xF3, 0x0F, 0x11, _r}, {xmm_m32, xmm32}, D_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(MOVSX, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {Size16, 0x0F, 0xBE, _r}, {r16, r_m8s}, D_U }, + {OpcodeInfo::all, {0x0F, 0xBE, _r}, {r32, r_m8s}, D_U }, + {OpcodeInfo::em64t, {REX_W, 0x0F, 0xBE, _r}, {r64, r_m8s}, D_U }, + + {OpcodeInfo::all, {0x0F, 0xBF, _r}, {r32, r_m16s}, D_U }, + {OpcodeInfo::em64t, {REX_W, 0x0F, 0xBF, _r}, {r64, r_m16s}, D_U }, + + {OpcodeInfo::em64t, {REX_W, 0x63, _r}, {r64, r_m32s}, D_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(MOVZX, MF_NONE, D_U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {Size16, 0x0F, 0xB6, _r}, {r16, r_m8u}, D_U }, + {OpcodeInfo::all, {0x0F, 0xB6, _r}, {r32, r_m8u}, D_U }, + {OpcodeInfo::em64t, {REX_W, 0x0F, 0xB6, _r}, {r64, r_m8u}, D_U }, + + {OpcodeInfo::all, {0x0F, 0xB7, _r}, {r32, r_m16u}, D_U }, + {OpcodeInfo::em64t, {REX_W, 0x0F, 0xB7, _r}, {r64, r_m16u}, D_U }, + //workaround to get r/rm32->r64 ZX mov functionality: + //simple 32bit reg copying zeros high bits in 64bit reg + {OpcodeInfo::em64t, {0x8B, _r}, {r64, r_m32u}, D_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(MULSD, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF2, 0x0F, 0x59, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(MULSS, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF3, 0x0F, 0x59, _r}, {xmm32, xmm_m32}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(NEG, MF_AFFECTS_FLAGS, DU ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF6, _3}, {r_m8}, DU }, + + {OpcodeInfo::all, {Size16, 0xF7, _3}, {r_m16}, DU }, + {OpcodeInfo::all, {0xF7, _3}, {r_m32}, DU }, + {OpcodeInfo::em64t, {REX_W, 0xF7, _3}, {r_m64}, DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(NOP, MF_NONE, N) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x90}, {}, N }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(NOT, MF_AFFECTS_FLAGS, DU ) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF6, _2}, {r_m8}, DU }, + {OpcodeInfo::all, {Size16, 0xF7, _2}, {r_m16}, DU }, + {OpcodeInfo::all, {0xF7, _2}, {r_m32}, DU }, + {OpcodeInfo::em64t, {REX_W, 0xF7, _2}, {r_m64}, DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(POP, MF_NONE, D) +BEGIN_OPCODES() + {OpcodeInfo::all, {Size16, 0x8F, _0}, {r_m16}, D }, + {OpcodeInfo::ia32, {0x8F, _0}, {r_m32}, D }, + {OpcodeInfo::em64t, {0x8F, _0}, {r_m64}, D }, + + {OpcodeInfo::all, {Size16, 0x58|rw }, {r16}, D }, + {OpcodeInfo::ia32, {0x58|rd }, {r32}, D }, + {OpcodeInfo::em64t, {0x58|rd }, {r64}, D }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(POPFD, MF_AFFECTS_FLAGS, N) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x9D}, {}, N }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PREFETCH, MF_NONE, U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x0F, 0x18, _0}, {m8}, U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PUSH, MF_NONE, U ) +BEGIN_OPCODES() + {OpcodeInfo::all, {Size16, 0xFF, _6}, {r_m16}, U }, + {OpcodeInfo::ia32, {0xFF, _6}, {r_m32}, U }, + {OpcodeInfo::em64t, {0xFF, _6}, {r_m64}, U }, + + {OpcodeInfo::all, {Size16, 0x50|rw }, {r16}, U }, + {OpcodeInfo::ia32, {0x50|rd }, {r32}, U }, + {OpcodeInfo::em64t, {0x50|rd }, {r64}, U }, + + {OpcodeInfo::all, {0x6A}, {imm8}, U }, + {OpcodeInfo::all, {Size16, 0x68}, {imm16}, U }, + {OpcodeInfo::ia32, {0x68}, {imm32}, U }, +// {OpcodeInfo::em64t, {0x68}, {imm64}, U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PUSHFD, MF_USES_FLAGS, N) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x9C}, {}, N }, +END_OPCODES() +END_MNEMONIC() + + +BEGIN_MNEMONIC(RET, MF_NONE, N) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xC3}, {}, N }, + {OpcodeInfo::all, {0xC2, iw}, {imm16}, U }, +END_OPCODES() +END_MNEMONIC() + +#define DEFINE_SETcc_MNEMONIC( cc ) \ + BEGIN_MNEMONIC(SET##cc, MF_USES_FLAGS|MF_CONDITIONAL, DU) \ +BEGIN_OPCODES() \ + {OpcodeInfo::all, {0x0F, 0x90 + ConditionMnemonic_##cc}, {r_m8}, DU }, \ +END_OPCODES() \ +END_MNEMONIC() + +DEFINE_SETcc_MNEMONIC(O) +DEFINE_SETcc_MNEMONIC(NO) +DEFINE_SETcc_MNEMONIC(B) +DEFINE_SETcc_MNEMONIC(NB) +DEFINE_SETcc_MNEMONIC(Z) +DEFINE_SETcc_MNEMONIC(NZ) +DEFINE_SETcc_MNEMONIC(BE) +DEFINE_SETcc_MNEMONIC(NBE) + +DEFINE_SETcc_MNEMONIC(S) +DEFINE_SETcc_MNEMONIC(NS) +DEFINE_SETcc_MNEMONIC(P) +DEFINE_SETcc_MNEMONIC(NP) +DEFINE_SETcc_MNEMONIC(L) +DEFINE_SETcc_MNEMONIC(NL) +DEFINE_SETcc_MNEMONIC(LE) +DEFINE_SETcc_MNEMONIC(NLE) + +#undef DEFINE_SETcc_MNEMONIC + +#define DEFINE_SHIFT_MNEMONIC(nam, slash_num, flags) \ +BEGIN_MNEMONIC(nam, flags, DU_U) \ +BEGIN_OPCODES()\ + /* D0 & D1 opcodes are added w/o 2nd operand (1) because */\ + /* they are used for decoding only so only instruction length is needed */\ + {OpcodeInfo::decoder, {0xD0, slash_num}, {r_m8/*,const_1*/}, DU },\ + {OpcodeInfo::all, {0xD2, slash_num}, {r_m8, CL}, DU_U },\ + {OpcodeInfo::all, {0xC0, slash_num, ib}, {r_m8, imm8}, DU_U },\ +\ + {OpcodeInfo::decoder, {Size16, 0xD1, slash_num}, {r_m16/*,const_1*/}, DU },\ + {OpcodeInfo::all, {Size16, 0xD3, slash_num}, {r_m16, CL}, DU_U },\ + {OpcodeInfo::all, {Size16, 0xC1, slash_num, ib}, {r_m16, imm8 }, DU_U },\ +\ + {OpcodeInfo::decoder, {0xD1, slash_num}, {r_m32/*,const_1*/}, DU },\ + {OpcodeInfo::decoder64, {REX_W, 0xD1, slash_num}, {r_m64/*,const_1*/}, DU },\ +\ + {OpcodeInfo::all, {0xD3, slash_num}, {r_m32, CL}, DU_U },\ + {OpcodeInfo::em64t, {REX_W, 0xD3, slash_num}, {r_m64, CL}, DU_U },\ +\ + {OpcodeInfo::all, {0xC1, slash_num, ib}, {r_m32, imm8}, DU_U },\ + {OpcodeInfo::em64t, {REX_W, 0xC1, slash_num, ib}, {r_m64, imm8}, DU_U },\ +END_OPCODES()\ +END_MNEMONIC() + + +DEFINE_SHIFT_MNEMONIC(ROL, _0, MF_AFFECTS_FLAGS) +DEFINE_SHIFT_MNEMONIC(ROR, _1, MF_AFFECTS_FLAGS) +DEFINE_SHIFT_MNEMONIC(RCL, _2, MF_AFFECTS_FLAGS|MF_USES_FLAGS) +DEFINE_SHIFT_MNEMONIC(RCR, _3, MF_AFFECTS_FLAGS|MF_USES_FLAGS) + +DEFINE_SHIFT_MNEMONIC(SAL, _4, MF_AFFECTS_FLAGS) +DEFINE_SHIFT_MNEMONIC(SHR, _5, MF_AFFECTS_FLAGS) +DEFINE_SHIFT_MNEMONIC(SAR, _7, MF_AFFECTS_FLAGS) + +#undef DEFINE_SHIFT_MNEMONIC + +BEGIN_MNEMONIC(SHLD, MF_AFFECTS_FLAGS, N) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x0F, 0xA5}, {r_m32, r32, CL}, DU_DU_U }, + {OpcodeInfo::all, {0x0F, 0xA4}, {r_m32, r32, imm8}, DU_DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(SHRD, MF_AFFECTS_FLAGS, N) +// TODO: the def/use info is wrong +BEGIN_OPCODES() + {OpcodeInfo::all, {0x0F, 0xAD}, {r_m32, r32, CL}, DU_DU_U }, +END_OPCODES() +END_MNEMONIC() + + +BEGIN_MNEMONIC(SUBSD, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF2, 0x0F, 0x5C, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(SUBSS, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF3, 0x0F, 0x5C, _r}, {xmm32, xmm_m32}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(TEST, MF_AFFECTS_FLAGS, U_U) +BEGIN_OPCODES() + + {OpcodeInfo::decoder, {0xA8, ib}, { AL, imm8}, U_U }, + {OpcodeInfo::decoder, {0xA9, iw}, { AX, imm16}, U_U }, + {OpcodeInfo::decoder, {0xA9, id}, { EAX, imm32}, U_U }, + {OpcodeInfo::decoder64, {REX_W, 0xA9, id}, { RAX, imm32s}, U_U }, + + {OpcodeInfo::all, {0xF6, _0, ib}, {r_m8,imm8}, U_U }, + + {OpcodeInfo::all, {Size16, 0xF7, _0, iw}, {r_m16,imm16}, U_U }, + {OpcodeInfo::all, {0xF7, _0, id}, {r_m32,imm32}, U_U }, + {OpcodeInfo::em64t, {REX_W, 0xF7, _0, id}, {r_m64,imm32s}, U_U }, + + {OpcodeInfo::all, {0x84, _r}, {r_m8,r8}, U_U }, + + {OpcodeInfo::all, {Size16, 0x85, _r}, {r_m16,r16}, U_U }, + {OpcodeInfo::all, {0x85, _r}, {r_m32,r32}, U_U }, + {OpcodeInfo::em64t, {REX_W, 0x85, _r}, {r_m64,r64}, U_U }, +END_OPCODES() +END_MNEMONIC() + + +BEGIN_MNEMONIC(UCOMISD, MF_AFFECTS_FLAGS, U_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0x2E, _r}, {xmm64, xmm_m64}, U_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(UCOMISS, MF_AFFECTS_FLAGS, U_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x0F, 0x2E, _r}, {xmm32, xmm_m32}, U_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(COMISD, MF_AFFECTS_FLAGS, U_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0x2F, _r}, {xmm64, xmm_m64}, U_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(COMISS, MF_AFFECTS_FLAGS, U_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x0F, 0x2F, _r}, {xmm32, xmm_m32}, U_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(XORPD, MF_SAME_ARG_NO_USE|MF_SYMMETRIC, DU_U) +BEGIN_OPCODES() + //Note: they're actually 128 bits + {OpcodeInfo::all, {0x66, 0x0F, 0x57, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(XORPS, MF_SAME_ARG_NO_USE|MF_SYMMETRIC, DU_U) +BEGIN_OPCODES() + //Note: they're actually 128 bits + {OpcodeInfo::all, {0x0F, 0x57, _r}, {xmm32, xmm_m32}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(CVTDQ2PD, MF_NONE, D_U ) +BEGIN_OPCODES() + //Note: they're actually 128 bits + {OpcodeInfo::all, {0xF3, 0x0F, 0xE6}, {xmm64, xmm_m64}, D_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(CVTDQ2PS, MF_NONE, D_U ) +BEGIN_OPCODES() + //Note: they're actually 128 bits + {OpcodeInfo::all, {0x0F, 0x5B, _r}, {xmm32, xmm_m32}, D_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(CVTTPD2DQ, MF_NONE, D_U ) +BEGIN_OPCODES() + //Note: they're actually 128 bits + {OpcodeInfo::all, {0x66, 0x0F, 0xE6}, {xmm64, xmm_m64}, D_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(CVTTPS2DQ, MF_NONE, D_U ) +BEGIN_OPCODES() + //Note: they're actually 128 bits + {OpcodeInfo::all, {0xF3, 0x0F, 0x5B, _r}, {xmm32, xmm_m32}, D_U }, +END_OPCODES() +END_MNEMONIC() + +// +// String operations +// +BEGIN_MNEMONIC(STD, MF_AFFECTS_FLAGS, N) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xFD}, {}, N }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(CLD, MF_AFFECTS_FLAGS, N) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xFC}, {}, N }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(SCAS, MF_AFFECTS_FLAGS, N) +// to be symmetric, this mnemonic must have either m32 or RegName_EAX +// but as long, as Jitrino's CG does not use the mnemonic, leaving it +// in its natural form +BEGIN_OPCODES() + {OpcodeInfo::all, {0xAF}, {}, N }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(STOS, MF_AFFECTS_FLAGS, DU_DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xAB}, {EDI, ECX, EAX}, DU_DU_U }, + {OpcodeInfo::all, {0xAA}, {EDI, ECX, AL}, DU_DU_U }, + {OpcodeInfo::em64t, {REX_W, 0xAB}, {RDI, RCX, RAX}, DU_DU_U }, +END_OPCODES() +END_MNEMONIC() + +/* +MOVS and CMPS are the special cases. +Most the code in both CG and Encoder do not expect 2 memory operands. +Also, they are not supposed to setup constrains on which register the +memory reference must reside - m8,m8 or m32,m32 is not the choice. +We can't use r8,r8 either - will have problem with 8bit EDI, ESI. +So, as the workaround we do r32,r32 and specify size of the operand through +the specific mnemonic - the same is in the codegen. +*/ +BEGIN_MNEMONIC(MOVS8, MF_NONE, DU_DU_DU) +BEGIN_OPCODES() + {OpcodeInfo::ia32, {0xA4}, {r32,r32,ECX}, DU_DU_DU }, + {OpcodeInfo::em64t, {0xA4}, {r64,r64,RCX}, DU_DU_DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(MOVS16, MF_NONE, DU_DU_DU) +BEGIN_OPCODES() + {OpcodeInfo::ia32, {Size16, 0xA5}, {r32,r32,ECX}, DU_DU_DU }, + {OpcodeInfo::em64t, {Size16, 0xA5}, {r64,r64,RCX}, DU_DU_DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(MOVS32, MF_NONE, DU_DU_DU) +BEGIN_OPCODES() + {OpcodeInfo::ia32, {0xA5}, {r32,r32,ECX}, DU_DU_DU }, + {OpcodeInfo::em64t, {0xA5}, {r64,r64,RCX}, DU_DU_DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(MOVS64, MF_NONE, DU_DU_DU) +BEGIN_OPCODES() + {OpcodeInfo::em64t, {REX_W,0xA5}, {r64,r64,RCX}, DU_DU_DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(CMPSB, MF_AFFECTS_FLAGS, DU_DU_DU) +BEGIN_OPCODES() + {OpcodeInfo::ia32, {0xA6}, {ESI,EDI,ECX}, DU_DU_DU }, + {OpcodeInfo::em64t, {0xA6}, {RSI,RDI,RCX}, DU_DU_DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(CMPSW, MF_AFFECTS_FLAGS, DU_DU_DU) +BEGIN_OPCODES() + {OpcodeInfo::ia32, {Size16, 0xA7}, {ESI,EDI,ECX}, DU_DU_DU }, + {OpcodeInfo::em64t, {Size16, 0xA7}, {RSI,RDI,RCX}, DU_DU_DU }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(CMPSD, MF_AFFECTS_FLAGS, DU_DU_DU) +BEGIN_OPCODES() + {OpcodeInfo::ia32, {0xA7}, {ESI,EDI,ECX}, DU_DU_DU }, + {OpcodeInfo::em64t, {0xA7}, {RSI,RDI,RCX}, DU_DU_DU }, +END_OPCODES() +END_MNEMONIC() + + +BEGIN_MNEMONIC(WAIT, MF_AFFECTS_FLAGS, N) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x9B}, {}, N }, +END_OPCODES() +END_MNEMONIC() + +// +// ~String operations +// + +// +//Note: the instructions below added for the sake of disassembling routine. +// They need to have flags, params and params usage to be defined more precisely. +// +BEGIN_MNEMONIC(LEAVE, MF_NONE, N) +BEGIN_OPCODES() + {OpcodeInfo::decoder, {0xC9}, {}, N }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(ENTER, MF_NONE, N) +BEGIN_OPCODES() + {OpcodeInfo::decoder, {0xC8, iw, ib}, {imm16, imm8}, N }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PADDB, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0xFC, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PADDW, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0xFD, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PADDD, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0xFE, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PSUBB, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0xF8, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PSUBW, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0xF9, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PSUBD, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0xFA, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PMULLW, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0xD5, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PMULLD, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0x38, 0x40, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PSLLW, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0xF1, _r}, {xmm64, xmm_m64}, DU_U }, + {OpcodeInfo::all, {0x66, 0x0F, 0x71, _6, ib}, {xmm64, imm8}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PSLLD, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0xF2, _r}, {xmm64, xmm_m64}, DU_U }, + {OpcodeInfo::all, {0x66, 0x0F, 0x72, _6, ib}, {xmm64, imm8}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PSRAW, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0xE1, _r}, {xmm64, xmm_m64}, DU_U }, + {OpcodeInfo::all, {0x66, 0x0F, 0x71, _4, ib}, {xmm64, imm8}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PSRAD, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0xE2, _r}, {xmm64, xmm_m64}, DU_U }, + {OpcodeInfo::all, {0x66, 0x0F, 0x72, _4, ib}, {xmm64, imm8}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PSRLW, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0xD1, _r}, {xmm64, xmm_m64}, DU_U }, + {OpcodeInfo::all, {0x66, 0x0F, 0x71, _2, ib}, {xmm64, imm8}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PSRLD, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0xD2, _r}, {xmm64, xmm_m64}, DU_U }, + {OpcodeInfo::all, {0x66, 0x0F, 0x72, _2, ib}, {xmm64, imm8}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PMOVSXBW, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0x38, 0x20, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PSHUFB, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0x38, 0x00, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PSHUFD, MF_NONE, D_U_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0x70, _r, ib}, {xmm64, xmm_m64, imm8}, D_U_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PSHUFLW, MF_NONE, D_U_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF2, 0x0F, 0x70, _r, ib}, {xmm64, xmm_m64, imm8}, D_U_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PSHUFHW, MF_NONE, D_U_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0xF3, 0x0F, 0x70, _r, ib}, {xmm64, xmm_m64, imm8}, D_U_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PHADDSW, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0x38, 0x03, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PHADDW, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0x38, 0x01, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PHADDD, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0x38, 0x02, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PHSUBSW, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0x38, 0x07, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PHSUBW, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0x38, 0x05, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PHSUBD, MF_NONE, DU_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0x38, 0x06, _r}, {xmm64, xmm_m64}, DU_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PEXTRB, MF_NONE, D_U_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0x3A, 0x14, _r, ib}, {r32, xmm64, imm8}, D_U_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PEXTRW, MF_NONE, D_U_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0xC5, _r, ib}, {r32, xmm64, imm8}, D_U_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(PEXTRD, MF_NONE, D_U_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0x3A, 0x16, _r, ib}, {r_m32, xmm64, imm8}, D_U_U }, +END_OPCODES() +END_MNEMONIC() + +BEGIN_MNEMONIC(MOVDQA, MF_NONE|MF_SYMMETRIC, D_U) +BEGIN_OPCODES() + {OpcodeInfo::all, {0x66, 0x0F, 0x6F, _r}, {xmm64, xmm_m64}, D_U }, + //The encoder cannot properly look up when operands are symmetric but opcode is not: + //{OpcodeInfo::all, {0x66, 0x0F, 0x7F, _r}, {xmm_m128, xmm128}, D_U }, +END_OPCODES() +END_MNEMONIC() + +}; // ~masterEncodingTable[] + +ENCODER_NAMESPACE_END + +ENCODER_NAMESPACE_START + +static int compareMnemonicInfo(const void* info1, const void* info2) +{ + Mnemonic id1, id2; + + id1 = ((const MnemonicInfo*) info1)->mn; + id2 = ((const MnemonicInfo*) info2)->mn; + if (id1 < id2) + return -1; + if (id1 > id2) + return 1; + return 0; +} + +int EncoderBase::buildTable(void) +{ + // A check: all mnemonics must be covered + assert(COUNTOF(masterEncodingTable) == Mnemonic_Count); + + // sort out the mnemonics so the list become ordered + qsort(masterEncodingTable, Mnemonic_Count, sizeof(MnemonicInfo), compareMnemonicInfo); + + // + // clear the things + // + memset(opcodesHashMap, NOHASH, sizeof(opcodesHashMap)); + memset(opcodes, 0, sizeof(opcodes)); + // + // and, finally, build it + for (unsigned i=0; i<Mnemonic_Count; i++) { + assert((Mnemonic)i == (masterEncodingTable + i)->mn); + buildMnemonicDesc(masterEncodingTable+i); + } + return 0; +} + +void EncoderBase::buildMnemonicDesc(const MnemonicInfo * minfo) +{ + MnemonicDesc& mdesc = mnemonics[minfo->mn]; + mdesc.mn = minfo->mn; + mdesc.flags = minfo->flags; + mdesc.roles = minfo->roles; + mdesc.name = minfo->name; + + // + // fill the used opcodes + // + for (unsigned i=0, oindex=0; i<COUNTOF(minfo->opcodes); i++) { + + const OpcodeInfo& oinfo = minfo->opcodes[i]; + OpcodeDesc& odesc = opcodes[minfo->mn][oindex]; + // last opcode ? + if (oinfo.opcode[0] == OpcodeByteKind_LAST) { + // mark the opcode 'last', exit + odesc.opcode_len = 0; + odesc.last = 1; + break; + } + odesc.last = 0; +#ifdef _EM64T_ + if (oinfo.platf == OpcodeInfo::ia32) { continue; } + if (oinfo.platf == OpcodeInfo::decoder32) { continue; } +#else + if (oinfo.platf == OpcodeInfo::em64t) { continue; } + if (oinfo.platf == OpcodeInfo::decoder64) { continue; } +#endif + if (oinfo.platf == OpcodeInfo::decoder64 || + oinfo.platf == OpcodeInfo::decoder32) { + odesc.platf = OpcodeInfo::decoder; + } + else { + odesc.platf = (char)oinfo.platf; + } + // + // fill out opcodes + // + unsigned j = 0; + odesc.opcode_len = 0; + for(; oinfo.opcode[j]; j++) { + unsigned opcod = oinfo.opcode[j]; + unsigned kind = opcod&OpcodeByteKind_KindMask; + if (kind == OpcodeByteKind_REX_W) { + odesc.opcode[odesc.opcode_len++] = (unsigned char)0x48; + continue; + } + else if(kind != 0 && kind != OpcodeByteKind_ZeroOpcodeByte) { + break; + } + unsigned lowByte = (opcod & OpcodeByteKind_OpcodeMask); + odesc.opcode[odesc.opcode_len++] = (unsigned char)lowByte; + } + assert(odesc.opcode_len<5); + odesc.aux0 = odesc.aux1 = 0; + if (oinfo.opcode[j] != 0) { + odesc.aux0 = oinfo.opcode[j]; + assert((odesc.aux0 & OpcodeByteKind_KindMask) != 0); + ++j; + if(oinfo.opcode[j] != 0) { + odesc.aux1 = oinfo.opcode[j]; + assert((odesc.aux1 & OpcodeByteKind_KindMask) != 0); + } + } + else if (oinfo.roles.count>=2) { + if (((oinfo.opnds[0].kind&OpndKind_Mem) && + (isRegKind(oinfo.opnds[1].kind))) || + ((oinfo.opnds[1].kind&OpndKind_Mem) && + (isRegKind(oinfo.opnds[0].kind)))) { + // Example: MOVQ xmm1, xmm/m64 has only opcodes + // same with SHRD + // Adding fake /r + odesc.aux0 = _r; + } + } + else if (oinfo.roles.count==1) { + if (oinfo.opnds[0].kind&OpndKind_Mem) { + // Example: SETcc r/m8, adding fake /0 + odesc.aux0 = _0; + } + } + // check imm + if (oinfo.roles.count > 0 && + (oinfo.opnds[0].kind == OpndKind_Imm || + oinfo.opnds[oinfo.roles.count-1].kind == OpndKind_Imm)) { + // Example: CALL cd, PUSH imm32 - they fit both opnds[0] and + // opnds[oinfo.roles.count-1]. + // The A3 opcode fits only opnds[0] - it's currently have + // MOV imm32, EAX. Looks ridiculous, but this is how the + // moffset is currently implemented. Will need to fix together + // with other usages of moff. + // adding fake /cd or fake /id + unsigned imm_opnd_index = + oinfo.opnds[0].kind == OpndKind_Imm ? 0 : oinfo.roles.count-1; + OpndSize sz = oinfo.opnds[imm_opnd_index].size; + unsigned imm_encode, coff_encode; + if (sz==OpndSize_8) {imm_encode = ib; coff_encode=cb; } + else if (sz==OpndSize_16) {imm_encode = iw; coff_encode=cw;} + else if (sz==OpndSize_32) {imm_encode = id; coff_encode=cd; } + else if (sz==OpndSize_64) {imm_encode = io; coff_encode=0xCC; } + else { assert(false); imm_encode=0xCC; coff_encode=0xCC; } + if (odesc.aux1 == 0) { + if (odesc.aux0==0) { + odesc.aux0 = imm_encode; + } + else { + if (odesc.aux0 != imm_encode && odesc.aux0 != coff_encode) { + odesc.aux1 = imm_encode; + } + } + } + else { + assert(odesc.aux1==imm_encode); + } + + } + + assert(sizeof(odesc.opnds) == sizeof(oinfo.opnds)); + memcpy(odesc.opnds, oinfo.opnds, + sizeof(EncoderBase::OpndDesc) + * EncoderBase::MAX_NUM_OPCODE_OPERANDS); + odesc.roles = oinfo.roles; + odesc.first_opnd = 0; + if (odesc.opnds[0].reg != RegName_Null) { + ++odesc.first_opnd; + if (odesc.opnds[1].reg != RegName_Null) { + ++odesc.first_opnd; + } + } + + if (odesc.platf == OpcodeInfo::decoder) { + // if the opcode is only for decoding info, then do not hash it. + ++oindex; + continue; + } + + // + // check whether the operand info is a mask (i.e. r_m*). + // in this case, split the info to have separate entries for 'r' + // and for 'm'. + // the good news is that there can be only one such operand. + // + int opnd2split = -1; + for (unsigned k=0; k<oinfo.roles.count; k++) { + if ((oinfo.opnds[k].kind & OpndKind_Mem) && + (OpndKind_Mem != oinfo.opnds[k].kind)) { + opnd2split = k; + break; + } + }; + + if (opnd2split == -1) { + // not a mask, hash it, store it, continue. + unsigned short hash = getHash(&oinfo); + opcodesHashMap[minfo->mn][hash] = (unsigned char)oindex; + ++oindex; + continue; + }; + + OpcodeInfo storeItem = oinfo; + unsigned short hash; + + // remove the memory part of the mask, and store only 'r' part + storeItem.opnds[opnd2split].kind = (OpndKind)(storeItem.opnds[opnd2split].kind & ~OpndKind_Mem); + hash = getHash(&storeItem); + if (opcodesHashMap[minfo->mn][hash] == NOHASH) { + opcodesHashMap[minfo->mn][hash] = (unsigned char)oindex; + } + // else { + // do not overwrite if there is something there, just check that operands match + // the reason is that for some instructions there are several possibilities: + // say 'DEC r' may be encode as either '48+r' or 'FF /1', and I believe + // the first one is better for 'dec r'. + // as we're currently processing an opcode with memory part in operand, + // leave already filled items intact, so if there is 'OP reg' there, this + // better choice will be left in the table instead of 'OP r_m' + // } + + // compute hash of memory-based operand, 'm' part in 'r_m' + storeItem.opnds[opnd2split].kind = OpndKind_Mem; + hash = getHash(&storeItem); + // should not happen: for the r_m opcodes, there is a possibility + // that hash value of 'r' part intersects with 'OP r' value, but it's + // impossible for 'm' part. + assert(opcodesHashMap[minfo->mn][hash] == NOHASH); + opcodesHashMap[minfo->mn][hash] = (unsigned char)oindex; + + ++oindex; + } +} + +ENCODER_NAMESPACE_END diff --git a/libpixelflinger/codeflinger/x86/libenc/enc_wrapper.cpp b/libpixelflinger/codeflinger/x86/libenc/enc_wrapper.cpp new file mode 100644 index 0000000..b8abffe --- /dev/null +++ b/libpixelflinger/codeflinger/x86/libenc/enc_wrapper.cpp @@ -0,0 +1,836 @@ +/* + * Copyright (C) 2012 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 <stdio.h> +#include <assert.h> +#include <limits.h> +#include "enc_base.h" +#include "enc_wrapper.h" +#include "dec_base.h" +#include "utils/Log.h" + +//#define PRINT_ENCODER_STREAM +bool dump_x86_inst = false; + +/** + * @brief Provides mapping between PhysicalReg and RegName used by encoder + * @param physicalReg The physical register + * @return Returns encoder's register name + */ +static RegName mapFromPhysicalReg (int physicalReg) +{ + RegName reg = RegName_Null; + + //Get mapping between PhysicalReg and RegName + switch (physicalReg) + { + case PhysicalReg_EAX: + reg = RegName_EAX; + break; + case PhysicalReg_EBX: + reg = RegName_EBX; + break; + case PhysicalReg_ECX: + reg = RegName_ECX; + break; + case PhysicalReg_EDX: + reg = RegName_EDX; + break; + case PhysicalReg_EDI: + reg = RegName_EDI; + break; + case PhysicalReg_ESI: + reg = RegName_ESI; + break; + case PhysicalReg_ESP: + reg = RegName_ESP; + break; + case PhysicalReg_EBP: + reg = RegName_EBP; + break; + case PhysicalReg_XMM0: + reg = RegName_XMM0; + break; + case PhysicalReg_XMM1: + reg = RegName_XMM1; + break; + case PhysicalReg_XMM2: + reg = RegName_XMM2; + break; + case PhysicalReg_XMM3: + reg = RegName_XMM3; + break; + case PhysicalReg_XMM4: + reg = RegName_XMM4; + break; + case PhysicalReg_XMM5: + reg = RegName_XMM5; + break; + case PhysicalReg_XMM6: + reg = RegName_XMM6; + break; + case PhysicalReg_XMM7: + reg = RegName_XMM7; + break; + default: + //We have no mapping + reg = RegName_Null; + break; + } + + return reg; +} + +//getRegSize, getAliasReg: +//OpndSize, RegName, OpndExt: enum enc_defs.h +inline void add_r(EncoderBase::Operands & args, int physicalReg, OpndSize sz, OpndExt ext = OpndExt_None) { + if (sz == OpndSize_128) + { + //For xmm registers, the encoder table contains them as 64-bit operands. Since semantics are determined + //by the encoding of the mnemonic, we change the size to 64-bit to make encoder happy. It will still + //generate the code for 128-bit size since for 64-bit all instructions have different encoding to use mmx. + sz = OpndSize_64; + } + + RegName reg = mapFromPhysicalReg (physicalReg); + if (sz != getRegSize(reg)) { + reg = getAliasReg(reg, sz); + } + args.add(EncoderBase::Operand(reg, ext)); +} +inline void add_m(EncoderBase::Operands & args, int baseReg, int disp, OpndSize sz, OpndExt ext = OpndExt_None) { + if (sz == OpndSize_128) + { + //For xmm registers, the encoder table contains them as 64-bit operands. Since semantics are determined + //by the encoding of the mnemonic, we change the size to 64-bit to make encoder happy. It will still + //generate the code for 128-bit size since for 64-bit all instructions have different encoding to use mmx. + sz = OpndSize_64; + } + + args.add(EncoderBase::Operand(sz, + mapFromPhysicalReg (baseReg), + RegName_Null, 0, + disp, ext)); +} +inline void add_m_scale(EncoderBase::Operands & args, int baseReg, int indexReg, int scale, + OpndSize sz, OpndExt ext = OpndExt_None) { + if (sz == OpndSize_128) + { + //For xmm registers, the encoder table contains them as 64-bit operands. Since semantics are determined + //by the encoding of the mnemonic, we change the size to 64-bit to make encoder happy. It will still + //generate the code for 128-bit size since for 64-bit all instructions have different encoding to use mmx. + sz = OpndSize_64; + } + + args.add(EncoderBase::Operand(sz, + mapFromPhysicalReg (baseReg), + mapFromPhysicalReg (indexReg), scale, + 0, ext)); +} +inline void add_m_disp_scale(EncoderBase::Operands & args, int baseReg, int disp, int indexReg, int scale, + OpndSize sz, OpndExt ext = OpndExt_None) { + if (sz == OpndSize_128) + { + //For xmm registers, the encoder table contains them as 64-bit operands. Since semantics are determined + //by the encoding of the mnemonic, we change the size to 64-bit to make encoder happy. It will still + //generate the code for 128-bit size since for 64-bit all instructions have different encoding to use mmx. + sz = OpndSize_64; + } + + args.add(EncoderBase::Operand(sz, + mapFromPhysicalReg (baseReg), + mapFromPhysicalReg (indexReg), scale, + disp, ext)); +} + +inline void add_fp(EncoderBase::Operands & args, unsigned i, bool dbl) { + return args.add((RegName)( (dbl ? RegName_FP0D : RegName_FP0S) + i)); +} +inline void add_imm(EncoderBase::Operands & args, OpndSize sz, int value, bool is_signed) { + //assert(n_size != imm.get_size()); + args.add(EncoderBase::Operand(sz, value, + is_signed ? OpndExt_Signed : OpndExt_Zero)); +} + +#define MAX_DECODED_STRING_LEN 1024 +char tmpBuffer[MAX_DECODED_STRING_LEN]; + +void printOperand(const EncoderBase::Operand & opnd) { + unsigned int sz; + if(!dump_x86_inst) return; + sz = strlen(tmpBuffer); + if(opnd.size() != OpndSize_32) { + const char * opndSizeString = getOpndSizeString(opnd.size()); + + if (opndSizeString == NULL) { + // If the string that represents operand size is null it means that + // the operand size is an invalid value. Although this could be a + // problem if instruction is corrupted, technically failing to + // disassemble is not fatal. Thus, let's warn but proceed with using + // an empty string. + ALOGW("JIT-WARNING: Cannot decode instruction operand size."); + opndSizeString = ""; + } + + sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN - sz, "%s ", + opndSizeString); + } + if(opnd.is_mem()) { + if(opnd.scale() != 0) { + sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, + "%d(%s,%s,%d)", opnd.disp(), + getRegNameString(opnd.base()), + getRegNameString(opnd.index()), opnd.scale()); + } else { + sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, "%d(%s)", + opnd.disp(), getRegNameString(opnd.base())); + } + } + if(opnd.is_imm()) { + sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, "#%x", + (int)opnd.imm()); + } + if(opnd.is_reg()) { + sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, "%s", + getRegNameString(opnd.reg())); + } +} +//TODO: the order of operands +//to make the printout have the same order as assembly in .S +//I reverse the order here +void printDecoderInst(Inst & decInst) { + unsigned int sz; + if(!dump_x86_inst) return; + sz = strlen(tmpBuffer); + sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, "%s ", + EncoderBase::toStr(decInst.mn)); + for(unsigned int k = 0; k < decInst.argc; k++) { + if(k > 0) { + sz = strlen(tmpBuffer); + sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, ", "); + } + printOperand(decInst.operands[decInst.argc-1-k]); + } + ALOGE("%s", tmpBuffer); +} +void printOperands(EncoderBase::Operands& opnds) { + unsigned int sz; + if(!dump_x86_inst) return; + for(unsigned int k = 0; k < opnds.count(); k++) { + if(k > 0) { + sz = strlen(tmpBuffer); + sz += snprintf(&tmpBuffer[sz], MAX_DECODED_STRING_LEN-sz, ", "); + } + printOperand(opnds[opnds.count()-1-k]); + } +} +void printEncoderInst(Mnemonic m, EncoderBase::Operands& opnds) { + if(!dump_x86_inst) return; + snprintf(tmpBuffer, MAX_DECODED_STRING_LEN, "--- ENC %s ", + EncoderBase::toStr(m)); + printOperands(opnds); + ALOGE("%s", tmpBuffer); +} +int decodeThenPrint(char* stream_start) { + if(!dump_x86_inst) return 0; + snprintf(tmpBuffer, MAX_DECODED_STRING_LEN, "--- INST @ %p: ", + stream_start); + Inst decInst; + unsigned numBytes = DecoderBase::decode(stream_start, &decInst); + printDecoderInst(decInst); + return numBytes; +} + +extern "C" ENCODER_DECLARE_EXPORT char * encoder_imm(Mnemonic m, OpndSize size, int imm, char * stream) { + EncoderBase::Operands args; + //assert(imm.get_size() == size_32); + add_imm(args, size, imm, true/*is_signed*/); +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + stream = (char *)EncoderBase::encode(stream, m, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(m, args); + decodeThenPrint(stream_start); +#endif + return stream; +} +extern "C" ENCODER_DECLARE_EXPORT unsigned encoder_get_inst_size(char * stream) { + Inst decInst; + unsigned numBytes = DecoderBase::decode(stream, &decInst); + return numBytes; +} + +extern "C" ENCODER_DECLARE_EXPORT uintptr_t encoder_get_cur_operand_offset(int opnd_id) +{ + return (uintptr_t)EncoderBase::getOpndLocation(opnd_id); +} + +extern "C" ENCODER_DECLARE_EXPORT char * encoder_update_imm(int imm, char * stream) { + Inst decInst; + EncoderBase::Operands args; + + //Decode the instruction + DecoderBase::decode(stream, &decInst); + + add_imm(args, decInst.operands[0].size(), imm, true/*is_signed*/); + char* stream_next = (char *)EncoderBase::encode(stream, decInst.mn, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(decInst.mn, args); + decodeThenPrint(stream); +#endif + return stream_next; +} +extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem(Mnemonic m, OpndSize size, + int disp, int base_reg, bool isBasePhysical, char * stream) { + EncoderBase::Operands args; + add_m(args, base_reg, disp, size); +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + stream = (char *)EncoderBase::encode(stream, m, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(m, args); + decodeThenPrint(stream_start); +#endif + return stream; +} +extern "C" ENCODER_DECLARE_EXPORT char * encoder_reg(Mnemonic m, OpndSize size, + int reg, bool isPhysical, LowOpndRegType type, char * stream) { + EncoderBase::Operands args; + if(m == Mnemonic_DIV || m == Mnemonic_IDIV || m == Mnemonic_MUL || m == Mnemonic_IMUL) { + add_r(args, 0/*eax*/, size); + add_r(args, 3/*edx*/, size); + } + add_r(args, reg, size); +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + stream = (char *)EncoderBase::encode(stream, m, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(m, args); + decodeThenPrint(stream_start); +#endif + return stream; +} +//! \brief Allows for different operand sizes +extern "C" ENCODER_DECLARE_EXPORT char * encoder_imm_reg(Mnemonic m, OpndSize size, + int imm, int reg, bool isPhysical, LowOpndRegType type, char * stream) { + return encoder_imm_reg_diff_sizes(m, size, imm, size, reg, isPhysical, type, stream); +} +extern "C" ENCODER_DECLARE_EXPORT char * encoder_reg_reg_diff_sizes(Mnemonic m, OpndSize srcOpndSize, + int reg, bool isPhysical, OpndSize destOpndSize, + int reg2, bool isPhysical2, LowOpndRegType type, char * stream) { + if((m == Mnemonic_MOV || m == Mnemonic_MOVQ || m == Mnemonic_MOVD) && reg == reg2) return stream; + EncoderBase::Operands args; + add_r(args, reg2, destOpndSize); //destination + if(m == Mnemonic_SAL || m == Mnemonic_SHR || m == Mnemonic_SHL || m == Mnemonic_SAR) + add_r(args, reg, OpndSize_8); + else + add_r(args, reg, srcOpndSize); +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + stream = (char *)EncoderBase::encode(stream, m, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(m, args); + decodeThenPrint(stream_start); +#endif + return stream; +} +//both operands have same size +extern "C" ENCODER_DECLARE_EXPORT char * encoder_reg_reg(Mnemonic m, OpndSize size, + int reg, bool isPhysical, + int reg2, bool isPhysical2, LowOpndRegType type, char * stream) { + return encoder_reg_reg_diff_sizes(m, size, reg, isPhysical, size, reg2, isPhysical2, type, stream); +} +//! \brief Allows for different operand sizes +extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem_to_reg_diff_sizes(Mnemonic m, OpndSize memOpndSize, + int disp, int base_reg, bool isBasePhysical, OpndSize regOpndSize, + int reg, bool isPhysical, LowOpndRegType type, char * stream) { + EncoderBase::Operands args; + add_r(args, reg, regOpndSize); + add_m(args, base_reg, disp, memOpndSize); +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + stream = (char *)EncoderBase::encode(stream, m, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(m, args); + decodeThenPrint(stream_start); +#endif + return stream; +} +extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem_reg(Mnemonic m, OpndSize size, + int disp, int base_reg, bool isBasePhysical, + int reg, bool isPhysical, LowOpndRegType type, char * stream) { + return encoder_mem_to_reg_diff_sizes(m, size, disp, base_reg, isBasePhysical, size, reg, isPhysical, type, stream); +} +extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem_scale_reg(Mnemonic m, OpndSize size, + int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale, + int reg, bool isPhysical, LowOpndRegType type, char * stream) { + EncoderBase::Operands args; + add_r(args, reg, size); + add_m_scale(args, base_reg, index_reg, scale, size); +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + stream = (char *)EncoderBase::encode(stream, m, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(m, args); + decodeThenPrint(stream_start); +#endif + return stream; +} +extern "C" ENCODER_DECLARE_EXPORT char * encoder_reg_mem_scale(Mnemonic m, OpndSize size, + int reg, bool isPhysical, + int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale, + LowOpndRegType type, char * stream) { + EncoderBase::Operands args; + add_m_scale(args, base_reg, index_reg, scale, size); + add_r(args, reg, size); +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + stream = (char *)EncoderBase::encode(stream, m, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(m, args); + decodeThenPrint(stream_start); +#endif + return stream; +} +//! \brief Allows for different operand sizes +extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem_disp_scale_to_reg_diff_sizes(Mnemonic m, OpndSize memOpndSize, + int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale, + OpndSize regOpndSize, int reg, bool isPhysical, LowOpndRegType type, char * stream) { + EncoderBase::Operands args; + add_r(args, reg, regOpndSize); + add_m_disp_scale(args, base_reg, disp, index_reg, scale, memOpndSize); +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + stream = (char *)EncoderBase::encode(stream, m, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(m, args); + decodeThenPrint(stream_start); +#endif + return stream; +} +extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem_disp_scale_reg(Mnemonic m, OpndSize size, + int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale, + int reg, bool isPhysical, LowOpndRegType type, char * stream) { + return encoder_mem_disp_scale_to_reg_diff_sizes(m, size, base_reg, isBasePhysical, + disp, index_reg, isIndexPhysical, scale, size, reg, isPhysical, + type, stream); +} +extern "C" ENCODER_DECLARE_EXPORT char * encoder_movzs_mem_disp_scale_reg(Mnemonic m, OpndSize size, + int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale, + int reg, bool isPhysical, LowOpndRegType type, char * stream) { + EncoderBase::Operands args; + add_r(args, reg, OpndSize_32); + add_m_disp_scale(args, base_reg, disp, index_reg, scale, size); +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + stream = (char *)EncoderBase::encode(stream, m, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(m, args); + decodeThenPrint(stream_start); +#endif + return stream; +} +extern "C" ENCODER_DECLARE_EXPORT char* encoder_reg_mem_disp_scale(Mnemonic m, OpndSize size, + int reg, bool isPhysical, + int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale, + LowOpndRegType type, char* stream) { + EncoderBase::Operands args; + add_m_disp_scale(args, base_reg, disp, index_reg, scale, size); + add_r(args, reg, size); +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + stream = (char *)EncoderBase::encode(stream, m, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(m, args); + decodeThenPrint(stream_start); +#endif + return stream; +} + +extern "C" ENCODER_DECLARE_EXPORT char * encoder_reg_mem(Mnemonic m, OpndSize size, + int reg, bool isPhysical, + int disp, int base_reg, bool isBasePhysical, LowOpndRegType type, char * stream) { + EncoderBase::Operands args; + add_m(args, base_reg, disp, size); + add_r(args, reg, size); +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + if (m == Mnemonic_CMPXCHG ){ + //CMPXCHG require EAX as args + add_r(args,PhysicalReg_EAX,size); + //Add lock prefix for CMPXCHG, guarantee the atomic of CMPXCHG in multi-core platform + stream = (char *)EncoderBase::prefix(stream, InstPrefix_LOCK); + } + stream = (char *)EncoderBase::encode(stream, m, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(m, args); + decodeThenPrint(stream_start); +#endif + return stream; +} +extern "C" ENCODER_DECLARE_EXPORT char * encoder_imm_reg_diff_sizes (Mnemonic m, OpndSize sizeImm, int imm, + OpndSize sizeReg, int reg, bool isPhysical, LowOpndRegType type, char * stream) +{ + //Create the operands + EncoderBase::Operands args; + //Add destination register + add_r (args, reg, sizeReg); + //For imul, we need to add implicit register explicitly + if (m == Mnemonic_IMUL) + { + add_r (args, reg, sizeReg); + } + //Finally add the immediate + add_imm (args, sizeImm, imm, true/*is_signed*/); + +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + + //Now do the encoding + stream = EncoderBase::encode (stream, m, args); + +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(m, args); + decodeThenPrint(stream_start); +#endif + + return stream; +} +extern "C" ENCODER_DECLARE_EXPORT char * encoder_update_imm_rm(int imm, char * stream) { + Inst decInst; + EncoderBase::Operands args; + + //Decode the instruction + DecoderBase::decode(stream, &decInst); + + args.add(decInst.operands[0]); + add_imm(args, decInst.operands[1].size(), imm, true/*is_signed*/); + char* stream_next = (char *)EncoderBase::encode(stream, decInst.mn, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(decInst.mn, args); + decodeThenPrint(stream); +#endif + return stream_next; +} + +extern "C" ENCODER_DECLARE_EXPORT char * encoder_imm_mem(Mnemonic m, OpndSize size, + int imm, + int disp, int base_reg, bool isBasePhysical, char * stream) { + return encoder_imm_mem_diff_sizes(m, size, imm, size, disp, base_reg, isBasePhysical, stream); +} + +extern "C" ENCODER_DECLARE_EXPORT char * encoder_imm_mem_diff_sizes (Mnemonic m, OpndSize immOpndSize, int imm, + OpndSize memOpndSize, int disp, int baseRegister, bool isBasePhysical, char * stream) +{ + //Add operands + EncoderBase::Operands args; + add_m (args, baseRegister, disp, memOpndSize); + add_imm (args, immOpndSize, imm, true); + +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + + //Do the encoding + stream = EncoderBase::encode (stream, m, args); + +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(m, args); + decodeThenPrint(stream_start); +#endif + + return stream; +} + +extern "C" ENCODER_DECLARE_EXPORT char * encoder_fp_mem(Mnemonic m, OpndSize size, int reg, + int disp, int base_reg, bool isBasePhysical, char * stream) { + EncoderBase::Operands args; + add_m(args, base_reg, disp, size); + // a fake FP register as operand + add_fp(args, reg, size == OpndSize_64/*is_double*/); +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + stream = (char *)EncoderBase::encode(stream, m, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(m, args); + decodeThenPrint(stream_start); +#endif + return stream; +} +extern "C" ENCODER_DECLARE_EXPORT char * encoder_mem_fp(Mnemonic m, OpndSize size, + int disp, int base_reg, bool isBasePhysical, + int reg, char * stream) { + EncoderBase::Operands args; + // a fake FP register as operand + add_fp(args, reg, size == OpndSize_64/*is_double*/); + add_m(args, base_reg, disp, size); +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + stream = (char *)EncoderBase::encode(stream, m, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(m, args); + decodeThenPrint(stream_start); +#endif + return stream; +} + +extern "C" ENCODER_DECLARE_EXPORT char * encoder_return(char * stream) { + EncoderBase::Operands args; +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + stream = (char *)EncoderBase::encode(stream, Mnemonic_RET, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(Mnemonic_RET, args); + decodeThenPrint(stream_start); +#endif + return stream; +} +extern "C" ENCODER_DECLARE_EXPORT char * encoder_compare_fp_stack(bool pop, int reg, bool isDouble, char * stream) { + Mnemonic m = pop ? Mnemonic_FUCOMIP : Mnemonic_FUCOMI; + //a single operand or 2 operands? + //FST ST(i) has a single operand in encoder.inl? + EncoderBase::Operands args; + add_fp(args, reg, isDouble); +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + stream = (char *)EncoderBase::encode(stream, m, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(m, args); + decodeThenPrint(stream_start); +#endif + return stream; +} +extern "C" ENCODER_DECLARE_EXPORT char * encoder_movez_mem_to_reg(OpndSize size, + int disp, int base_reg, bool isBasePhysical, + int reg, bool isPhysical, char * stream) { + EncoderBase::Operands args; + add_r(args, reg, OpndSize_32); + add_m(args, base_reg, disp, size); +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + stream = (char *)EncoderBase::encode(stream, Mnemonic_MOVZX, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(Mnemonic_MOVZX, args); + decodeThenPrint(stream_start); +#endif + return stream; +} +extern "C" ENCODER_DECLARE_EXPORT char * encoder_moves_mem_to_reg(OpndSize size, + int disp, int base_reg, bool isBasePhysical, + int reg, bool isPhysical, char * stream) { + EncoderBase::Operands args; + add_r(args, reg, OpndSize_32); + add_m(args, base_reg, disp, size); +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + stream = (char *)EncoderBase::encode(stream, Mnemonic_MOVSX, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(Mnemonic_MOVSX, args); + decodeThenPrint(stream_start); +#endif + return stream; +} +extern "C" ENCODER_DECLARE_EXPORT char * encoder_movez_reg_to_reg(OpndSize size, + int reg, bool isPhysical, int reg2, + bool isPhysical2, LowOpndRegType type, char * stream) { + EncoderBase::Operands args; + add_r(args, reg2, OpndSize_32); //destination + add_r(args, reg, size); +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + stream = (char *)EncoderBase::encode(stream, Mnemonic_MOVZX, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(Mnemonic_MOVZX, args); + decodeThenPrint(stream_start); +#endif + return stream; +} +extern "C" ENCODER_DECLARE_EXPORT char * encoder_moves_reg_to_reg(OpndSize size, + int reg, bool isPhysical,int reg2, + bool isPhysical2, LowOpndRegType type, char * stream) { + EncoderBase::Operands args; + add_r(args, reg2, OpndSize_32); //destination + add_r(args, reg, size); +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + stream = (char *)EncoderBase::encode(stream, Mnemonic_MOVSX, args); +#ifdef PRINT_ENCODER_STREAM + printEncoderInst(Mnemonic_MOVSX, args); + decodeThenPrint(stream_start); +#endif + return stream; +} + +extern "C" ENCODER_DECLARE_EXPORT char * encoder_imm_reg_reg (Mnemonic m, int imm, OpndSize immediateSize, + int sourceReg, OpndSize sourceRegSize, int destReg, OpndSize destRegSize, char * stream) +{ + EncoderBase::Operands args; + + //Add the source and destination registers + add_r (args, destReg, destRegSize); + add_r (args, sourceReg, sourceRegSize); + + //Now add the immediate. We expect in three operand situation that immediate is last argument + add_imm (args, immediateSize, imm, true/*is_signed*/); + +#ifdef PRINT_ENCODER_STREAM + char* stream_start = stream; +#endif + + //Do the actual encoding + stream = EncoderBase::encode (stream, m, args); + +#ifdef PRINT_ENCODER_STREAM + printEncoderInst (m, args); + decodeThenPrint (stream_start); +#endif + + //Return the updated stream pointer + return stream; +} + +/** + * @brief Generates variable sized nop instructions. + * @param numBytes Number of bytes for the nop instruction. If this value is + * larger than 9 bytes, more than one nop instruction will be generated. + * @param stream Instruction stream where to place the nops + * @return Updated instruction stream pointer after generating the nops + */ +extern "C" ENCODER_DECLARE_EXPORT char * encoder_nops(unsigned numBytes, char * stream) { + return EncoderBase::nops(stream, numBytes); +} + +// Disassemble the operand "opnd" and put the readable format in "strbuf" +// up to a string length of "len". +unsigned int DisassembleOperandToBuf(const EncoderBase::Operand& opnd, char* strbuf, unsigned int len) +{ + unsigned int sz = 0; + if(opnd.size() != OpndSize_32) { + const char * opndSizeString = getOpndSizeString(opnd.size()); + + if (opndSizeString == NULL) { + // If the string that represents operand size is null it means that + // the operand size is an invalid value. Although this could be a + // problem if instruction is corrupted, technically failing to + // disassemble is not fatal. Thus, let's warn but proceed with using + // an empty string. + ALOGW("JIT-WARNING: Cannot decode instruction operand size."); + opndSizeString = ""; + } + + sz += snprintf(&strbuf[sz], len-sz, "%s ", opndSizeString); + } + if(opnd.is_mem()) { + if(opnd.scale() != 0) { + sz += snprintf(&strbuf[sz], len-sz, "%d(%s,%s,%d)", opnd.disp(), + getRegNameString(opnd.base()), + getRegNameString(opnd.index()), opnd.scale()); + } else { + sz += snprintf(&strbuf[sz], len-sz, "%d(%s)", + opnd.disp(), getRegNameString(opnd.base())); + } + } else if(opnd.is_imm()) { + sz += snprintf(&strbuf[sz], len-sz, "#%x", (int)opnd.imm()); + } else if(opnd.is_reg()) { + sz += snprintf(&strbuf[sz], len-sz, "%s", + getRegNameString(opnd.reg())); + } + return sz; +} + +// Disassemble the instruction "decInst" and put the readable format +// in "strbuf" up to a string length of "len". +void DisassembleInstToBuf(Inst& decInst, char* strbuf, unsigned int len) +{ + unsigned int sz = 0; + int k; + sz += snprintf(&strbuf[sz], len-sz, "%s ", EncoderBase::toStr(decInst.mn)); + if (decInst.argc > 0) { + sz += DisassembleOperandToBuf(decInst.operands[decInst.argc-1], + &strbuf[sz], len-sz); + for(k = decInst.argc-2; k >= 0; k--) { + sz += snprintf(&strbuf[sz], len-sz, ", "); + sz += DisassembleOperandToBuf(decInst.operands[k], &strbuf[sz], len-sz); + } + } +} + +// Disassmble the x86 instruction pointed to by code pointer "stream." +// Put the disassemble text in the "strbuf" up to string length "len". +// Return the code pointer after the disassemble x86 instruction. +extern "C" ENCODER_DECLARE_EXPORT +char* decoder_disassemble_instr(char* stream, char* strbuf, unsigned int len) +{ + Inst decInst; + unsigned numBytes = DecoderBase::decode(stream, &decInst); + DisassembleInstToBuf(decInst, strbuf, len); + return (stream + numBytes); +} + +/** + * @brief Physical register char* counterparts + */ +static const char * PhysicalRegString[] = { "eax", "ebx", "ecx", "edx", "edi", + "esi", "esp", "ebp", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", + "xmm6", "xmm7", "st0", "st1", "st2", "st3", "st4", "st5", "st6", "st7", + "null" + }; + +/** + * @brief Scratch register char* counterparts + */ +static const char * ScratchRegString[] = { "scratch1", "scratch2", "scratch3", + "scratch4", "scratch5", "scratch6", "scratch7", "scratch8", "scratch9", + "scratch10" }; + +extern "C" ENCODER_DECLARE_EXPORT +/** + * @brief Transform a physical register into its char* counterpart + * @param reg the PhysicalReg we want to have a char* equivalent + * @return the register reg in char* form + */ +const char * physicalRegToString(PhysicalReg reg) +{ + if (reg < PhysicalReg_Null) { + return PhysicalRegString[reg]; + } else if (reg >= PhysicalReg_SCRATCH_1 && reg <= PhysicalReg_SCRATCH_10) { + return ScratchRegString[reg - PhysicalReg_SCRATCH_1]; + } else if (reg == PhysicalReg_Null) { + return "null"; + } else { + return "corrupted-data"; + } +} diff --git a/libpixelflinger/codeflinger/x86/libenc/enc_wrapper.h b/libpixelflinger/codeflinger/x86/libenc/enc_wrapper.h new file mode 100644 index 0000000..3d2e68d --- /dev/null +++ b/libpixelflinger/codeflinger/x86/libenc/enc_wrapper.h @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2012 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 _VM_ENC_WRAPPER_H_ +#define _VM_ENC_WRAPPER_H_ + +#include "enc_defs_ext.h" + +extern bool dump_x86_inst; +typedef enum PhysicalReg { + // Currently initializing StartOfGPMarker to be 0 in order to match + // register index in Reg_No. However, ideally PhysicalReg_Null should + // be 0 and the rest moved over. + PhysicalReg_StartOfGPMarker = 0, + PhysicalReg_EAX = PhysicalReg_StartOfGPMarker, + PhysicalReg_EBX, PhysicalReg_ECX, PhysicalReg_EDX, + PhysicalReg_EDI, PhysicalReg_ESI, PhysicalReg_ESP, PhysicalReg_EBP, + PhysicalReg_EndOfGPMarker = PhysicalReg_EBP, + + PhysicalReg_StartOfXmmMarker, + PhysicalReg_XMM0 = PhysicalReg_StartOfXmmMarker, + PhysicalReg_XMM1, PhysicalReg_XMM2, PhysicalReg_XMM3, + PhysicalReg_XMM4, PhysicalReg_XMM5, PhysicalReg_XMM6, PhysicalReg_XMM7, + PhysicalReg_EndOfXmmMarker = PhysicalReg_XMM7, + + PhysicalReg_StartOfX87Marker, + PhysicalReg_ST0 = PhysicalReg_StartOfX87Marker, PhysicalReg_ST1, + PhysicalReg_ST2, PhysicalReg_ST3, PhysicalReg_ST4, PhysicalReg_ST5, + PhysicalReg_ST6, PhysicalReg_ST7, + PhysicalReg_EndOfX87Marker = PhysicalReg_ST7, + + PhysicalReg_Null, + //used as scratch logical register in NCG O1 + //should not overlap with regular logical register, start from 100 + PhysicalReg_SCRATCH_1 = 100, PhysicalReg_SCRATCH_2, PhysicalReg_SCRATCH_3, PhysicalReg_SCRATCH_4, + PhysicalReg_SCRATCH_5, PhysicalReg_SCRATCH_6, PhysicalReg_SCRATCH_7, PhysicalReg_SCRATCH_8, + PhysicalReg_SCRATCH_9, PhysicalReg_SCRATCH_10, + + //This should be the last entry + PhysicalReg_Last = PhysicalReg_SCRATCH_10 +} PhysicalReg; + +typedef enum Reg_No { +#ifdef _EM64T_ + rax_reg = 0,rbx_reg, rcx_reg, rdx_reg, + rdi_reg, rsi_reg, rsp_reg, rbp_reg, + r8_reg, r9_reg, r10_reg, r11_reg, + r12_reg, r13_reg, r14_reg, r15_reg, + xmm0_reg, xmm1_reg, xmm2_reg, xmm3_reg, + xmm4_reg, xmm5_reg, xmm6_reg, xmm7_reg, + xmm8_reg, xmm9_reg, xmm10_reg, xmm11_reg, + xmm12_reg, xmm13_reg, xmm14_reg, xmm15_reg, + +#else // !defined(_EM64T_) + + eax_reg = 0,ebx_reg, ecx_reg, edx_reg, + edi_reg, esi_reg, esp_reg, ebp_reg, + xmm0_reg, xmm1_reg, xmm2_reg, xmm3_reg, + xmm4_reg, xmm5_reg, xmm6_reg, xmm7_reg, + fs_reg, +#endif + /** @brief Total number of registers.*/ + n_reg +} Reg_No; +// +// instruction operand sizes: 8,16,32,64 bits +// +typedef enum Opnd_Size { + size_8 = 0, + size_16, + size_32, + size_64, + n_size, +#ifdef _EM64T_ + size_platf = size_64 +#else + size_platf = size_32 +#endif +} Opnd_Size; + +// +// opcodes for alu instructions +// +typedef enum ALU_Opcode { + add_opc = 0,or_opc, adc_opc, sbb_opc, + and_opc, sub_opc, xor_opc, cmp_opc, + mul_opc, imul_opc, div_opc, idiv_opc, + sll_opc, srl_opc, sra_opc, //shift right arithmetic + shl_opc, shr_opc, + sal_opc, sar_opc, + neg_opc, not_opc, andn_opc, + n_alu +} ALU_Opcode; + +typedef enum ConditionCode { + Condition_O = 0, + Condition_NO = 1, + Condition_B = 2, + Condition_NAE = Condition_B, + Condition_C = Condition_B, + Condition_NB = 3, + Condition_AE = Condition_NB, + Condition_NC = Condition_NB, + Condition_Z = 4, + Condition_E = Condition_Z, + Condition_NZ = 5, + Condition_NE = Condition_NZ, + Condition_BE = 6, + Condition_NA = Condition_BE, + Condition_NBE = 7, + Condition_A = Condition_NBE, + + Condition_S = 8, + Condition_NS = 9, + Condition_P = 10, + Condition_PE = Condition_P, + Condition_NP = 11, + Condition_PO = Condition_NP, + Condition_L = 12, + Condition_NGE = Condition_L, + Condition_NL = 13, + Condition_GE = Condition_NL, + Condition_LE = 14, + Condition_NG = Condition_LE, + Condition_NLE = 15, + Condition_G = Condition_NLE, + Condition_Count = 16 +} ConditionCode; + +// +// prefix code +// +typedef enum InstrPrefix { + no_prefix, + lock_prefix = 0xF0, + hint_branch_taken_prefix = 0x2E, + hint_branch_not_taken_prefix = 0x3E, + prefix_repne = 0xF2, + prefix_repnz = prefix_repne, + prefix_repe = 0xF3, + prefix_repz = prefix_repe, + prefix_rep = 0xF3, + prefix_cs = 0x2E, + prefix_ss = 0x36, + prefix_ds = 0x3E, + prefix_es = 0x26, + prefix_fs = 0x64, + prefix_gs = 0x65 +} InstrPrefix; + +enum LowOpndRegType +{ + LowOpndRegType_gp = 0, + LowOpndRegType_fs = 1, + LowOpndRegType_xmm = 2, + LowOpndRegType_fs_s = 3, + LowOpndRegType_ss = 4, + LowOpndRegType_invalid = 256, +}; + +enum LogicalRegType +{ + LogicalType_invalid = 0, + LowOpndRegType_scratch = 8, + LowOpndRegType_temp = 16, + LowOpndRegType_hard = 32, + LowOpndRegType_virtual = 64, +}; + +//if inline, separte enc_wrapper.cpp into two files, one of them is .inl +// enc_wrapper.cpp needs to handle both cases +#ifdef ENCODER_INLINE + #define ENCODER_DECLARE_EXPORT inline + #include "enc_wrapper.inl" +#else + #define ENCODER_DECLARE_EXPORT +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif +ENCODER_DECLARE_EXPORT char* encoder_imm(Mnemonic m, OpndSize size, + int imm, char* stream); +ENCODER_DECLARE_EXPORT unsigned encoder_get_inst_size(char * stream); +ENCODER_DECLARE_EXPORT char* encoder_update_imm(int imm, char * stream); +ENCODER_DECLARE_EXPORT char* encoder_mem(Mnemonic m, OpndSize size, + int disp, int base_reg, bool isBasePhysical, char* stream); +ENCODER_DECLARE_EXPORT char* encoder_reg(Mnemonic m, OpndSize size, + int reg, bool isPhysical, LowOpndRegType type, char* stream); +ENCODER_DECLARE_EXPORT char* encoder_reg_reg(Mnemonic m, OpndSize size, + int reg, bool isPhysical, + int reg2, bool isPhysical2, LowOpndRegType type, char* stream); +ENCODER_DECLARE_EXPORT char* encoder_reg_reg_diff_sizes(Mnemonic m, OpndSize srcOpndSize, + int reg, bool isPhysical, OpndSize destOpndSize, + int reg2, bool isPhysical2, LowOpndRegType type, char* stream); +ENCODER_DECLARE_EXPORT char* encoder_mem_reg(Mnemonic m, OpndSize size, + int disp, int base_reg, bool isBasePhysical, + int reg, bool isPhysical, LowOpndRegType type, char* stream); +ENCODER_DECLARE_EXPORT char* encoder_mem_to_reg_diff_sizes(Mnemonic m, OpndSize memOpndSize, + int disp, int base_reg, bool isBasePhysical, OpndSize regOpndSize, + int reg, bool isPhysical, LowOpndRegType type, char* stream); +ENCODER_DECLARE_EXPORT char* encoder_mem_scale_reg(Mnemonic m, OpndSize size, + int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale, + int reg, bool isPhysical, LowOpndRegType type, char* stream); +ENCODER_DECLARE_EXPORT char* encoder_reg_mem_scale(Mnemonic m, OpndSize size, + int reg, bool isPhysical, + int base_reg, bool isBasePhysical, int index_reg, bool isIndexPhysical, int scale, + LowOpndRegType type, char* stream); +ENCODER_DECLARE_EXPORT char * encoder_mem_disp_scale_reg(Mnemonic m, OpndSize size, + int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale, + int reg, bool isPhysical, LowOpndRegType type, char * stream); +ENCODER_DECLARE_EXPORT char * encoder_mem_disp_scale_to_reg_diff_sizes(Mnemonic m, OpndSize memOpndSize, + int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale, + OpndSize regOpndSize, int reg, bool isPhysical, LowOpndRegType type, char * stream); +ENCODER_DECLARE_EXPORT char * encoder_movzs_mem_disp_scale_reg(Mnemonic m, OpndSize size, + int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale, + int reg, bool isPhysical, LowOpndRegType type, char * stream); +ENCODER_DECLARE_EXPORT char * encoder_mem_disp_scale_to_reg_2(Mnemonic m, OpndSize memOpndSize, + int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale, + OpndSize regOpndSize, int reg, bool isPhysical, LowOpndRegType type, char * stream); +ENCODER_DECLARE_EXPORT char* encoder_reg_mem_disp_scale(Mnemonic m, OpndSize size, + int reg, bool isPhysical, + int base_reg, bool isBasePhysical, int disp, int index_reg, bool isIndexPhysical, int scale, + LowOpndRegType type, char* stream); +ENCODER_DECLARE_EXPORT char* encoder_reg_mem(Mnemonic m, OpndSize size, + int reg, bool isPhysical, + int disp, int base_reg, bool isBasePhysical, LowOpndRegType type, char* stream); +ENCODER_DECLARE_EXPORT char* encoder_imm_reg(Mnemonic m, OpndSize size, + int imm, int reg, bool isPhysical, LowOpndRegType type, char* stream); +ENCODER_DECLARE_EXPORT char * encoder_imm_reg_diff_sizes(Mnemonic m, OpndSize sizeImm, + int imm, OpndSize sizeReg, int reg, bool isPhysical, LowOpndRegType type, char * stream); +ENCODER_DECLARE_EXPORT char * encoder_update_imm_rm(int imm, char * stream); +ENCODER_DECLARE_EXPORT char* encoder_imm_mem(Mnemonic m, OpndSize size, + int imm, + int disp, int base_reg, bool isBasePhysical, char* stream); +ENCODER_DECLARE_EXPORT char * encoder_imm_mem_diff_sizes (Mnemonic m, OpndSize immOpndSize, int imm, + OpndSize memOpndSize, int disp, int baseRegister, bool isBasePhysical, char * stream); +ENCODER_DECLARE_EXPORT char* encoder_fp_mem(Mnemonic m, OpndSize size, int reg, + int disp, int base_reg, bool isBasePhysical, char* stream); +ENCODER_DECLARE_EXPORT char* encoder_mem_fp(Mnemonic m, OpndSize size, + int disp, int base_reg, bool isBasePhysical, + int reg, char* stream); +ENCODER_DECLARE_EXPORT char* encoder_return(char* stream); +ENCODER_DECLARE_EXPORT char* encoder_compare_fp_stack(bool pop, int reg, bool isDouble, char* stream); +ENCODER_DECLARE_EXPORT char* encoder_movez_mem_to_reg(OpndSize size, + int disp, int base_reg, bool isBasePhysical, + int reg, bool isPhysical, char* stream); +ENCODER_DECLARE_EXPORT char* encoder_moves_mem_to_reg(OpndSize size, + int disp, int base_reg, bool isBasePhysical, + int reg, bool isPhysical, char* stream); +ENCODER_DECLARE_EXPORT char * encoder_movez_reg_to_reg(OpndSize size, + int reg, bool isPhysical, int reg2, + bool isPhysical2, LowOpndRegType type, char * stream); +ENCODER_DECLARE_EXPORT char * encoder_moves_reg_to_reg(OpndSize size, + int reg, bool isPhysical, int reg2, + bool isPhysical2, LowOpndRegType type, char * stream); +ENCODER_DECLARE_EXPORT char * encoder_imm_reg_reg (Mnemonic m, int imm, OpndSize immediateSize, + int sourceReg, OpndSize sourceRegSize, int destReg, + OpndSize destRegSize, char * stream); +ENCODER_DECLARE_EXPORT char * encoder_nops(unsigned numBytes, char * stream); +ENCODER_DECLARE_EXPORT int decodeThenPrint(char* stream_start); +ENCODER_DECLARE_EXPORT char* decoder_disassemble_instr(char* stream, char* strbuf, unsigned int len); + +//Provide a char* equivalent to a PhysicalReg type +ENCODER_DECLARE_EXPORT const char * physicalRegToString(PhysicalReg reg); +#ifdef __cplusplus +} +#endif +#endif // _VM_ENC_WRAPPER_H_ diff --git a/libpixelflinger/codeflinger/x86/libenc/encoder.h b/libpixelflinger/codeflinger/x86/libenc/encoder.h new file mode 100644 index 0000000..9ac0219 --- /dev/null +++ b/libpixelflinger/codeflinger/x86/libenc/encoder.h @@ -0,0 +1,717 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexander V. Astapchuk + */ +/** + * @file + * @brief Simple interface for generating processor instructions. + * + * The interface works for both IA32 and EM64T. By default, only IA32 + * capabilities are presented. To enable EM64T feature, the _EM64T_ macro + * must be defined (and, of course, a proper library version to be used). + * + * The interface is based on the original ia32.h encoder interface, + * with some simplifications and add-ons - EM64T-specific, SSE and SSE2. + * + * The interface mostly intended for existing legacy code like LIL code + * generator. From the implementation point of view, it's just a wrapper + * around the EncoderBase functionality. + */ + +#ifndef _VM_ENCODER_H_ +#define _VM_ENCODER_H_ + +#include <limits.h> +#include "enc_base.h" +//#include "open/types.h" + +#ifdef _EM64T_ +// size of general-purpose value on the stack in bytes +#define GR_STACK_SIZE 8 +// size of floating-point value on the stack in bytes +#define FR_STACK_SIZE 8 + +#if defined(WIN32) || defined(_WIN64) + // maximum number of GP registers for inputs + const int MAX_GR = 4; + // maximum number of FP registers for inputs + const int MAX_FR = 4; + // WIN64 reserves 4 words for shadow space + const int SHADOW = 4 * GR_STACK_SIZE; +#else + // maximum number of GP registers for inputs + const int MAX_GR = 6; + // maximum number of FP registers for inputs + const int MAX_FR = 8; + // Linux x64 doesn't reserve shadow space + const int SHADOW = 0; +#endif + +#else +// size of general-purpose value on the stack in bytes +#define GR_STACK_SIZE 4 +// size of general-purpose value on the stack in bytes +#define FR_STACK_SIZE 8 + +// maximum number of GP registers for inputs +const int MAX_GR = 0; +// maximum number of FP registers for inputs +const int MAX_FR = 0; +#endif + +typedef enum Reg_No { +#ifdef _EM64T_ + rax_reg = 0,rbx_reg, rcx_reg, rdx_reg, + rdi_reg, rsi_reg, rsp_reg, rbp_reg, + r8_reg, r9_reg, r10_reg, r11_reg, + r12_reg, r13_reg, r14_reg, r15_reg, + xmm0_reg, xmm1_reg, xmm2_reg, xmm3_reg, + xmm4_reg, xmm5_reg, xmm6_reg, xmm7_reg, + xmm8_reg, xmm9_reg, xmm10_reg, xmm11_reg, + xmm12_reg, xmm13_reg, xmm14_reg, xmm15_reg, + +#else // !defined(_EM64T_) + + eax_reg = 0,ebx_reg, ecx_reg, edx_reg, + edi_reg, esi_reg, esp_reg, ebp_reg, + xmm0_reg, xmm1_reg, xmm2_reg, xmm3_reg, + xmm4_reg, xmm5_reg, xmm6_reg, xmm7_reg, + fs_reg, +#endif + /** @brief Total number of registers.*/ + n_reg +} Reg_No; +// +// instruction operand sizes: 8,16,32,64 bits +// +typedef enum Opnd_Size { + size_8 = 0, + size_16, + size_32, + size_64, + n_size, +#ifdef _EM64T_ + size_platf = size_64 +#else + size_platf = size_32 +#endif +} Opnd_Size; + +// +// opcodes for alu instructions +// +typedef enum ALU_Opcode { + add_opc = 0,or_opc, adc_opc, sbb_opc, + and_opc, sub_opc, xor_opc, cmp_opc, + n_alu +} ALU_Opcode; + +// +// opcodes for shift instructions +// +typedef enum Shift_Opcode { + shld_opc, shrd_opc, shl_opc, shr_opc, + sar_opc, ror_opc, max_shift_opcode=6, n_shift = 6 +} Shift_Opcode; + +typedef enum ConditionCode { + Condition_O = 0, + Condition_NO = 1, + Condition_B = 2, + Condition_NAE = Condition_B, + Condition_C = Condition_B, + Condition_NB = 3, + Condition_AE = Condition_NB, + Condition_NC = Condition_NB, + Condition_Z = 4, + Condition_E = Condition_Z, + Condition_NZ = 5, + Condition_NE = Condition_NZ, + Condition_BE = 6, + Condition_NA = Condition_BE, + Condition_NBE = 7, + Condition_A = Condition_NBE, + + Condition_S = 8, + Condition_NS = 9, + Condition_P = 10, + Condition_PE = Condition_P, + Condition_NP = 11, + Condition_PO = Condition_NP, + Condition_L = 12, + Condition_NGE = Condition_L, + Condition_NL = 13, + Condition_GE = Condition_NL, + Condition_LE = 14, + Condition_NG = Condition_LE, + Condition_NLE = 15, + Condition_G = Condition_NLE, + Condition_Count = 16 +} ConditionCode; + +// +// prefix code +// +typedef enum InstrPrefix { + no_prefix, + lock_prefix = 0xF0, + hint_branch_taken_prefix = 0x2E, + hint_branch_not_taken_prefix = 0x3E, + prefix_repne = 0xF2, + prefix_repnz = prefix_repne, + prefix_repe = 0xF3, + prefix_repz = prefix_repe, + prefix_rep = 0xF3, + prefix_cs = 0x2E, + prefix_ss = 0x36, + prefix_ds = 0x3E, + prefix_es = 0x26, + prefix_fs = 0x64, + prefix_gs = 0x65 +} InstrPrefix; + + +// +// an instruction operand +// +class Opnd { + +protected: + enum Tag { SignedImm, UnsignedImm, Reg, Mem, FP, XMM }; + + const Tag tag; + + Opnd(Tag t): tag(t) {} + +public: + void * operator new(size_t, void * mem) { + return mem; + } + + void operator delete(void *) {} + + void operator delete(void *, void *) {} + +private: + // disallow copying + Opnd(const Opnd &): tag(Mem) { assert(false); } + Opnd& operator=(const Opnd &) { assert(false); return *this; } +}; +typedef int I_32; +class Imm_Opnd: public Opnd { + +protected: + union { +#ifdef _EM64T_ + int64 value; + unsigned char bytes[8]; +#else + I_32 value; + unsigned char bytes[4]; +#endif + }; + Opnd_Size size; + +public: + Imm_Opnd(I_32 val, bool isSigned = true): + Opnd(isSigned ? SignedImm : UnsignedImm), value(val), size(size_32) { + if (isSigned) { + if (CHAR_MIN <= val && val <= CHAR_MAX) { + size = size_8; + } else if (SHRT_MIN <= val && val <= SHRT_MAX) { + size = size_16; + } + } else { + assert(val >= 0); + if (val <= UCHAR_MAX) { + size = size_8; + } else if (val <= USHRT_MAX) { + size = size_16; + } + } + } + Imm_Opnd(const Imm_Opnd& that): Opnd(that.tag), value(that.value), size(that.size) {}; + +#ifdef _EM64T_ + Imm_Opnd(Opnd_Size sz, int64 val, bool isSigned = true): + Opnd(isSigned ? SignedImm : UnsignedImm), value(val), size(sz) { +#ifndef NDEBUG + switch (size) { + case size_8: + assert(val == (int64)(I_8)val); + break; + case size_16: + assert(val == (int64)(int16)val); + break; + case size_32: + assert(val == (int64)(I_32)val); + break; + case size_64: + break; + case n_size: + assert(false); + break; + } +#endif // NDEBUG + } + + int64 get_value() const { return value; } + +#else + + Imm_Opnd(Opnd_Size sz, I_32 val, int isSigned = true): + Opnd(isSigned ? SignedImm : UnsignedImm), value(val), size(sz) { +#ifndef NDEBUG + switch (size) { + case size_8: + assert((I_32)val == (I_32)(I_8)val); + break; + case size_16: + assert((I_32)val == (I_32)(int16)val); + break; + case size_32: + break; + case size_64: + case n_size: + assert(false); + break; + } +#endif // NDEBUG + } + + I_32 get_value() const { return value; } + +#endif + Opnd_Size get_size() const { return size; } + bool is_signed() const { return tag == SignedImm; } +}; + +class RM_Opnd: public Opnd { + +public: + bool is_reg() const { return tag != SignedImm && tag != UnsignedImm && tag != Mem; } + +protected: + RM_Opnd(Tag t): Opnd(t) {} + +private: + // disallow copying + RM_Opnd(const RM_Opnd &): Opnd(Reg) { assert(false); } +}; + +class R_Opnd: public RM_Opnd { + +protected: + Reg_No _reg_no; + +public: + R_Opnd(Reg_No r): RM_Opnd(Reg), _reg_no(r) {} + Reg_No reg_no() const { return _reg_no; } + +private: + // disallow copying + R_Opnd(const R_Opnd &): RM_Opnd(Reg) { assert(false); } +}; + +// +// a memory operand with displacement +// Can also serve as a full memory operand with base,index, displacement and scale. +// Use n_reg to specify 'no register', say, for index. +class M_Opnd: public RM_Opnd { + +protected: + Imm_Opnd m_disp; + Imm_Opnd m_scale; + R_Opnd m_index; + R_Opnd m_base; + +public: + //M_Opnd(Opnd_Size sz): RM_Opnd(Mem, K_M, sz), m_disp(0), m_scale(0), m_index(n_reg), m_base(n_reg) {} + M_Opnd(I_32 disp): + RM_Opnd(Mem), m_disp(disp), m_scale(0), m_index(n_reg), m_base(n_reg) {} + M_Opnd(Reg_No rbase, I_32 rdisp): + RM_Opnd(Mem), m_disp(rdisp), m_scale(0), m_index(n_reg), m_base(rbase) {} + M_Opnd(I_32 disp, Reg_No rbase, Reg_No rindex, unsigned scale): + RM_Opnd(Mem), m_disp(disp), m_scale(scale), m_index(rindex), m_base(rbase) {} + M_Opnd(const M_Opnd & that) : RM_Opnd(Mem), + m_disp((int)that.m_disp.get_value()), m_scale((int)that.m_scale.get_value()), + m_index(that.m_index.reg_no()), m_base(that.m_base.reg_no()) + {} + // + inline const R_Opnd & base(void) const { return m_base; } + inline const R_Opnd & index(void) const { return m_index; } + inline const Imm_Opnd & scale(void) const { return m_scale; } + inline const Imm_Opnd & disp(void) const { return m_disp; } +}; + +// +// a memory operand with base register and displacement +// +class M_Base_Opnd: public M_Opnd { + +public: + M_Base_Opnd(Reg_No base, I_32 disp) : M_Opnd(disp, base, n_reg, 0) {} + +private: + // disallow copying - but it leads to ICC errors #734 in encoder.inl + // M_Base_Opnd(const M_Base_Opnd &): M_Opnd(0) { assert(false); } +}; + +// +// a memory operand with base register, scaled index register +// and displacement. +// +class M_Index_Opnd : public M_Opnd { + +public: + M_Index_Opnd(Reg_No base, Reg_No index, I_32 disp, unsigned scale): + M_Opnd(disp, base, index, scale) {} + +private: + // disallow copying - but it leads to ICC errors #734 in encoder.inl + // M_Index_Opnd(const M_Index_Opnd &): M_Opnd(0) { assert(false); } +}; + +class XMM_Opnd : public Opnd { + +protected: + unsigned m_idx; + +public: + XMM_Opnd(unsigned _idx): Opnd(XMM), m_idx(_idx) {}; + unsigned get_idx( void ) const { return m_idx; }; + +private: + // disallow copying + XMM_Opnd(const XMM_Opnd &): Opnd(XMM) { assert(false); } +}; + +// +// operand structures for ia32 registers +// +#ifdef _EM64T_ + +extern R_Opnd rax_opnd; +extern R_Opnd rcx_opnd; +extern R_Opnd rdx_opnd; +extern R_Opnd rbx_opnd; +extern R_Opnd rdi_opnd; +extern R_Opnd rsi_opnd; +extern R_Opnd rsp_opnd; +extern R_Opnd rbp_opnd; + +extern R_Opnd r8_opnd; +extern R_Opnd r9_opnd; +extern R_Opnd r10_opnd; +extern R_Opnd r11_opnd; +extern R_Opnd r12_opnd; +extern R_Opnd r13_opnd; +extern R_Opnd r14_opnd; +extern R_Opnd r15_opnd; + +extern XMM_Opnd xmm8_opnd; +extern XMM_Opnd xmm9_opnd; +extern XMM_Opnd xmm10_opnd; +extern XMM_Opnd xmm11_opnd; +extern XMM_Opnd xmm12_opnd; +extern XMM_Opnd xmm13_opnd; +extern XMM_Opnd xmm14_opnd; +extern XMM_Opnd xmm15_opnd; +#else + +extern R_Opnd eax_opnd; +extern R_Opnd ecx_opnd; +extern R_Opnd edx_opnd; +extern R_Opnd ebx_opnd; +extern R_Opnd esp_opnd; +extern R_Opnd ebp_opnd; +extern R_Opnd esi_opnd; +extern R_Opnd edi_opnd; + +#endif // _EM64T_ + +extern XMM_Opnd xmm0_opnd; +extern XMM_Opnd xmm1_opnd; +extern XMM_Opnd xmm2_opnd; +extern XMM_Opnd xmm3_opnd; +extern XMM_Opnd xmm4_opnd; +extern XMM_Opnd xmm5_opnd; +extern XMM_Opnd xmm6_opnd; +extern XMM_Opnd xmm7_opnd; + +#ifdef NO_ENCODER_INLINE + #define ENCODER_DECLARE_EXPORT +#else + #define ENCODER_DECLARE_EXPORT inline + #include "encoder.inl" +#endif + +// prefix +ENCODER_DECLARE_EXPORT char * prefix(char * stream, InstrPrefix p); + +// stack push and pop instructions +ENCODER_DECLARE_EXPORT char * push(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * push(char * stream, const Imm_Opnd & imm); +ENCODER_DECLARE_EXPORT char * pop(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf); + +// cmpxchg or xchg +ENCODER_DECLARE_EXPORT char * cmpxchg(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * xchg(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz = size_platf); + +// inc(rement), dec(rement), not, neg(ate) instructions +ENCODER_DECLARE_EXPORT char * inc(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * dec(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * _not(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * neg(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * nop(char * stream); +ENCODER_DECLARE_EXPORT char * int3(char * stream); + +// alu instructions: add, or, adc, sbb, and, sub, xor, cmp +ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const M_Opnd & m, const R_Opnd & r, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf); + +// test instruction +ENCODER_DECLARE_EXPORT char * test(char * stream, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * test(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz = size_platf); + +// shift instructions: shl, shr, sar, shld, shrd, ror +ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode opc, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode opc, const RM_Opnd & rm, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode opc, const RM_Opnd & rm, const R_Opnd & r, const Imm_Opnd & imm, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode opc, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz = size_platf); + +// multiply instructions: mul, imul +ENCODER_DECLARE_EXPORT char * mul(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const Imm_Opnd & imm, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const RM_Opnd & rm, const Imm_Opnd& imm, Opnd_Size sz = size_platf); + +// divide instructions: div, idiv +ENCODER_DECLARE_EXPORT char * idiv(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * div(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf); + +// data movement: mov +ENCODER_DECLARE_EXPORT char * mov(char * stream, const M_Opnd & m, const R_Opnd & r, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * mov(char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * mov(char * stream, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz = size_platf); + +ENCODER_DECLARE_EXPORT char * movsx( char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * movzx( char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf); + +ENCODER_DECLARE_EXPORT char * movd(char * stream, const RM_Opnd & rm, const XMM_Opnd & xmm); +ENCODER_DECLARE_EXPORT char * movd(char * stream, const XMM_Opnd & xmm, const RM_Opnd & rm); +ENCODER_DECLARE_EXPORT char * movq(char * stream, const RM_Opnd & rm, const XMM_Opnd & xmm); +ENCODER_DECLARE_EXPORT char * movq(char * stream, const XMM_Opnd & xmm, const RM_Opnd & rm); + +// sse mov +ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl); +ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const M_Opnd & mem, const XMM_Opnd & xmm, bool dbl); +ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl); + +// sse add, sub, mul, div +ENCODER_DECLARE_EXPORT char * sse_add(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl); +ENCODER_DECLARE_EXPORT char * sse_add(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl); + +ENCODER_DECLARE_EXPORT char * sse_sub(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl); +ENCODER_DECLARE_EXPORT char * sse_sub(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl); + +ENCODER_DECLARE_EXPORT char * sse_mul(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl); +ENCODER_DECLARE_EXPORT char * sse_mul(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl); + +ENCODER_DECLARE_EXPORT char * sse_div(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl); +ENCODER_DECLARE_EXPORT char * sse_div(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl); + +// xor, compare +ENCODER_DECLARE_EXPORT char * sse_xor(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1); + +ENCODER_DECLARE_EXPORT char * sse_compare(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl); +ENCODER_DECLARE_EXPORT char * sse_compare(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem, bool dbl); + +// sse conversions +ENCODER_DECLARE_EXPORT char * sse_cvt_si(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl); +ENCODER_DECLARE_EXPORT char * sse_cvtt2si(char * stream, const R_Opnd & reg, const M_Opnd & mem, bool dbl); +ENCODER_DECLARE_EXPORT char * sse_cvtt2si(char * stream, const R_Opnd & reg, const XMM_Opnd & xmm, bool dbl); +ENCODER_DECLARE_EXPORT char * sse_cvt_fp2dq(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl); +ENCODER_DECLARE_EXPORT char * sse_cvt_dq2fp(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl); +ENCODER_DECLARE_EXPORT char * sse_d2s(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem64); +ENCODER_DECLARE_EXPORT char * sse_d2s(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1); +ENCODER_DECLARE_EXPORT char * sse_s2d(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem32); +ENCODER_DECLARE_EXPORT char * sse_s2d(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1); + +// condition operations +ENCODER_DECLARE_EXPORT char * cmov(char * stream, ConditionCode cc, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * setcc(char * stream, ConditionCode cc, const RM_Opnd & rm8); + +// load effective address: lea +ENCODER_DECLARE_EXPORT char * lea(char * stream, const R_Opnd & r, const M_Opnd & m, Opnd_Size sz = size_platf); +ENCODER_DECLARE_EXPORT char * cdq(char * stream); +ENCODER_DECLARE_EXPORT char * wait(char * stream); + +// control-flow instructions +ENCODER_DECLARE_EXPORT char * loop(char * stream, const Imm_Opnd & imm); + +// jump with 8-bit relative +ENCODER_DECLARE_EXPORT char * jump8(char * stream, const Imm_Opnd & imm); + +// jump with 32-bit relative +ENCODER_DECLARE_EXPORT char * jump32(char * stream, const Imm_Opnd & imm); + +// register indirect jump +ENCODER_DECLARE_EXPORT char * jump(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf); + +// jump to target address +ENCODER_DECLARE_EXPORT char *jump(char * stream, char *target); + +// jump with displacement +//char * jump(char * stream, I_32 disp); + +// conditional branch with 8-bit branch offset +ENCODER_DECLARE_EXPORT char * branch8(char * stream, ConditionCode cc, const Imm_Opnd & imm, InstrPrefix prefix = no_prefix); + +// conditional branch with 32-bit branch offset +ENCODER_DECLARE_EXPORT char * branch32(char * stream, ConditionCode cc, const Imm_Opnd & imm, InstrPrefix prefix = no_prefix); + +// conditional branch with target label address +//char * branch(char * stream, ConditionCode cc, const char * target, InstrPrefix prefix = no_prefix); + +// conditional branch with displacement immediate +ENCODER_DECLARE_EXPORT char * branch(char * stream, ConditionCode cc, I_32 disp, InstrPrefix prefix = no_prefix); + +// call with displacement +ENCODER_DECLARE_EXPORT char * call(char * stream, const Imm_Opnd & imm); + +// indirect call through register or memory location +ENCODER_DECLARE_EXPORT char * call(char * stream, const RM_Opnd & rm, Opnd_Size sz = size_platf); + +// call target address +ENCODER_DECLARE_EXPORT char * call(char * stream, const char * target); + +// return instruction +ENCODER_DECLARE_EXPORT char * ret(char * stream); +ENCODER_DECLARE_EXPORT char * ret(char * stream, unsigned short pop); +ENCODER_DECLARE_EXPORT char * ret(char * stream, const Imm_Opnd & imm); + +// string operations +ENCODER_DECLARE_EXPORT char * set_d(char * stream, bool set); +ENCODER_DECLARE_EXPORT char * scas(char * stream, unsigned char prefix); +ENCODER_DECLARE_EXPORT char * stos(char * stream, unsigned char prefix); + +// floating-point instructions + +// st(0) = st(0) fp_op m{32,64}real +//!char * fp_op_mem(char * stream, FP_Opcode opc,const M_Opnd& mem,int is_double); + +// st(0) = st(0) fp_op st(i) +//!char *fp_op(char * stream, FP_Opcode opc,unsigned i); + +// st(i) = st(i) fp_op st(0) ; optionally pop stack +//!char * fp_op(char * stream, FP_Opcode opc,unsigned i,unsigned pop_stk); + +// compare st(0),st(1) and pop stack twice +//!char * fcompp(char * stream); +ENCODER_DECLARE_EXPORT char * fldcw(char * stream, const M_Opnd & mem); +ENCODER_DECLARE_EXPORT char * fnstcw(char * stream, const M_Opnd & mem); +ENCODER_DECLARE_EXPORT char * fnstsw(char * stream); +//!char * fchs(char * stream); +//!char * frem(char * stream); +//!char * fxch(char * stream,unsigned i); +//!char * fcomip(char * stream, unsigned i); + +// load from memory (as fp) into fp register stack +ENCODER_DECLARE_EXPORT char * fld(char * stream, const M_Opnd & m, bool is_double); +//!char *fld80(char * stream,const M_Opnd& mem); + +// load from memory (as int) into fp register stack +//!char * fild(char * stream,const M_Opnd& mem,int is_long); + +// push st(i) onto fp register stack +//!char * fld(char * stream,unsigned i); + +// push the constants 0.0 and 1.0 onto the fp register stack +//!char * fldz(char * stream); +//!char * fld1(char * stream); + +// store stack to memory (as int), always popping the stack +ENCODER_DECLARE_EXPORT char * fist(char * stream, const M_Opnd & mem, bool is_long, bool pop_stk); +// store stack to to memory (as fp), optionally popping the stack +ENCODER_DECLARE_EXPORT char * fst(char * stream, const M_Opnd & m, bool is_double, bool pop_stk); +// store ST(0) to ST(i), optionally popping the stack. Takes 1 clock +ENCODER_DECLARE_EXPORT char * fst(char * stream, unsigned i, bool pop_stk); + +//!char * pushad(char * stream); +//!char * pushfd(char * stream); +//!char * popad(char * stream); +//!char * popfd(char * stream); + +// stack frame allocation instructions: enter & leave +// +// enter frame_size +// +// is equivalent to: +// +// push ebp +// mov ebp,esp +// sub esp,frame_size +// +//!char *enter(char * stream,const Imm_Opnd& imm); + +// leave +// is equivalent to: +// +// mov esp,ebp +// pop ebp +//!char *leave(char * stream); + +// sahf loads SF, ZF, AF, PF, and CF flags from eax +//!char *sahf(char * stream); + +// Intrinsic FP math functions + +//!char *math_fsin(char * stream); +//!char *math_fcos(char * stream); +//!char *math_fabs(char * stream); +//!char *math_fpatan(char * stream); +ENCODER_DECLARE_EXPORT char * fprem(char * stream); +ENCODER_DECLARE_EXPORT char * fprem1(char * stream); +//!char *math_frndint(char * stream); +//!char *math_fptan(char * stream); + +// +// Add 1-7 bytes padding, with as few instructions as possible, +// with no effect on the processor state (e.g., registers, flags) +// +//!char *padding(char * stream, unsigned num); + +// prolog and epilog code generation +//- char *prolog(char * stream,unsigned frame_size,unsigned reg_save_mask); +//- char *epilog(char * stream,unsigned reg_save_mask); + +//!extern R_Opnd reg_operand_array[]; + +// fsave and frstor +//!char *fsave(char * stream); +//!char *frstor(char * stream); + +// lahf : Load Status Flags into AH Register +//!char *lahf(char * stream); + +// mfence : Memory Fence +//!char *mfence(char * stream); + +#endif // _VM_ENCODER_H_ diff --git a/libpixelflinger/codeflinger/x86/libenc/encoder.inl b/libpixelflinger/codeflinger/x86/libenc/encoder.inl new file mode 100644 index 0000000..ec72097 --- /dev/null +++ b/libpixelflinger/codeflinger/x86/libenc/encoder.inl @@ -0,0 +1,863 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * @author Alexander V. Astapchuk + */ +#include <stdio.h> +#include <assert.h> +#include <limits.h> + +extern const RegName map_of_regno_2_regname[]; +extern const OpndSize map_of_EncoderOpndSize_2_RealOpndSize[]; +extern const Mnemonic map_of_alu_opcode_2_mnemonic[]; +extern const Mnemonic map_of_shift_opcode_2_mnemonic[]; + +// S_ stands for 'Signed' +extern const Mnemonic S_map_of_condition_code_2_branch_mnemonic[]; +// U_ stands for 'Unsigned' +extern const Mnemonic U_map_of_condition_code_2_branch_mnemonic[]; + +inline static RegName map_reg(Reg_No r) { + assert(r >= 0 && r <= n_reg); + return map_of_regno_2_regname[r]; +} + +inline static OpndSize map_size(Opnd_Size o_size) { + assert(o_size >= 0 && o_size <= n_size); + return map_of_EncoderOpndSize_2_RealOpndSize[o_size]; +} + +inline static Mnemonic map_alu(ALU_Opcode alu) { + assert(alu >= 0 && alu < n_alu); + return map_of_alu_opcode_2_mnemonic[alu]; +} + +inline static Mnemonic map_shift(Shift_Opcode shc) { + assert(shc >= 0 && shc < n_shift); + return map_of_shift_opcode_2_mnemonic[shc]; +} + +inline bool fit8(int64 val) { + return (CHAR_MIN <= val) && (val <= CHAR_MAX); +} + +inline bool fit32(int64 val) { + return (INT_MIN <= val) && (val <= INT_MAX); +} + +inline static void add_r(EncoderBase::Operands & args, const R_Opnd & r, Opnd_Size sz, OpndExt ext = OpndExt_None) { + RegName reg = map_reg(r.reg_no()); + if (sz != n_size) { + OpndSize size = map_size(sz); + if (size != getRegSize(reg)) { + reg = getAliasReg(reg, size); + } + } + args.add(EncoderBase::Operand(reg, ext)); +} + +inline static void add_m(EncoderBase::Operands & args, const M_Opnd & m, Opnd_Size sz, OpndExt ext = OpndExt_None) { + assert(n_size != sz); + args.add(EncoderBase::Operand(map_size(sz), + map_reg(m.base().reg_no()), map_reg(m.index().reg_no()), + (unsigned)m.scale().get_value(), (int)m.disp().get_value(), ext)); +} + +inline static void add_rm(EncoderBase::Operands & args, const RM_Opnd & rm, Opnd_Size sz, OpndExt ext = OpndExt_None) { + rm.is_reg() ? add_r(args, (R_Opnd &)rm, sz, ext) : add_m(args, (M_Opnd &)rm, sz, ext); +} + +inline static void add_xmm(EncoderBase::Operands & args, const XMM_Opnd & xmm, bool dbl) { + // Gregory - + // XMM registers indexes in Reg_No enum are shifted by xmm0_reg, their indexes + // don't start with 0, so it is necessary to subtract xmm0_reg index from + // xmm.get_idx() value + assert(xmm.get_idx() >= xmm0_reg); + return args.add((RegName)( (dbl ? RegName_XMM0D : RegName_XMM0S) + xmm.get_idx() - + xmm0_reg)); +} + +inline static void add_fp(EncoderBase::Operands & args, unsigned i, bool dbl) { + return args.add((RegName)( (dbl ? RegName_FP0D : RegName_FP0S) + i)); +} + +inline static void add_imm(EncoderBase::Operands & args, const Imm_Opnd & imm) { + assert(n_size != imm.get_size()); + args.add(EncoderBase::Operand(map_size(imm.get_size()), imm.get_value(), + imm.is_signed() ? OpndExt_Signed : OpndExt_Zero)); +} + +ENCODER_DECLARE_EXPORT char * prefix(char * stream, InstrPrefix p) { + *stream = (char)p; + return stream + 1; +} + +// stack push and pop instructions +ENCODER_DECLARE_EXPORT char * push(char * stream, const RM_Opnd & rm, Opnd_Size sz) { + EncoderBase::Operands args; + add_rm(args, rm, sz); + return (char*)EncoderBase::encode(stream, Mnemonic_PUSH, args); +} + +ENCODER_DECLARE_EXPORT char * push(char * stream, const Imm_Opnd & imm) { + EncoderBase::Operands args; +#ifdef _EM64T_ + add_imm(args, imm); +#else + // we need this workaround to be compatible with the former ia32 encoder implementation + add_imm(args, Imm_Opnd(size_32, imm.get_value())); +#endif + return EncoderBase::encode(stream, Mnemonic_PUSH, args); +} + +ENCODER_DECLARE_EXPORT char * pop(char * stream, const RM_Opnd & rm, Opnd_Size sz) { + EncoderBase::Operands args; + add_rm(args, rm, sz); + return (char*)EncoderBase::encode(stream, Mnemonic_POP, args); +} + +// cmpxchg or xchg +ENCODER_DECLARE_EXPORT char * cmpxchg(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz) { + EncoderBase::Operands args; + add_rm(args, rm, sz); + add_r(args, r, sz); + RegName implicitReg = getAliasReg(RegName_EAX, map_size(sz)); + args.add(implicitReg); + return (char*)EncoderBase::encode(stream, Mnemonic_CMPXCHG, args); +} + +ENCODER_DECLARE_EXPORT char * xchg(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz) { + EncoderBase::Operands args; + add_rm(args, rm, sz); + add_r(args, r, sz); + return (char*)EncoderBase::encode(stream, Mnemonic_XCHG, args); +} + +// inc(rement), dec(rement), not, neg(ate) instructions +ENCODER_DECLARE_EXPORT char * inc(char * stream, const RM_Opnd & rm, Opnd_Size sz) { + EncoderBase::Operands args; + add_rm(args, rm, sz); + return (char*)EncoderBase::encode(stream, Mnemonic_INC, args); +} + +ENCODER_DECLARE_EXPORT char * dec(char * stream, const RM_Opnd & rm, Opnd_Size sz) { + EncoderBase::Operands args; + add_rm(args, rm, sz); + return (char*)EncoderBase::encode(stream, Mnemonic_DEC, args); +} + +ENCODER_DECLARE_EXPORT char * _not(char * stream, const RM_Opnd & rm, Opnd_Size sz) { + EncoderBase::Operands args; + add_rm(args, rm, sz); + return (char*)EncoderBase::encode(stream, Mnemonic_NOT, args); +} + +ENCODER_DECLARE_EXPORT char * neg(char * stream, const RM_Opnd & rm, Opnd_Size sz) { + EncoderBase::Operands args; + add_rm(args, rm, sz); + return (char*)EncoderBase::encode(stream, Mnemonic_NEG, args); +} + +ENCODER_DECLARE_EXPORT char * nop(char * stream) { + EncoderBase::Operands args; + return (char*)EncoderBase::encode(stream, Mnemonic_NOP, args); +} + +ENCODER_DECLARE_EXPORT char * int3(char * stream) { + EncoderBase::Operands args; + return (char*)EncoderBase::encode(stream, Mnemonic_INT3, args); +} + +// alu instructions: add, or, adc, sbb, and, sub, xor, cmp +ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz) { + EncoderBase::Operands args; + add_rm(args, rm, sz); + add_imm(args, imm); + return (char*)EncoderBase::encode(stream, map_alu(opc), args); +}; + +ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const M_Opnd & m, const R_Opnd & r, Opnd_Size sz) { + EncoderBase::Operands args; + add_rm(args, m, sz); + add_rm(args, r, sz); + return (char*)EncoderBase::encode(stream, map_alu(opc), args); +} + +ENCODER_DECLARE_EXPORT char * alu(char * stream, ALU_Opcode opc, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz) { + EncoderBase::Operands args; + add_rm(args, r, sz); + add_rm(args, rm, sz); + return (char*)EncoderBase::encode(stream, map_alu(opc), args); +} + +// test instruction +ENCODER_DECLARE_EXPORT char * test(char * stream, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz) { + EncoderBase::Operands args; + add_rm(args, rm, sz); + assert(imm.get_size() <= sz); + add_imm(args, imm); + return (char*)EncoderBase::encode(stream, Mnemonic_TEST, args); +} + +ENCODER_DECLARE_EXPORT char * test(char * stream, const RM_Opnd & rm, const R_Opnd & r, Opnd_Size sz) { + EncoderBase::Operands args; + add_rm(args, rm, sz); + add_r(args, r, sz); + return (char*)EncoderBase::encode(stream, Mnemonic_TEST, args); +} + +// shift instructions: shl, shr, sar, shld, shrd +ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode shc, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz) { + EncoderBase::Operands args; + add_rm(args, rm, sz); + add_imm(args, imm); + return (char*)EncoderBase::encode(stream, map_shift(shc), args); +} + +ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode shc, const RM_Opnd & rm, Opnd_Size sz) { + EncoderBase::Operands args; + add_rm(args, rm, sz); + args.add(RegName_CL); + return (char*)EncoderBase::encode(stream, map_shift(shc), args); +} + +ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode shc, const RM_Opnd & rm, + const R_Opnd & r, const Imm_Opnd & imm, Opnd_Size sz) { + EncoderBase::Operands args; + assert(shc == shld_opc || shc == shrd_opc); + add_rm(args, rm, sz); + add_r(args, r, sz); + add_imm(args, imm); + return (char*)EncoderBase::encode(stream, map_shift(shc), args); +} + +ENCODER_DECLARE_EXPORT char * shift(char * stream, Shift_Opcode shc, const RM_Opnd & rm, + const R_Opnd & r, Opnd_Size sz) { + EncoderBase::Operands args; + assert(shc == shld_opc || shc == shrd_opc); + add_rm(args, rm, sz); + add_r(args, r, sz); + args.add(RegName_CL); + return (char*)EncoderBase::encode(stream, map_shift(shc), args); +} + +// multiply instructions: mul, imul +ENCODER_DECLARE_EXPORT char * mul(char * stream, const RM_Opnd & rm, Opnd_Size sz) { + EncoderBase::Operands args; + args.add(RegName_EDX); + args.add(RegName_EAX); + add_rm(args, rm, sz); + return (char*)EncoderBase::encode(stream, Mnemonic_MUL, args); +} + +ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz) { + EncoderBase::Operands args; + add_r(args, r, sz); + add_rm(args, rm, sz); + return (char*)EncoderBase::encode(stream, Mnemonic_IMUL, args); +} + +ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const Imm_Opnd & imm, Opnd_Size sz) { + EncoderBase::Operands args; + add_r(args, r, sz); + add_imm(args, imm); + return (char*)EncoderBase::encode(stream, Mnemonic_IMUL, args); +} + +ENCODER_DECLARE_EXPORT char * imul(char * stream, const R_Opnd & r, const RM_Opnd & rm, + const Imm_Opnd & imm, Opnd_Size sz) { + EncoderBase::Operands args; + add_r(args, r, sz); + add_rm(args, rm, sz); + add_imm(args, imm); + return (char*)EncoderBase::encode(stream, Mnemonic_IMUL, args); +} + +// divide instructions: div, idiv +ENCODER_DECLARE_EXPORT char * idiv(char * stream, const RM_Opnd & rm, Opnd_Size sz) { + EncoderBase::Operands args; +#ifdef _EM64T_ + add_r(args, rdx_opnd, sz); + add_r(args, rax_opnd, sz); +#else + add_r(args, edx_opnd, sz); + add_r(args, eax_opnd, sz); +#endif + add_rm(args, rm, sz); + return (char*)EncoderBase::encode(stream, Mnemonic_IDIV, args); +} + +ENCODER_DECLARE_EXPORT char * div(char * stream, const RM_Opnd & rm, Opnd_Size sz) { + EncoderBase::Operands args; +#ifdef _EM64T_ + add_r(args, rdx_opnd, sz); + add_r(args, rax_opnd, sz); +#else + add_r(args, edx_opnd, sz); + add_r(args, eax_opnd, sz); +#endif + add_rm(args, rm, sz); + return (char*)EncoderBase::encode(stream, Mnemonic_DIV, args); +} + +// data movement: mov +ENCODER_DECLARE_EXPORT char * mov(char * stream, const M_Opnd & m, const R_Opnd & r, Opnd_Size sz) { + EncoderBase::Operands args; + add_m(args, m, sz); + add_r(args, r, sz); + return (char*)EncoderBase::encode(stream, Mnemonic_MOV, args); +} + +ENCODER_DECLARE_EXPORT char * mov(char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz) { + EncoderBase::Operands args; + add_r(args, r, sz); + add_rm(args, rm, sz); + return (char*)EncoderBase::encode(stream, Mnemonic_MOV, args); +} + +ENCODER_DECLARE_EXPORT char * mov(char * stream, const RM_Opnd & rm, const Imm_Opnd & imm, Opnd_Size sz) { + EncoderBase::Operands args; + add_rm(args, rm, sz); + add_imm(args, imm); + return (char*)EncoderBase::encode(stream, Mnemonic_MOV, args); +} + +ENCODER_DECLARE_EXPORT char * movd(char * stream, const RM_Opnd & rm, const XMM_Opnd & xmm) { + EncoderBase::Operands args; + add_rm(args, rm, size_32); + add_xmm(args, xmm, false); + return (char*)EncoderBase::encode(stream, Mnemonic_MOVD, args); +} + +ENCODER_DECLARE_EXPORT char * movd(char * stream, const XMM_Opnd & xmm, const RM_Opnd & rm) { + EncoderBase::Operands args; + add_xmm(args, xmm, false); + add_rm(args, rm, size_32); + return (char*)EncoderBase::encode(stream, Mnemonic_MOVD, args); +} + +ENCODER_DECLARE_EXPORT char * movq(char * stream, const RM_Opnd & rm, const XMM_Opnd & xmm) { + EncoderBase::Operands args; + add_rm(args, rm, size_64); + add_xmm(args, xmm, true); + return (char*)EncoderBase::encode(stream, Mnemonic_MOVQ, args); +} + +ENCODER_DECLARE_EXPORT char * movq(char * stream, const XMM_Opnd & xmm, const RM_Opnd & rm) { + EncoderBase::Operands args; + add_xmm(args, xmm, true); + add_rm(args, rm, size_64); + return (char*)EncoderBase::encode(stream, Mnemonic_MOVQ, args); +} + +ENCODER_DECLARE_EXPORT char * movsx(char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz) { + EncoderBase::Operands args; + add_r(args, r, n_size); + add_rm(args, rm, sz, OpndExt_Signed); + return (char*)EncoderBase::encode(stream, Mnemonic_MOVSX, args); +} + +ENCODER_DECLARE_EXPORT char * movzx(char * stream, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz) { + EncoderBase::Operands args; + add_r(args, r, n_size); + // movzx r64, r/m32 is not available on em64t + // mov r32, r/m32 should zero out upper bytes + assert(sz <= size_16); + add_rm(args, rm, sz, OpndExt_Zero); + return (char*)EncoderBase::encode(stream, Mnemonic_MOVZX, args); +} + +// sse mov +ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl) { + EncoderBase::Operands args; + add_xmm(args, xmm, dbl); + add_m(args, mem, dbl ? size_64 : size_32); + return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_MOVSD : Mnemonic_MOVSS, args); +} + +ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const M_Opnd & mem, const XMM_Opnd & xmm, bool dbl) { + EncoderBase::Operands args; + add_m(args, mem, dbl ? size_64 : size_32); + add_xmm(args, xmm, dbl); + return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_MOVSD : Mnemonic_MOVSS, args); +} + +ENCODER_DECLARE_EXPORT char * sse_mov(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) { + EncoderBase::Operands args; + add_xmm(args, xmm0, dbl); + add_xmm(args, xmm1, dbl); + return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_MOVSD : Mnemonic_MOVSS, args ); +} + +// sse add, sub, mul, div +ENCODER_DECLARE_EXPORT char * sse_add(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl) { + EncoderBase::Operands args; + add_xmm(args, xmm, dbl); + add_m(args, mem, dbl ? size_64 : size_32); + return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_ADDSD : Mnemonic_ADDSS, args); +} + +ENCODER_DECLARE_EXPORT char * sse_add(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) { + EncoderBase::Operands args; + add_xmm(args, xmm0, dbl); + add_xmm(args, xmm1, dbl); + return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_ADDSD : Mnemonic_ADDSS, args); +} + +ENCODER_DECLARE_EXPORT char * sse_sub(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl) { + EncoderBase::Operands args; + add_xmm(args, xmm, dbl); + add_m(args, mem, dbl ? size_64 : size_32); + return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_SUBSD : Mnemonic_SUBSS, args); +} + +ENCODER_DECLARE_EXPORT char * sse_sub(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) { + EncoderBase::Operands args; + add_xmm(args, xmm0, dbl); + add_xmm(args, xmm1, dbl); + return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_SUBSD : Mnemonic_SUBSS, args); +} + +ENCODER_DECLARE_EXPORT char * sse_mul( char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl) { + EncoderBase::Operands args; + add_xmm(args, xmm, dbl); + add_m(args, mem, dbl ? size_64 : size_32); + return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_MULSD : Mnemonic_MULSS, args); +} + +ENCODER_DECLARE_EXPORT char * sse_mul(char * stream, const XMM_Opnd& xmm0, const XMM_Opnd& xmm1, bool dbl) { + EncoderBase::Operands args; + add_xmm(args, xmm0, dbl); + add_xmm(args, xmm1, dbl); + return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_MULSD : Mnemonic_MULSS, args); +} + +ENCODER_DECLARE_EXPORT char * sse_div(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl) { + EncoderBase::Operands args; + add_xmm(args, xmm, dbl); + add_m(args, mem, dbl ? size_64 : size_32); + return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_DIVSD : Mnemonic_DIVSS, args); +} + +ENCODER_DECLARE_EXPORT char * sse_div(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) { + EncoderBase::Operands args; + add_xmm(args, xmm0, dbl); + add_xmm(args, xmm1, dbl); + return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_DIVSD : Mnemonic_DIVSS, args); +} + +ENCODER_DECLARE_EXPORT char * sse_xor(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1) { + EncoderBase::Operands args; + add_xmm(args, xmm0, true); + add_xmm(args, xmm1, true); + return (char*)EncoderBase::encode(stream, Mnemonic_PXOR, args); +} + +ENCODER_DECLARE_EXPORT char * sse_compare(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) { + EncoderBase::Operands args; + add_xmm(args, xmm0, true); + add_xmm(args, xmm1, true); + return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_COMISD : Mnemonic_COMISS, args); +} + +ENCODER_DECLARE_EXPORT char * sse_compare(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem, bool dbl) { + EncoderBase::Operands args; + add_xmm(args, xmm0, dbl); + add_m(args, mem, dbl ? size_64 : size_32); + return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_COMISD : Mnemonic_COMISS, args); +} + +// sse conversions +ENCODER_DECLARE_EXPORT char * sse_cvt_si(char * stream, const XMM_Opnd & xmm, const M_Opnd & mem, bool dbl) { + EncoderBase::Operands args; + add_xmm(args, xmm, dbl); + add_m(args, mem, size_32); + return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_CVTSI2SD : Mnemonic_CVTSI2SS, args); +} + +ENCODER_DECLARE_EXPORT char * sse_cvtt2si(char * stream, const R_Opnd & reg, const M_Opnd & mem, bool dbl) { + EncoderBase::Operands args; + add_rm(args, reg, size_32); + add_m(args, mem, dbl ? size_64 : size_32); + return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_CVTTSD2SI : Mnemonic_CVTTSS2SI, args); +} + +ENCODER_DECLARE_EXPORT char * sse_cvtt2si(char * stream, const R_Opnd & reg, const XMM_Opnd & xmm, bool dbl) { + EncoderBase::Operands args; + add_rm(args, reg, size_32); + add_xmm(args, xmm, dbl); + return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_CVTTSD2SI : Mnemonic_CVTTSS2SI, args); +} + +ENCODER_DECLARE_EXPORT char * sse_cvt_fp2dq(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) { + EncoderBase::Operands args; + add_xmm(args, xmm0, dbl); + add_xmm(args, xmm1, dbl); + return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_CVTTPD2DQ : Mnemonic_CVTTPS2DQ, args); +} + +ENCODER_DECLARE_EXPORT char * sse_cvt_dq2fp(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1, bool dbl) { + EncoderBase::Operands args; + add_xmm(args, xmm0, dbl); + add_xmm(args, xmm1, dbl); + return (char*)EncoderBase::encode(stream, dbl ? Mnemonic_CVTDQ2PD : Mnemonic_CVTDQ2PS, args); +} + +ENCODER_DECLARE_EXPORT char * sse_d2s(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem64) { + EncoderBase::Operands args; + add_xmm(args, xmm0, false); + add_m(args, mem64, size_64); + return (char*)EncoderBase::encode(stream, Mnemonic_CVTSD2SS, args); +} + +ENCODER_DECLARE_EXPORT char * sse_d2s(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1) { + EncoderBase::Operands args; + add_xmm(args, xmm0, false); + add_xmm(args, xmm1, true); + return (char*)EncoderBase::encode(stream, Mnemonic_CVTSD2SS, args); +} + +ENCODER_DECLARE_EXPORT char * sse_s2d(char * stream, const XMM_Opnd & xmm0, const M_Opnd & mem32) { + EncoderBase::Operands args; + add_xmm(args, xmm0, true); + add_m(args, mem32, size_32); + return (char*)EncoderBase::encode(stream, Mnemonic_CVTSS2SD, args); +} + +ENCODER_DECLARE_EXPORT char * sse_s2d(char * stream, const XMM_Opnd & xmm0, const XMM_Opnd & xmm1) { + EncoderBase::Operands args; + add_xmm(args, xmm0, true); + add_xmm(args, xmm1, false); + return (char*)EncoderBase::encode(stream, Mnemonic_CVTSS2SD, args); +} + +// condition operations +ENCODER_DECLARE_EXPORT char *cmov(char * stream, ConditionCode cc, const R_Opnd & r, const RM_Opnd & rm, Opnd_Size sz) { + EncoderBase::Operands args; + add_r(args, r, sz); + add_rm(args, rm, sz); + return (char*)EncoderBase::encode(stream, (Mnemonic)(Mnemonic_CMOVcc + cc), args); +} + +ENCODER_DECLARE_EXPORT char * setcc(char * stream, ConditionCode cc, const RM_Opnd & rm8) { + EncoderBase::Operands args; + add_rm(args, rm8, size_8); + return (char*)EncoderBase::encode(stream, (Mnemonic)(Mnemonic_SETcc + cc), args); +} + +// load effective address: lea +ENCODER_DECLARE_EXPORT char * lea(char * stream, const R_Opnd & r, const M_Opnd & m, Opnd_Size sz) { + EncoderBase::Operands args; + add_r(args, r, sz); + add_m(args, m, sz); + return (char*)EncoderBase::encode(stream, Mnemonic_LEA, args); +} + +ENCODER_DECLARE_EXPORT char * cdq(char * stream) { + EncoderBase::Operands args; + args.add(RegName_EDX); + args.add(RegName_EAX); + return (char*)EncoderBase::encode(stream, Mnemonic_CDQ, args); +} + +ENCODER_DECLARE_EXPORT char * wait(char * stream) { + return (char*)EncoderBase::encode(stream, Mnemonic_WAIT, EncoderBase::Operands()); +} + +// control-flow instructions + +// loop +ENCODER_DECLARE_EXPORT char * loop(char * stream, const Imm_Opnd & imm) { + EncoderBase::Operands args; + assert(imm.get_size() == size_8); + args.add(RegName_ECX); + add_imm(args, imm); + return (char*)EncoderBase::encode(stream, Mnemonic_LOOP, args); +} + +// jump +ENCODER_DECLARE_EXPORT char * jump8(char * stream, const Imm_Opnd & imm) { + EncoderBase::Operands args; + assert(imm.get_size() == size_8); + add_imm(args, imm); + return (char*)EncoderBase::encode(stream, Mnemonic_JMP, args); +} + +ENCODER_DECLARE_EXPORT char * jump32(char * stream, const Imm_Opnd & imm) { + EncoderBase::Operands args; + assert(imm.get_size() == size_32); + add_imm(args, imm); + return (char*)EncoderBase::encode(stream, Mnemonic_JMP, args); +} + +ENCODER_DECLARE_EXPORT char * jump(char * stream, const RM_Opnd & rm, Opnd_Size sz) { + EncoderBase::Operands args; + add_rm(args, rm, sz); + return (char*)EncoderBase::encode(stream, Mnemonic_JMP, args); +} + +/** + * @note On EM64T: if target lies beyond 2G (does not fit into 32 bit + * offset) then generates indirect jump using RAX (whose content is + * destroyed). + */ +ENCODER_DECLARE_EXPORT char * jump(char * stream, char * target) { +#ifdef _EM64T_ + int64 offset = target - stream; + // sub 2 bytes for the short version + offset -= 2; + if (fit8(offset)) { + // use 8-bit signed relative form + return jump8(stream, Imm_Opnd(size_8, offset)); + } else if (fit32(offset)) { + // sub 5 (3 + 2)bytes for the long version + offset -= 3; + // use 32-bit signed relative form + return jump32(stream, Imm_Opnd(size_32, offset)); + } + // need to use absolute indirect jump + stream = mov(stream, rax_opnd, Imm_Opnd(size_64, (int64)target), size_64); + return jump(stream, rax_opnd, size_64); +#else + I_32 offset = target - stream; + // sub 2 bytes for the short version + offset -= 2; + if (fit8(offset)) { + // use 8-bit signed relative form + return jump8(stream, Imm_Opnd(size_8, offset)); + } + // sub 5 (3 + 2) bytes for the long version + offset -= 3; + // use 32-bit signed relative form + return jump32(stream, Imm_Opnd(size_32, offset)); +#endif +} + +// branch +ENCODER_DECLARE_EXPORT char * branch8(char * stream, ConditionCode cond, + const Imm_Opnd & imm, + InstrPrefix pref) +{ + if (pref != no_prefix) { + assert(pref == hint_branch_taken_prefix || pref == hint_branch_taken_prefix); + stream = prefix(stream, pref); + } + Mnemonic m = (Mnemonic)(Mnemonic_Jcc + cond); + EncoderBase::Operands args; + assert(imm.get_size() == size_8); + add_imm(args, imm); + return (char*)EncoderBase::encode(stream, m, args); +} + +ENCODER_DECLARE_EXPORT char * branch32(char * stream, ConditionCode cond, + const Imm_Opnd & imm, + InstrPrefix pref) +{ + if (pref != no_prefix) { + assert(pref == hint_branch_taken_prefix || pref == hint_branch_taken_prefix); + stream = prefix(stream, pref); + } + Mnemonic m = (Mnemonic)(Mnemonic_Jcc + cond); + EncoderBase::Operands args; + assert(imm.get_size() == size_32); + add_imm(args, imm); + return (char*)EncoderBase::encode(stream, m, args); +} + +/* +ENCODER_DECLARE_EXPORT char * branch(char * stream, ConditionCode cc, const char * target, InstrPrefix prefix) { +// sub 2 bytes for the short version +int64 offset = stream-target-2; +if( fit8(offset) ) { +return branch8(stream, cc, Imm_Opnd(size_8, (char)offset), is_signed); +} +return branch32(stream, cc, Imm_Opnd(size_32, (int)offset), is_signed); +} +*/ + +// call +ENCODER_DECLARE_EXPORT char * call(char * stream, const Imm_Opnd & imm) +{ + EncoderBase::Operands args; + add_imm(args, imm); + return (char*)EncoderBase::encode(stream, Mnemonic_CALL, args); +} + +ENCODER_DECLARE_EXPORT char * call(char * stream, const RM_Opnd & rm, + Opnd_Size sz) +{ + EncoderBase::Operands args; + add_rm(args, rm, sz); + return (char*)EncoderBase::encode(stream, Mnemonic_CALL, args); +} + +/** +* @note On EM64T: if target lies beyond 2G (does not fit into 32 bit +* offset) then generates indirect jump using RAX (whose content is +* destroyed). +*/ +ENCODER_DECLARE_EXPORT char * call(char * stream, const char * target) +{ +#ifdef _EM64T_ + int64 offset = target - stream; + if (fit32(offset)) { + offset -= 5; // sub 5 bytes for this instruction + Imm_Opnd imm(size_32, offset); + return call(stream, imm); + } + // need to use absolute indirect call + stream = mov(stream, rax_opnd, Imm_Opnd(size_64, (int64)target), size_64); + return call(stream, rax_opnd, size_64); +#else + I_32 offset = target - stream; + offset -= 5; // sub 5 bytes for this instruction + Imm_Opnd imm(size_32, offset); + return call(stream, imm); +#endif +} + +// return instruction +ENCODER_DECLARE_EXPORT char * ret(char * stream) +{ + EncoderBase::Operands args; + return (char*)EncoderBase::encode(stream, Mnemonic_RET, args); +} + +ENCODER_DECLARE_EXPORT char * ret(char * stream, const Imm_Opnd & imm) +{ + EncoderBase::Operands args; + // TheManual says imm can be 16-bit only + //assert(imm.get_size() <= size_16); + args.add(EncoderBase::Operand(map_size(size_16), imm.get_value())); + return (char*)EncoderBase::encode(stream, Mnemonic_RET, args); +} + +ENCODER_DECLARE_EXPORT char * ret(char * stream, unsigned short pop) +{ + // TheManual says it can only be imm16 + EncoderBase::Operands args(EncoderBase::Operand(OpndSize_16, pop, OpndExt_Zero)); + return (char*)EncoderBase::encode(stream, Mnemonic_RET, args); +} + +// floating-point instructions +ENCODER_DECLARE_EXPORT char * fld(char * stream, const M_Opnd & m, + bool is_double) { + EncoderBase::Operands args; + // a fake FP register as operand + add_fp(args, 0, is_double); + add_m(args, m, is_double ? size_64 : size_32); + return (char*)EncoderBase::encode(stream, Mnemonic_FLD, args); +} + +ENCODER_DECLARE_EXPORT char * fist(char * stream, const M_Opnd & mem, + bool is_long, bool pop_stk) +{ + EncoderBase::Operands args; + if (pop_stk) { + add_m(args, mem, is_long ? size_64 : size_32); + // a fake FP register as operand + add_fp(args, 0, is_long); + return (char*)EncoderBase::encode(stream, Mnemonic_FISTP, args); + } + // only 32-bit operands are supported + assert(is_long == false); + add_m(args, mem, size_32); + add_fp(args, 0, false); + return (char*)EncoderBase::encode(stream, Mnemonic_FIST, args); +} + +ENCODER_DECLARE_EXPORT char * fst(char * stream, const M_Opnd & m, + bool is_double, bool pop_stk) +{ + EncoderBase::Operands args; + add_m(args, m, is_double ? size_64 : size_32); + // a fake FP register as operand + add_fp(args, 0, is_double); + return (char*)EncoderBase::encode(stream, + pop_stk ? Mnemonic_FSTP : Mnemonic_FST, + args); +} + +ENCODER_DECLARE_EXPORT char * fst(char * stream, unsigned i, bool pop_stk) +{ + EncoderBase::Operands args; + add_fp(args, i, true); + return (char*)EncoderBase::encode(stream, + pop_stk ? Mnemonic_FSTP : Mnemonic_FST, + args); +} + +ENCODER_DECLARE_EXPORT char * fldcw(char * stream, const M_Opnd & mem) { + EncoderBase::Operands args; + add_m(args, mem, size_16); + return (char*)EncoderBase::encode(stream, Mnemonic_FLDCW, args); +} + +ENCODER_DECLARE_EXPORT char * fnstcw(char * stream, const M_Opnd & mem) { + EncoderBase::Operands args; + add_m(args, mem, size_16); + return (char*)EncoderBase::encode(stream, Mnemonic_FNSTCW, args); +} + +ENCODER_DECLARE_EXPORT char * fnstsw(char * stream) +{ + return (char*)EncoderBase::encode(stream, Mnemonic_FNSTCW, + EncoderBase::Operands()); +} + +// string operations +ENCODER_DECLARE_EXPORT char * set_d(char * stream, bool set) { + EncoderBase::Operands args; + return (char*)EncoderBase::encode(stream, + set ? Mnemonic_STD : Mnemonic_CLD, + args); +} + +ENCODER_DECLARE_EXPORT char * scas(char * stream, unsigned char prefix) +{ + EncoderBase::Operands args; + if (prefix != no_prefix) { + assert(prefix == prefix_repnz || prefix == prefix_repz); + *stream = prefix; + ++stream; + } + return (char*)EncoderBase::encode(stream, Mnemonic_SCAS, args); +} + +ENCODER_DECLARE_EXPORT char * stos(char * stream, unsigned char prefix) +{ + if (prefix != no_prefix) { + assert(prefix == prefix_rep); + *stream = prefix; + ++stream; + } + + EncoderBase::Operands args; + return (char*)EncoderBase::encode(stream, Mnemonic_STOS, args); +} + +// Intrinsic FP math functions + +ENCODER_DECLARE_EXPORT char * fprem(char * stream) { + return (char*)EncoderBase::encode(stream, Mnemonic_FPREM, + EncoderBase::Operands()); +} + +ENCODER_DECLARE_EXPORT char * fprem1(char * stream) { + return (char*)EncoderBase::encode(stream, Mnemonic_FPREM1, + EncoderBase::Operands()); +} diff --git a/libpixelflinger/codeflinger/x86/load_store.cpp b/libpixelflinger/codeflinger/x86/load_store.cpp new file mode 100644 index 0000000..a427411 --- /dev/null +++ b/libpixelflinger/codeflinger/x86/load_store.cpp @@ -0,0 +1,458 @@ +/* libs/pixelflinger/codeflinger/x86/load_store.cpp +** +** Copyright 2006, 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 <assert.h> +#include <stdio.h> +#include <cutils/log.h> + +#include "codeflinger/x86/GGLX86Assembler.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +void GGLX86Assembler::store(const pointer_t& addr, const pixel_t& s, uint32_t flags) +{ + const int bits = addr.size; + const int inc = (flags & WRITE_BACK)?1:0; + switch (bits) { + case 32: + if (inc) { + MOV_REG_TO_MEM(s.reg, 0, addr.reg); + ADD_IMM_TO_REG(4, addr.reg); + } else { + MOV_REG_TO_MEM(s.reg, 0, addr.reg); + } + break; + case 24: + // 24 bits formats are a little special and used only for RGB + // 0x00BBGGRR is unpacked as R,G,B + MOV_REG_TO_MEM(s.reg, 0, addr.reg, OpndSize_8); + ROR(8, s.reg); + MOV_REG_TO_MEM(s.reg, 1, addr.reg, OpndSize_8); + ROR(8, s.reg); + MOV_REG_TO_MEM(s.reg, 2, addr.reg, OpndSize_8); + if (!(s.flags & CORRUPTIBLE)) { + ROR(16, s.reg); + } + if (inc) { + ADD_IMM_TO_REG(3, addr.reg); + } + break; + case 16: + if (inc) { + MOV_REG_TO_MEM(s.reg, 0, addr.reg,OpndSize_16); + ADD_IMM_TO_REG(2, addr.reg); + } else { + MOV_REG_TO_MEM(s.reg, 0, addr.reg,OpndSize_16); + } + break; + case 8: + if (inc) { + MOV_REG_TO_MEM(s.reg, 0, addr.reg,OpndSize_8); + ADD_IMM_TO_REG(1, addr.reg); + } else { + MOV_REG_TO_MEM(s.reg, 0, addr.reg,OpndSize_8); + } + break; + } +} + +void GGLX86Assembler::load(pointer_t& addr, const pixel_t& s, uint32_t flags) +{ + Scratch scratches(registerFile()); + int s0; + + const int bits = addr.size; + // WRITE_BACK indicates that the base register will also be updated after loading the data + const int inc = (flags & WRITE_BACK)?1:0; + switch (bits) { + case 32: + if (inc) { + MOV_MEM_TO_REG(0, addr.reg, s.reg); + ADD_IMM_TO_REG(4, addr.reg); + + } else MOV_MEM_TO_REG(0, addr.reg, s.reg); + break; + case 24: + // 24 bits formats are a little special and used only for RGB + // R,G,B is packed as 0x00BBGGRR + s0 = scratches.obtain(); + if (s.reg != addr.reg) { + MOVZX_MEM_TO_REG(OpndSize_8, addr.reg, 0, s.reg); //R + MOVZX_MEM_TO_REG(OpndSize_8, addr.reg, 1, s0); //G + SHL(8, s0); + OR_REG_TO_REG(s0, s.reg); + MOVZX_MEM_TO_REG(OpndSize_8, addr.reg, 2, s0); //B + SHL(16, s0); + OR_REG_TO_REG(s0, s.reg); + } else { + int s1 = scratches.obtain(); + MOVZX_MEM_TO_REG(OpndSize_8, addr.reg, 0, s1); //R + MOVZX_MEM_TO_REG(OpndSize_8, addr.reg, 1, s0); //G + SHL(8, s0); + OR_REG_TO_REG(s0, s1); + MOVZX_MEM_TO_REG(OpndSize_8, addr.reg, 2, s0); //B + SHL(16, s0); + OR_REG_TO_REG(s0, s1); + MOV_REG_TO_REG(s1, s.reg); + scratches.recycle(s1); + + } + scratches.recycle(s0); + if (inc) + ADD_IMM_TO_REG(3, addr.reg); + break; + case 16: + if (inc) { + MOVZX_MEM_TO_REG(OpndSize_16, addr.reg, 0, s.reg); + ADD_IMM_TO_REG(2, addr.reg); + } + else MOVZX_MEM_TO_REG(OpndSize_16, addr.reg, 0, s.reg); + break; + case 8: + if (inc) { + MOVZX_MEM_TO_REG(OpndSize_8, addr.reg, 0, s.reg); + ADD_IMM_TO_REG(1, addr.reg); + } + else MOVZX_MEM_TO_REG(OpndSize_8, addr.reg, 0, s.reg); + break; + } + if (inc) MOV_REG_TO_MEM(addr.reg, addr.offset_ebp, PhysicalReg_EBP); +} + +void GGLX86Assembler::extract(integer_t& d, int s, int h, int l, int bits) +{ + const int maskLen = h-l; + + assert(maskLen<=8); + assert(h); + + + if (h != bits) { + const int mask = ((1<<maskLen)-1) << l; + MOV_REG_TO_REG(s, d.reg); + AND_IMM_TO_REG(mask, d.reg);// component = packed & mask; + s = d.reg; + } + + if (l) { + MOV_REG_TO_REG(s, d.reg); + SHR(l, d.reg);// component = packed >> l; + s = d.reg; + } + + if (s != d.reg) { + MOV_REG_TO_REG(s, d.reg); + } + + d.s = maskLen; +} + +void GGLX86Assembler::extract(integer_t& d, const pixel_t& s, int component) +{ + extract(d, s.reg, + s.format.c[component].h, + s.format.c[component].l, + s.size()); +} + +void GGLX86Assembler::extract(component_t& d, const pixel_t& s, int component) +{ + integer_t r(d.reg, 32, d.flags, d.offset_ebp); + extract(r, s.reg, + s.format.c[component].h, + s.format.c[component].l, + s.size()); + d = component_t(r); +} + + +void GGLX86Assembler::expand(integer_t& d, const component_t& s, int dbits) +{ + if (s.l || (s.flags & CLEAR_HI)) { + extract(d, s.reg, s.h, s.l, 32); + expand(d, d, dbits); + } else { + expand(d, integer_t(s.reg, s.size(), s.flags, s.offset_ebp), dbits); + } +} + +void GGLX86Assembler::expand(component_t& d, const component_t& s, int dbits) +{ + integer_t r(d.reg, 32, d.flags, d.offset_ebp); + expand(r, s, dbits); + d = component_t(r); +} + +void GGLX86Assembler::expand(integer_t& dst, const integer_t& src, int dbits) +{ + assert(src.size()); + + Scratch scratches(registerFile()); + int sbits = src.size(); + int s = src.reg; + int d = dst.reg; + + // be sure to set 'dst' after we read 'src' as they may be identical + dst.s = dbits; + dst.flags = 0; + + if (dbits<=sbits) { + if (s != d) { + MOV_REG_TO_REG(s, d); + } + return; + } + + if (sbits == 1) { + MOV_REG_TO_REG(s, d); + SHL(dbits, d); + SUB_REG_TO_REG(s, d); + // d = (s<<dbits) - s; + return; + } + + if (dbits % sbits) { + MOV_REG_TO_REG(s, d); + SHL(dbits-sbits, d); + // d = s << (dbits-sbits); + dbits -= sbits; + int temp = scratches.obtain(); + do { + MOV_REG_TO_REG(d, temp); + SHR(sbits, temp); + OR_REG_TO_REG(temp, d); + // d |= d >> sbits; + dbits -= sbits; + sbits *= 2; + } while(dbits>0); + return; + } + + dbits -= sbits; + do { + MOV_REG_TO_REG(s, d); + SHL(sbits, d); + OR_REG_TO_REG(s, d); + // d |= d<<sbits; + s = d; + dbits -= sbits; + if (sbits*2 < dbits) { + sbits *= 2; + } + } while(dbits>0); +} + +void GGLX86Assembler::downshift( + pixel_t& d, int component, component_t s, reg_t& dither) +{ + const needs_t& needs = mBuilderContext.needs; + Scratch scratches(registerFile()); + // s(temp) is loaded in build_blending + s.reg = scratches.obtain(); + MOV_MEM_TO_REG(s.offset_ebp, EBP, s.reg); + + int sh = s.h; + int sl = s.l; + int maskHiBits = (sh!=32) ? ((s.flags & CLEAR_HI)?1:0) : 0; + int maskLoBits = (sl!=0) ? ((s.flags & CLEAR_LO)?1:0) : 0; + int sbits = sh - sl; + + int dh = d.format.c[component].h; + int dl = d.format.c[component].l; + int dbits = dh - dl; + int dithering = 0; + + ALOGE_IF(sbits<dbits, "sbits (%d) < dbits (%d) in downshift", sbits, dbits); + + if (sbits>dbits) { + // see if we need to dither + dithering = mDithering; + } + + int ireg = d.reg; + if (!(d.flags & FIRST)) { + if (s.flags & CORRUPTIBLE) { + ireg = s.reg; + } else { + ireg = scratches.obtain(); + } + } + d.flags &= ~FIRST; + + if (maskHiBits) { + // we need to mask the high bits (and possibly the lowbits too) + // and we might be able to use immediate mask. + if (!dithering) { + // we don't do this if we only have maskLoBits because we can + // do it more efficiently below (in the case where dl=0) + const int offset = sh - dbits; + if (dbits<=8 && offset >= 0) { + const uint32_t mask = ((1<<dbits)-1) << offset; + build_and_immediate(ireg, s.reg, mask, 32); + s.reg = ireg; + sl = offset; + sbits = dbits; + maskLoBits = maskHiBits = 0; + } + } else { + // in the dithering case though, we need to preserve the lower bits + const uint32_t mask = ((1<<sbits)-1) << sl; + build_and_immediate(ireg, s.reg, mask, 32); + s.reg = ireg; + maskLoBits = maskHiBits = 0; + } + } + + // XXX: we could special case (maskHiBits & !maskLoBits) + // like we do for maskLoBits below, but it happens very rarely + // that we have maskHiBits only and the conditions necessary to lead + // to better code (like doing d |= s << 24) + + if (maskHiBits) { + MOV_REG_TO_REG(s.reg, ireg); + SHL(32-sh, ireg); + sl += 32-sh; + sh = 32; + s.reg = ireg; + maskHiBits = 0; + } + + // Downsampling should be performed as follows: + // V * ((1<<dbits)-1) / ((1<<sbits)-1) + // V * [(1<<dbits)/((1<<sbits)-1) - 1/((1<<sbits)-1)] + // V * [1/((1<<sbits)-1)>>dbits - 1/((1<<sbits)-1)] + // V/((1<<(sbits-dbits))-(1>>dbits)) - (V>>sbits)/((1<<sbits)-1)>>sbits + // V/((1<<(sbits-dbits))-(1>>dbits)) - (V>>sbits)/(1-(1>>sbits)) + // + // By approximating (1>>dbits) and (1>>sbits) to 0: + // + // V>>(sbits-dbits) - V>>sbits + // + // A good approximation is V>>(sbits-dbits), + // but better one (needed for dithering) is: + // + // (V>>(sbits-dbits)<<sbits - V)>>sbits + // (V<<dbits - V)>>sbits + // (V - V>>dbits)>>(sbits-dbits) + + // Dithering is done here + if (dithering) { + comment("dithering"); + if (sl) { + MOV_REG_TO_REG(s.reg, ireg); + SHR(sl, ireg); + sh -= sl; + sl = 0; + s.reg = ireg; + } + // scaling (V-V>>dbits) + int temp_reg = scratches.obtain(); + MOV_REG_TO_REG(s.reg, temp_reg); + SHR(dbits, temp_reg); + MOV_REG_TO_REG(s.reg, ireg); + SUB_REG_TO_REG(temp_reg, ireg); + scratches.recycle(temp_reg); + const int shift = (GGL_DITHER_BITS - (sbits-dbits)); + dither.reg = scratches.obtain(); + MOV_MEM_TO_REG(dither.offset_ebp, EBP, dither.reg); + if (shift>0) { + temp_reg = scratches.obtain(); + MOV_REG_TO_REG(dither.reg, temp_reg); + SHR(shift, temp_reg); + ADD_REG_TO_REG(temp_reg, ireg); + scratches.recycle(temp_reg); + } + else if (shift<0) { + temp_reg = scratches.obtain(); + MOV_REG_TO_REG(dither.reg, temp_reg); + SHL(-shift, temp_reg); + ADD_REG_TO_REG(temp_reg, ireg); + scratches.recycle(temp_reg); + } + else { + ADD_REG_TO_REG(dither.reg, ireg); + } + scratches.recycle(dither.reg); + s.reg = ireg; + } + + if ((maskLoBits|dithering) && (sh > dbits)) { + int shift = sh-dbits; + if (dl) { + MOV_REG_TO_REG(s.reg, ireg); + SHR(shift, ireg); + if (ireg == d.reg) { + MOV_REG_TO_REG(ireg, d.reg); + SHL(dl, d.reg); + } else { + int temp_reg = scratches.obtain(); + MOV_REG_TO_REG(ireg, temp_reg); + SHL(dl, temp_reg); + OR_REG_TO_REG(temp_reg, d.reg); + scratches.recycle(temp_reg); + } + } else { + if (ireg == d.reg) { + MOV_REG_TO_REG(s.reg, d.reg); + SHR(shift, d.reg); + } else { + int temp_reg = scratches.obtain(); + MOV_REG_TO_REG(s.reg, temp_reg); + SHR(shift, temp_reg); + OR_REG_TO_REG(temp_reg, d.reg); + scratches.recycle(temp_reg); + } + } + } else { + int shift = sh-dh; + if (shift>0) { + if (ireg == d.reg) { + MOV_REG_TO_REG(s.reg, d.reg); + SHR(shift, d.reg); + } else { + int temp_reg = scratches.obtain(); + MOV_REG_TO_REG(s.reg, temp_reg); + SHR(shift, temp_reg); + OR_REG_TO_REG(temp_reg, d.reg); + scratches.recycle(temp_reg); + } + } else if (shift<0) { + if (ireg == d.reg) { + MOV_REG_TO_REG(s.reg, d.reg); + SHL(-shift, d.reg); + } else { + int temp_reg = scratches.obtain(); + MOV_REG_TO_REG(s.reg, temp_reg); + SHL(-shift, temp_reg); + OR_REG_TO_REG(temp_reg, d.reg); + scratches.recycle(temp_reg); + } + } else { + if (ireg == d.reg) { + if (s.reg != d.reg) { + MOV_REG_TO_REG(s.reg, d.reg); + } + } else { + OR_REG_TO_REG(s.reg, d.reg); + } + } + } +} + +}; // namespace android diff --git a/libpixelflinger/codeflinger/x86/texturing.cpp b/libpixelflinger/codeflinger/x86/texturing.cpp new file mode 100644 index 0000000..c02f12b --- /dev/null +++ b/libpixelflinger/codeflinger/x86/texturing.cpp @@ -0,0 +1,1799 @@ +/* libs/pixelflinger/codeflinger/x86/texturing.cpp +** +** Copyright 2006, 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 <assert.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> + +#include <cutils/log.h> + +#include "codeflinger/x86/GGLX86Assembler.h" + + +namespace android { + +// --------------------------------------------------------------------------- + +// iterators are initialized like this: +// (intToFixedCenter(x) * dx)>>16 + x0 +// ((x<<16 + 0x8000) * dx)>>16 + x0 +// ((x<<16)*dx + (0x8000*dx))>>16 + x0 +// ( (x*dx) + dx>>1 ) + x0 +// (x*dx) + (dx>>1 + x0) + +void GGLX86Assembler::init_iterated_color(fragment_parts_t& parts, const reg_t& x) +{ + context_t const* c = mBuilderContext.c; + const needs_t& needs = mBuilderContext.needs; + int temp_reg; + + if (mSmooth) { + // NOTE: we could take this case in the mDithering + !mSmooth case, + // but this would use up to 4 more registers for the color components + // for only a little added quality. + // Currently, this causes the system to run out of registers in + // some case (see issue #719496) + + comment("compute initial iterated color (smooth and/or dither case)"); + + parts.iterated_packed = 0; + parts.packed = 0; + + // 0x1: color component + // 0x2: iterators + //parts.reload = 3; + const int optReload = mOptLevel >> 1; + if (optReload >= 3) parts.reload = 0; // reload nothing + else if (optReload == 2) parts.reload = 2; // reload iterators + else if (optReload == 1) parts.reload = 1; // reload colors + else if (optReload <= 0) parts.reload = 3; // reload both + + if (!mSmooth) { + // we're not smoothing (just dithering), we never have to + // reload the iterators + parts.reload &= ~2; + } + + Scratch scratches(registerFile()); + const int t0 = (parts.reload & 1) ? scratches.obtain() : 0; + const int t1 = (parts.reload & 2) ? scratches.obtain() : 0; + for (int i=0 ; i<4 ; i++) { + if (!mInfo[i].iterated) + continue; + // this component exists in the destination and is not replaced + // by a texture unit. + const int c = (parts.reload & 1) ? t0 : obtainReg(); + if (i==0) CONTEXT_LOAD(c, iterators.ydady); + if (i==1) CONTEXT_LOAD(c, iterators.ydrdy); + if (i==2) CONTEXT_LOAD(c, iterators.ydgdy); + if (i==3) CONTEXT_LOAD(c, iterators.ydbdy); + parts.argb[i].reg = c; + + if (mInfo[i].smooth) { + parts.argb_dx[i].reg = (parts.reload & 2) ? t1 : obtainReg(); + const int dvdx = parts.argb_dx[i].reg; + temp_reg = scratches.obtain(); + CONTEXT_LOAD(dvdx, generated_vars.argb[i].dx); + MOV_REG_TO_REG(dvdx, temp_reg); + IMUL(x.reg, temp_reg); + ADD_REG_TO_REG(temp_reg, c); + scratches.recycle(temp_reg); + + // adjust the color iterator to make sure it won't overflow + if (!mAA) { + // this is not needed when we're using anti-aliasing + // because we will (have to) clamp the components + // anyway. + int end = scratches.obtain(); + MOV_MEM_TO_REG(parts.count.offset_ebp, PhysicalReg_EBP, end); + SHR(16, end); + IMUL(end, dvdx); + temp_reg = end; + // c - (dvdx*end + c) = -(dvdx*end) + MOV_REG_TO_REG(dvdx, temp_reg); + NEG(temp_reg); + ADD_REG_TO_REG(c, dvdx); + CMOV_REG_TO_REG(Mnemonic_CMOVS, temp_reg, c); + /* + SUB_REG_TO_REG(dvdx, temp_reg); + switch(i) { + case 0: + JCC(Mnemonic_JNS, "1f_init_iterated_color"); + SUB_REG_TO_REG(dvdx, c); + label("1f_init_iterated_color"); + break; + case 1: + JCC(Mnemonic_JNS, "2f_init_iterated_color"); + SUB_REG_TO_REG(dvdx, c); + label("2f_init_iterated_color"); + break; + case 2: + JCC(Mnemonic_JNS, "3f_init_iterated_color"); + SUB_REG_TO_REG(dvdx, c); + label("3f_init_iterated_color"); + break; + case 3: + JCC(Mnemonic_JNS, "4f_init_iterated_color"); + SUB_REG_TO_REG(dvdx, c); + label("4f_init_iterated_color"); + break; + } + */ + + MOV_REG_TO_REG(c, temp_reg); + SAR(31, temp_reg); + NOT(temp_reg); + AND_REG_TO_REG(temp_reg, c); + scratches.recycle(end); + } + if(parts.reload & 2) + scratches.recycle(dvdx); + else + recycleReg(dvdx); + } + CONTEXT_STORE(c, generated_vars.argb[i].c); + if(parts.reload & 1) + scratches.recycle(parts.argb[i].reg); + else + recycleReg(parts.argb[i].reg); + + parts.argb[i].reg = -1; + //if (parts.reload & 1) { + // //MOV_MEM_TO_REG(8, PhysicalReg_EBP, mBuilderContext.Rctx); + //} + } + } else { + // We're not smoothed, so we can + // just use a packed version of the color and extract the + // components as needed (or not at all if we don't blend) + + // figure out if we need the iterated color + int load = 0; + for (int i=0 ; i<4 ; i++) { + component_info_t& info = mInfo[i]; + if ((info.inDest || info.needed) && !info.replaced) + load |= 1; + } + + parts.iterated_packed = 1; + parts.packed = (!mTextureMachine.mask && !mBlending + && !mFog && !mDithering); + parts.reload = 0; + if (load || parts.packed) { + if (mBlending || mDithering || mInfo[GGLFormat::ALPHA].needed) { + comment("load initial iterated color (8888 packed)"); + parts.iterated.setTo(obtainReg(), + &(c->formats[GGL_PIXEL_FORMAT_RGBA_8888])); + CONTEXT_LOAD(parts.iterated.reg, packed8888); + } else { + comment("load initial iterated color (dest format packed)"); + + parts.iterated.setTo(obtainReg(), &mCbFormat); + + // pre-mask the iterated color + const int bits = parts.iterated.size(); + const uint32_t size = ((bits>=32) ? 0 : (1LU << bits)) - 1; + uint32_t mask = 0; + if (mMasking) { + for (int i=0 ; i<4 ; i++) { + const int component_mask = 1<<i; + const int h = parts.iterated.format.c[i].h; + const int l = parts.iterated.format.c[i].l; + if (h && (!(mMasking & component_mask))) { + mask |= ((1<<(h-l))-1) << l; + } + } + } + + if (mMasking && ((mask & size)==0)) { + // none of the components are present in the mask + } else { + CONTEXT_LOAD(parts.iterated.reg, packed); + if (mCbFormat.size == 1) { + int imm = 0xFF; + AND_IMM_TO_REG(imm, parts.iterated.reg); + } else if (mCbFormat.size == 2) { + SHR(16, parts.iterated.reg); + } + } + + // pre-mask the iterated color + if (mMasking) { + //AND_IMM_TO_REG(mask, parts.iterated.reg); + build_and_immediate(parts.iterated.reg, parts.iterated.reg, + mask, bits); + } + } + mCurSp = mCurSp - 4; + parts.iterated.offset_ebp = mCurSp; + MOV_REG_TO_MEM(parts.iterated.reg, parts.iterated.offset_ebp, EBP); + //PUSH(parts.iterated.reg); + recycleReg(parts.iterated.reg); + parts.iterated.reg=-1; + } + } +} + +void GGLX86Assembler::build_iterated_color( + component_t& fragment, + fragment_parts_t& parts, + int component, + Scratch& regs) +{ + + if (!mInfo[component].iterated) + return; + + if (parts.iterated_packed) { + // iterated colors are packed, extract the one we need + parts.iterated.reg = regs.obtain(); + MOV_MEM_TO_REG(parts.iterated.offset_ebp, EBP, parts.iterated.reg); + extract(fragment, parts.iterated, component); + regs.recycle(parts.iterated.reg); + } else { + fragment.h = GGL_COLOR_BITS; + fragment.l = GGL_COLOR_BITS - 8; + fragment.flags |= CLEAR_LO; + // iterated colors are held in their own register, + // (smooth and/or dithering case) + Scratch scratches(registerFile()); + mBuilderContext.Rctx = scratches.obtain(); + MOV_MEM_TO_REG(8, EBP, mBuilderContext.Rctx); + if (parts.reload==3) { + // this implies mSmooth + int dx = scratches.obtain(); + CONTEXT_LOAD(fragment.reg, generated_vars.argb[component].c); + CONTEXT_LOAD(dx, generated_vars.argb[component].dx); + ADD_REG_TO_REG(fragment.reg, dx); + CONTEXT_STORE(dx, generated_vars.argb[component].c); + scratches.recycle(dx); + } else if (parts.reload & 1) { + //MOV_MEM_TO_REG(parts.argb[component].offset_ebp, EBP, fragment.reg); + CONTEXT_LOAD(fragment.reg, generated_vars.argb[component].c); + } else { + // we don't reload, so simply rename the register and mark as + // non CORRUPTIBLE so that the texture env or blending code + // won't modify this (renamed) register + //regs.recycle(fragment.reg); + //MOV_MEM_TO_REG(parts.argb[component].offset_ebp, EBP, fragment.reg); + // it will also be used in build_smooth_shade + CONTEXT_LOAD(fragment.reg, generated_vars.argb[component].c); + //fragment.reg = parts.argb[component].reg; + //fragment.flags &= ~CORRUPTIBLE; + } + scratches.recycle(mBuilderContext.Rctx); + if (mInfo[component].smooth && mAA) { + // when using smooth shading AND anti-aliasing, we need to clamp + // the iterators because there is always an extra pixel on the + // edges, which most of the time will cause an overflow + // (since technically its outside of the domain). + int temp = scratches.obtain(); + MOV_REG_TO_REG(fragment.reg, temp); + SAR(31, temp); + NOT(temp); + OR_REG_TO_REG(temp, fragment.reg); + component_sat(fragment, temp); + scratches.recycle(temp); + } + } +} + +// --------------------------------------------------------------------------- + +void GGLX86Assembler::decodeLogicOpNeeds(const needs_t& needs) +{ + // gather some informations about the components we need to process... + const int opcode = GGL_READ_NEEDS(LOGIC_OP, needs.n) | GGL_CLEAR; + switch(opcode) { + case GGL_COPY: + mLogicOp = 0; + break; + case GGL_CLEAR: + case GGL_SET: + mLogicOp = LOGIC_OP; + break; + case GGL_AND: + case GGL_AND_REVERSE: + case GGL_AND_INVERTED: + case GGL_XOR: + case GGL_OR: + case GGL_NOR: + case GGL_EQUIV: + case GGL_OR_REVERSE: + case GGL_OR_INVERTED: + case GGL_NAND: + mLogicOp = LOGIC_OP|LOGIC_OP_SRC|LOGIC_OP_DST; + break; + case GGL_NOOP: + case GGL_INVERT: + mLogicOp = LOGIC_OP|LOGIC_OP_DST; + break; + case GGL_COPY_INVERTED: + mLogicOp = LOGIC_OP|LOGIC_OP_SRC; + break; + }; +} + +void GGLX86Assembler::decodeTMUNeeds(const needs_t& needs, context_t const* c) +{ + uint8_t replaced=0; + mTextureMachine.mask = 0; + mTextureMachine.activeUnits = 0; + for (int i=GGL_TEXTURE_UNIT_COUNT-1 ; i>=0 ; i--) { + texture_unit_t& tmu = mTextureMachine.tmu[i]; + if (replaced == 0xF) { + // all components are replaced, skip this TMU. + tmu.format_idx = 0; + tmu.mask = 0; + tmu.replaced = replaced; + continue; + } + tmu.format_idx = GGL_READ_NEEDS(T_FORMAT, needs.t[i]); + tmu.format = c->formats[tmu.format_idx]; + tmu.bits = tmu.format.size*8; + tmu.swrap = GGL_READ_NEEDS(T_S_WRAP, needs.t[i]); + tmu.twrap = GGL_READ_NEEDS(T_T_WRAP, needs.t[i]); + tmu.env = ggl_needs_to_env(GGL_READ_NEEDS(T_ENV, needs.t[i])); + tmu.pot = GGL_READ_NEEDS(T_POT, needs.t[i]); + tmu.linear = GGL_READ_NEEDS(T_LINEAR, needs.t[i]) + && tmu.format.size!=3; // XXX: only 8, 16 and 32 modes for now + + // 5551 linear filtering is not supported + if (tmu.format_idx == GGL_PIXEL_FORMAT_RGBA_5551) + tmu.linear = 0; + + tmu.mask = 0; + tmu.replaced = replaced; + + if (tmu.format_idx) { + mTextureMachine.activeUnits++; + if (tmu.format.c[0].h) tmu.mask |= 0x1; + if (tmu.format.c[1].h) tmu.mask |= 0x2; + if (tmu.format.c[2].h) tmu.mask |= 0x4; + if (tmu.format.c[3].h) tmu.mask |= 0x8; + if (tmu.env == GGL_REPLACE) { + replaced |= tmu.mask; + } else if (tmu.env == GGL_DECAL) { + if (!tmu.format.c[GGLFormat::ALPHA].h) { + // if we don't have alpha, decal does nothing + tmu.mask = 0; + } else { + // decal always ignores At + tmu.mask &= ~(1<<GGLFormat::ALPHA); + } + } + } + mTextureMachine.mask |= tmu.mask; + ////printf("%d: mask=%08lx, replaced=%08lx\n", + // i, int(tmu.mask), int(tmu.replaced)); + } + mTextureMachine.replaced = replaced; + mTextureMachine.directTexture = 0; + ////printf("replaced=%08lx\n", mTextureMachine.replaced); +} + + +void GGLX86Assembler::init_textures( + tex_coord_t* coords, + const reg_t& x, const reg_t& y) +{ + context_t const* c = mBuilderContext.c; + const needs_t& needs = mBuilderContext.needs; + reg_t temp_reg_t; + int Rx = x.reg; + int Ry = y.reg; + + if (mTextureMachine.mask) { + comment("compute texture coordinates"); + } + + // init texture coordinates for each tmu + const int cb_format_idx = GGL_READ_NEEDS(CB_FORMAT, needs.n); + const bool multiTexture = mTextureMachine.activeUnits > 1; + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT; i++) { + const texture_unit_t& tmu = mTextureMachine.tmu[i]; + if (tmu.format_idx == 0) + continue; + if ((tmu.swrap == GGL_NEEDS_WRAP_11) && + (tmu.twrap == GGL_NEEDS_WRAP_11)) + { + Scratch scratches(registerFile()); + // 1:1 texture + pointer_t& txPtr = coords[i].ptr; + txPtr.setTo(obtainReg(), tmu.bits); + CONTEXT_LOAD(txPtr.reg, state.texture[i].iterators.ydsdy); + SAR(16, txPtr.reg); + ADD_REG_TO_REG(txPtr.reg, Rx); + CONTEXT_LOAD(txPtr.reg, state.texture[i].iterators.ydtdy); + SAR(16, txPtr.reg); + ADD_REG_TO_REG(txPtr.reg, Ry); + // Rx and Ry are changed + // Rx = Rx + ti.iterators.ydsdy>>16 + // Ry = Ry + ti.iterators.ydtdy>>16 + // Rx = Ry * ti.stide + Rx + + // merge base & offset + CONTEXT_LOAD(txPtr.reg, generated_vars.texture[i].stride); + IMUL(Ry, txPtr.reg); + ADD_REG_TO_REG(txPtr.reg, Rx); + + CONTEXT_LOAD(txPtr.reg, generated_vars.texture[i].data); + temp_reg_t.setTo(Rx); + base_offset(txPtr, txPtr, temp_reg_t); + //PUSH(txPtr.reg); + mCurSp = mCurSp - 4; + txPtr.offset_ebp = mCurSp; //ebx, esi, edi, parts.count.reg, parts.cbPtr.reg, parts.z.reg + MOV_REG_TO_MEM(txPtr.reg, txPtr.offset_ebp, EBP); + recycleReg(txPtr.reg); + txPtr.reg=-1; + } else { + Scratch scratches(registerFile()); + reg_t& s = coords[i].s; + reg_t& t = coords[i].t; + // s = (x * dsdx)>>16 + ydsdy + // s = (x * dsdx)>>16 + (y*dsdy)>>16 + s0 + // t = (x * dtdx)>>16 + ydtdy + // t = (x * dtdx)>>16 + (y*dtdy)>>16 + t0 + const int need_w = GGL_READ_NEEDS(W, needs.n); + MOV_MEM_TO_REG(8, PhysicalReg_EBP, mBuilderContext.Rctx); + if (need_w) { + s.setTo(obtainReg()); + t.setTo(obtainReg()); + CONTEXT_LOAD(s.reg, state.texture[i].iterators.ydsdy); + CONTEXT_LOAD(t.reg, state.texture[i].iterators.ydtdy); + CONTEXT_STORE(s.reg, generated_vars.texture[i].spill[0]); + CONTEXT_STORE(t.reg, generated_vars.texture[i].spill[1]); + recycleReg(s.reg); + recycleReg(t.reg); + } else { + int ydsdy = scratches.obtain(); + int dsdx = scratches.obtain(); + CONTEXT_LOAD(ydsdy, state.texture[i].iterators.ydsdy); + CONTEXT_LOAD(dsdx, generated_vars.texture[i].dsdx); + IMUL(Rx, dsdx); + ADD_REG_TO_REG(dsdx, ydsdy); + CONTEXT_STORE(ydsdy, generated_vars.texture[i].spill[0]); + scratches.recycle(ydsdy); + scratches.recycle(dsdx); + + int ydtdy = scratches.obtain(); + int dtdx = scratches.obtain(); + CONTEXT_LOAD(ydtdy, state.texture[i].iterators.ydtdy); + CONTEXT_LOAD(dtdx, generated_vars.texture[i].dtdx); + IMUL(Rx, dtdx); + ADD_REG_TO_REG(dtdx, ydtdy); + CONTEXT_STORE(ydtdy, generated_vars.texture[i].spill[1]); + scratches.recycle(ydtdy); + scratches.recycle(dtdx); + + // s.reg = Rx * ti.dsdx + ydsdy + // t.reg = Rx * ti.dtdx + ydtdy + } + } + + // direct texture? + if (!multiTexture && !mBlending && !mDithering && !mFog && + cb_format_idx == tmu.format_idx && !tmu.linear && + mTextureMachine.replaced == tmu.mask) + { + mTextureMachine.directTexture = i + 1; + } + } +} + +void GGLX86Assembler::build_textures( fragment_parts_t& parts, + Scratch& regs) +{ + context_t const* c = mBuilderContext.c; + const needs_t& needs = mBuilderContext.needs; + reg_t temp_reg_t; + //int Rctx = mBuilderContext.Rctx; + + + const bool multiTexture = mTextureMachine.activeUnits > 1; + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT; i++) { + const texture_unit_t& tmu = mTextureMachine.tmu[i]; + if (tmu.format_idx == 0) + continue; + + pointer_t& txPtr = parts.coords[i].ptr; + pixel_t& texel = parts.texel[i]; + + // repeat... + if ((tmu.swrap == GGL_NEEDS_WRAP_11) && + (tmu.twrap == GGL_NEEDS_WRAP_11)) + { // 1:1 textures + comment("fetch texel"); + texel.setTo(regs.obtain(), &tmu.format); + txPtr.reg = regs.obtain(); + MOV_MEM_TO_REG(txPtr.offset_ebp, EBP, txPtr.reg); + mCurSp = mCurSp - 4; + texel.offset_ebp = mCurSp; + load(txPtr, texel, WRITE_BACK); + MOV_REG_TO_MEM(texel.reg, texel.offset_ebp, EBP); + regs.recycle(texel.reg); + regs.recycle(txPtr.reg); + } else { + Scratch scratches(registerFile()); + reg_t& s = parts.coords[i].s; + reg_t& t = parts.coords[i].t; + comment("reload s/t (multitexture or linear filtering)"); + s.reg = scratches.obtain(); + t.reg = scratches.obtain(); + mBuilderContext.Rctx = scratches.obtain(); + MOV_MEM_TO_REG(8, PhysicalReg_EBP, mBuilderContext.Rctx); + CONTEXT_LOAD(s.reg, generated_vars.texture[i].spill[0]); + CONTEXT_LOAD(t.reg, generated_vars.texture[i].spill[1]); + + comment("compute repeat/clamp"); + int width = scratches.obtain(); + int height = scratches.obtain(); + int U = 0; + int V = 0; + // U and V will be stored onto the stack due to the limited register + reg_t reg_U, reg_V; + + CONTEXT_LOAD(width, generated_vars.texture[i].width); + CONTEXT_LOAD(height, generated_vars.texture[i].height); + scratches.recycle(mBuilderContext.Rctx); + + int FRAC_BITS = 0; + if (tmu.linear) { + // linear interpolation + if (tmu.format.size == 1) { + // for 8-bits textures, we can afford + // 7 bits of fractional precision at no + // additional cost (we can't do 8 bits + // because filter8 uses signed 16 bits muls) + FRAC_BITS = 7; + } else if (tmu.format.size == 2) { + // filter16() is internally limited to 4 bits, so: + // FRAC_BITS=2 generates less instructions, + // FRAC_BITS=3,4,5 creates unpleasant artifacts, + // FRAC_BITS=6+ looks good + FRAC_BITS = 6; + } else if (tmu.format.size == 4) { + // filter32() is internally limited to 8 bits, so: + // FRAC_BITS=4 looks good + // FRAC_BITS=5+ looks better, but generates 3 extra ipp + FRAC_BITS = 6; + } else { + // for all other cases we use 4 bits. + FRAC_BITS = 4; + } + } + int u = scratches.obtain(); + // s.reg and t.reg are recycled in wrapping + wrapping(u, s.reg, width, tmu.swrap, FRAC_BITS, scratches); + int v = scratches.obtain(); + wrapping(v, t.reg, height, tmu.twrap, FRAC_BITS, scratches); + + + if (tmu.linear) { + + //mBuilderContext.Rctx = scratches.obtain(); + //MOV_MEM_TO_REG(8, PhysicalReg_EBP, mBuilderContext.Rctx); + //CONTEXT_LOAD(width, generated_vars.texture[i].width); + //CONTEXT_LOAD(height, generated_vars.texture[i].height); + //scratches.recycle(mBuilderContext.Rctx); + + comment("compute linear filtering offsets"); + // pixel size scale + const int shift = 31 - gglClz(tmu.format.size); + U = scratches.obtain(); + V = scratches.obtain(); + + + // sample the texel center + SUB_IMM_TO_REG(1<<(FRAC_BITS-1), u); + SUB_IMM_TO_REG(1<<(FRAC_BITS-1), v); + + // get the fractionnal part of U,V + MOV_REG_TO_REG(u, U); + AND_IMM_TO_REG((1<<FRAC_BITS)-1, U); + MOV_REG_TO_REG(v, V); + AND_IMM_TO_REG((1<<FRAC_BITS)-1, V); + + // below we will pop U and V in the filter function + mCurSp = mCurSp - 4; + MOV_REG_TO_MEM(U, mCurSp, EBP); + reg_U.offset_ebp = mCurSp; + mCurSp = mCurSp - 4; + MOV_REG_TO_MEM(V, mCurSp, EBP); + reg_V.offset_ebp = mCurSp; + + scratches.recycle(U); + scratches.recycle(V); + + // compute width-1 and height-1 + SUB_IMM_TO_REG(1, width); + SUB_IMM_TO_REG(1, height); + + // the registers are used up + int temp1 = scratches.obtain(); + int temp2 = scratches.obtain(); + // get the integer part of U,V and clamp/wrap + // and compute offset to the next texel + if (tmu.swrap == GGL_NEEDS_WRAP_REPEAT) { + // u has already been REPEATed + SAR(FRAC_BITS, u); + CMOV_REG_TO_REG(Mnemonic_CMOVS, width, u); + MOV_IMM_TO_REG(1<<shift, temp1); + MOV_REG_TO_REG(width, temp2); + // SHL may pollute the CF flag + SHL(shift, temp2); + mCurSp = mCurSp - 4; + int width_offset_ebp = mCurSp; + // width will be changed after the first comparison + MOV_REG_TO_MEM(width, width_offset_ebp, EBP); + CMP_REG_TO_REG(width, u); + CMOV_REG_TO_REG(Mnemonic_CMOVL, temp1, width); + if (shift) { + CMOV_REG_TO_REG(Mnemonic_CMOVGE, temp2, width); + } + MOV_REG_TO_REG(width, temp1); + NEG(temp1); + // width is actually changed + CMP_MEM_TO_REG(EBP, width_offset_ebp, u); + CMOV_REG_TO_REG(Mnemonic_CMOVGE, temp1, width); + } else { + // u has not been CLAMPed yet + // algorithm: + // if ((u>>4) >= width) + // u = width<<4 + // width = 0 + // else + // width = 1<<shift + // u = u>>4; // get integer part + // if (u<0) + // u = 0 + // width = 0 + // generated_vars.rt = width + + MOV_REG_TO_REG(width, temp2); + SHL(FRAC_BITS, temp2); + MOV_REG_TO_REG(u, temp1); + SAR(FRAC_BITS, temp1); + CMP_REG_TO_REG(temp1, width); + CMOV_REG_TO_REG(Mnemonic_CMOVLE, temp2, u); + // mov doesn't affect the flags + MOV_IMM_TO_REG(0, temp2); + CMOV_REG_TO_REG(Mnemonic_CMOVLE, temp2, width); + MOV_IMM_TO_REG(1 << shift, temp2); + CMOV_REG_TO_REG(Mnemonic_CMOVG, temp2, width); + + MOV_IMM_TO_REG(0, temp2); + SAR(FRAC_BITS, u); + CMOV_REG_TO_REG(Mnemonic_CMOVS, temp2, u); + CMOV_REG_TO_REG(Mnemonic_CMOVS, temp2, width); + } + scratches.recycle(temp1); + scratches.recycle(temp2); + mBuilderContext.Rctx = scratches.obtain(); + MOV_MEM_TO_REG(8, PhysicalReg_EBP, mBuilderContext.Rctx); + CONTEXT_STORE(width, generated_vars.rt); + + const int stride = width; + CONTEXT_LOAD(stride, generated_vars.texture[i].stride); + scratches.recycle(mBuilderContext.Rctx); + + temp1 = scratches.obtain(); + temp2 = scratches.obtain(); + + int height_offset_ebp; + if (tmu.twrap == GGL_NEEDS_WRAP_REPEAT) { + // v has already been REPEATed + SAR(FRAC_BITS, v); + CMOV_REG_TO_REG(Mnemonic_CMOVS, height, v); + MOV_IMM_TO_REG(1<<shift, temp1); + MOV_REG_TO_REG(height, temp2); + SHL(shift, temp2); + mCurSp = mCurSp - 4; + height_offset_ebp = mCurSp; + // height will be changed after the first comparison + MOV_REG_TO_MEM(height, height_offset_ebp, EBP); + CMP_REG_TO_REG(height, v); + CMOV_REG_TO_REG(Mnemonic_CMOVL, temp1, height); + if (shift) { + CMOV_REG_TO_REG(Mnemonic_CMOVGE, temp2, height); + } + MOV_REG_TO_REG(height, temp1); + NEG(temp1); + // height is actually changed + CMP_MEM_TO_REG(EBP, height_offset_ebp, v); + CMOV_REG_TO_REG(Mnemonic_CMOVGE, temp1, height); + IMUL(stride, height); + } else { + // u has not been CLAMPed yet + MOV_REG_TO_REG(height, temp2); + SHL(FRAC_BITS, temp2); + MOV_REG_TO_REG(v, temp1); + SAR(FRAC_BITS, temp1); + + mCurSp = mCurSp - 4; + height_offset_ebp = mCurSp; + // height may be changed after the first comparison + MOV_REG_TO_MEM(height, height_offset_ebp, EBP); + + CMP_REG_TO_REG(temp1, height); + CMOV_REG_TO_REG(Mnemonic_CMOVLE, temp2, v); + MOV_IMM_TO_REG(0, temp2); + CMOV_REG_TO_REG(Mnemonic_CMOVLE, temp2, height); + + if (shift) { + // stride = width. It's not used + // shift may pollute the flags + SHL(shift, stride); + // height may be changed to 0 + CMP_REG_TO_MEM(temp1, height_offset_ebp, EBP); + CMOV_REG_TO_REG(Mnemonic_CMOVG, stride, height); + } else { + CMOV_REG_TO_REG(Mnemonic_CMOVG, stride, height); + } + MOV_IMM_TO_REG(0, temp2); + SAR(FRAC_BITS, v); + CMOV_REG_TO_REG(Mnemonic_CMOVS, temp2, v); + CMOV_REG_TO_REG(Mnemonic_CMOVS, temp2, height); + } + scratches.recycle(temp1); + scratches.recycle(temp2); + mBuilderContext.Rctx = scratches.obtain(); + MOV_MEM_TO_REG(8, PhysicalReg_EBP, mBuilderContext.Rctx); + CONTEXT_STORE(height, generated_vars.lb); + scratches.recycle(mBuilderContext.Rctx); + } + + scratches.recycle(width); + scratches.recycle(height); + + // iterate texture coordinates... + comment("iterate s,t"); + int dsdx = scratches.obtain(); + s.reg = scratches.obtain(); + mBuilderContext.Rctx = scratches.obtain(); + MOV_MEM_TO_REG(8, PhysicalReg_EBP, mBuilderContext.Rctx); + CONTEXT_LOAD(dsdx, generated_vars.texture[i].dsdx); + CONTEXT_LOAD(s.reg, generated_vars.texture[i].spill[0]); + ADD_REG_TO_REG(dsdx, s.reg); + CONTEXT_STORE(s.reg, generated_vars.texture[i].spill[0]); + scratches.recycle(s.reg); + scratches.recycle(dsdx); + int dtdx = scratches.obtain(); + t.reg = scratches.obtain(); + CONTEXT_LOAD(dtdx, generated_vars.texture[i].dtdx); + CONTEXT_LOAD(t.reg, generated_vars.texture[i].spill[1]); + ADD_REG_TO_REG(dtdx, t.reg); + CONTEXT_STORE(t.reg, generated_vars.texture[i].spill[1]); + scratches.recycle(dtdx); + scratches.recycle(t.reg); + + // merge base & offset... + comment("merge base & offset"); + texel.setTo(scratches.obtain(), &tmu.format); + //txPtr.setTo(texel.reg, tmu.bits); + txPtr.setTo(scratches.obtain(), tmu.bits); + int stride = scratches.obtain(); + CONTEXT_LOAD(stride, generated_vars.texture[i].stride); + CONTEXT_LOAD(txPtr.reg, generated_vars.texture[i].data); + scratches.recycle(mBuilderContext.Rctx); + MOVSX_REG_TO_REG(OpndSize_16, v, v); + MOVSX_REG_TO_REG(OpndSize_16, stride, stride); + IMUL(v, stride); + ADD_REG_TO_REG(stride, u);// u+v*stride + temp_reg_t.setTo(u); + base_offset(txPtr, txPtr, temp_reg_t); + + // recycle registers we don't need anymore + scratches.recycle(u); + scratches.recycle(v); + scratches.recycle(stride); + + mCurSp = mCurSp - 4; + texel.offset_ebp = mCurSp; + // load texel + if (!tmu.linear) { + comment("fetch texel in building texture"); + load(txPtr, texel, 0); + MOV_REG_TO_MEM(texel.reg, texel.offset_ebp, EBP); + scratches.recycle(texel.reg); + scratches.recycle(txPtr.reg); + } else { + comment("fetch texel, bilinear"); + // the registes are not enough. We spill texel and previous U and V + // texel.reg is recycled in the following functions since there are more than one code path + switch (tmu.format.size) { + case 1: + filter8(parts, texel, tmu, reg_U, reg_V, txPtr, FRAC_BITS, scratches); + break; + case 2: + filter16(parts, texel, tmu, reg_U, reg_V, txPtr, FRAC_BITS, scratches); + break; + case 3: + filter24(parts, texel, tmu, U, V, txPtr, FRAC_BITS); + break; + case 4: + filter32(parts, texel, tmu, reg_U, reg_V, txPtr, FRAC_BITS, scratches); + break; + } + } + } + } +} + +void GGLX86Assembler::build_iterate_texture_coordinates( + const fragment_parts_t& parts) +{ + const bool multiTexture = mTextureMachine.activeUnits > 1; + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT; i++) { + const texture_unit_t& tmu = mTextureMachine.tmu[i]; + if (tmu.format_idx == 0) + continue; + + if ((tmu.swrap == GGL_NEEDS_WRAP_11) && + (tmu.twrap == GGL_NEEDS_WRAP_11)) + { // 1:1 textures + const pointer_t& txPtr = parts.coords[i].ptr; + ADD_IMM_TO_MEM(txPtr.size>>3, txPtr.offset_ebp, EBP); + } else { + Scratch scratches(registerFile()); + int s = parts.coords[i].s.reg; + int t = parts.coords[i].t.reg; + mBuilderContext.Rctx = scratches.obtain(); + MOV_MEM_TO_REG(8, PhysicalReg_EBP, mBuilderContext.Rctx); + s = scratches.obtain(); + int dsdx = scratches.obtain(); + CONTEXT_LOAD(s, generated_vars.texture[i].spill[0]); + CONTEXT_LOAD(dsdx, generated_vars.texture[i].dsdx); + ADD_REG_TO_REG(dsdx, s); + CONTEXT_STORE(s, generated_vars.texture[i].spill[0]); + scratches.recycle(s); + scratches.recycle(dsdx); + int dtdx = scratches.obtain(); + t = scratches.obtain(); + CONTEXT_LOAD(t, generated_vars.texture[i].spill[1]); + CONTEXT_LOAD(dtdx, generated_vars.texture[i].dtdx); + ADD_REG_TO_REG(dtdx, t); + CONTEXT_STORE(t, generated_vars.texture[i].spill[1]); + scratches.recycle(t); + scratches.recycle(dtdx); + } + } +} + +void GGLX86Assembler::filter8( + const fragment_parts_t& parts, + pixel_t& texel, const texture_unit_t& tmu, + reg_t reg_U, reg_t reg_V, pointer_t& txPtr, + int FRAC_BITS, Scratch& scratches) +{ + if (tmu.format.components != GGL_ALPHA && + tmu.format.components != GGL_LUMINANCE) + { + // this is a packed format, and we don't support + // linear filtering (it's probably RGB 332) + // Should not happen with OpenGL|ES + MOVZX_MEM_TO_REG(OpndSize_8, txPtr.reg, 0, texel.reg); + MOV_REG_TO_MEM(texel.reg, texel.offset_ebp, EBP); + scratches.recycle(texel.reg); + scratches.recycle(txPtr.reg); + return; + } + + // ------------------------ + + //int d = scratches.obtain(); + //int u = scratches.obtain(); + //int k = scratches.obtain(); + + scratches.recycle(texel.reg); + int rt = scratches.obtain(); + int lb = scratches.obtain(); + + // RB -> U * V + + mBuilderContext.Rctx = scratches.obtain(); + MOV_MEM_TO_REG(8, EBP, mBuilderContext.Rctx); + CONTEXT_LOAD(rt, generated_vars.rt); + CONTEXT_LOAD(lb, generated_vars.lb); + scratches.recycle(mBuilderContext.Rctx); + int pixel= scratches.obtain(); + + int offset = pixel; + + MOV_REG_TO_REG(rt, offset); + ADD_REG_TO_REG(lb, offset); + + int temp_reg1 = scratches.obtain(); + int temp_reg2 = scratches.obtain(); + // it seems that the address mode with base and scale reg cannot be encoded correctly + //MOV_MEM_SCALE_TO_REG(txPtr.reg, offset, 1, temp_reg1, OpndSize_8); + ADD_REG_TO_REG(txPtr.reg, offset); + MOVZX_MEM_TO_REG(OpndSize_8, offset, 0, temp_reg1); + // pixel is only 8-bits + MOV_REG_TO_REG(temp_reg1, pixel); + MOVSX_MEM_TO_REG(OpndSize_16, EBP, reg_U.offset_ebp, temp_reg1); + MOVSX_MEM_TO_REG(OpndSize_16, EBP, reg_V.offset_ebp, temp_reg2); + IMUL(temp_reg2, temp_reg1); + MOVSX_REG_TO_REG(OpndSize_16, pixel, pixel); + MOVSX_REG_TO_REG(OpndSize_16, temp_reg1, temp_reg2); + IMUL(temp_reg2, pixel); + NEG(temp_reg1); + ADD_IMM_TO_REG(1<<(FRAC_BITS*2), temp_reg1); + mCurSp = mCurSp - 4; + int d_offset_ebp = mCurSp; + MOV_REG_TO_MEM(pixel, d_offset_ebp, EBP); + mCurSp = mCurSp - 4; + int k_offset_ebp = mCurSp; + MOV_REG_TO_MEM(temp_reg1, k_offset_ebp, EBP); + + + // LB -> (1-U) * V + MOV_MEM_TO_REG(reg_U.offset_ebp, EBP, temp_reg2); + NEG(temp_reg2); + ADD_IMM_TO_REG(1<<FRAC_BITS, temp_reg2); + MOV_REG_TO_MEM(temp_reg2, reg_U.offset_ebp, EBP); + + //MOV_MEM_SCALE_TO_REG(txPtr.reg, lb, 1, pixel, OpndSize_8); + ADD_REG_TO_REG(txPtr.reg, lb); + MOVZX_MEM_TO_REG(OpndSize_8, lb, 0, pixel); + + MOVSX_REG_TO_REG(OpndSize_16, temp_reg2, temp_reg2); + MOVSX_MEM_TO_REG(OpndSize_16, EBP, reg_V.offset_ebp, temp_reg1); + IMUL(temp_reg1, temp_reg2); + MOVSX_REG_TO_REG(OpndSize_16, pixel, pixel); + MOVSX_REG_TO_REG(OpndSize_16, temp_reg2, temp_reg1); + IMUL(pixel, temp_reg1); + ADD_REG_TO_MEM(temp_reg1, EBP, d_offset_ebp); + SUB_REG_TO_MEM(temp_reg2, EBP, k_offset_ebp); + + + // LT -> (1-U)*(1-V) + MOV_MEM_TO_REG(reg_V.offset_ebp, EBP, temp_reg2); + NEG(temp_reg2); + ADD_IMM_TO_REG(1<<FRAC_BITS, temp_reg2); + MOV_REG_TO_MEM(temp_reg2, reg_V.offset_ebp, EBP); + + MOVZX_MEM_TO_REG(OpndSize_8, txPtr.reg, 0, pixel); + + MOVSX_MEM_TO_REG(OpndSize_16, EBP, reg_U.offset_ebp, temp_reg1); + MOVSX_REG_TO_REG(OpndSize_16, temp_reg2, temp_reg2); + IMUL(temp_reg1, temp_reg2); + MOVSX_REG_TO_REG(OpndSize_16, temp_reg2, temp_reg1); + MOVSX_REG_TO_REG(OpndSize_16, pixel, pixel); + IMUL(pixel, temp_reg1); + ADD_REG_TO_MEM(temp_reg1, EBP, d_offset_ebp); + + // RT -> U*(1-V) + //MOV_MEM_SCALE_TO_REG(txPtr.reg, rt, 1, pixel, OpndSize_8); + ADD_REG_TO_REG(txPtr.reg, rt); + MOVZX_MEM_TO_REG(OpndSize_8, rt, 0, pixel); + + int k = rt; + MOV_MEM_TO_REG(k_offset_ebp, EBP, k); + SUB_REG_TO_REG(temp_reg2, k); + MOVSX_REG_TO_REG(OpndSize_16, pixel, pixel); + MOVSX_REG_TO_REG(OpndSize_16, k, k); + IMUL(pixel, k); + ADD_MEM_TO_REG(EBP, d_offset_ebp, k); + MOV_REG_TO_MEM(k, texel.offset_ebp, EBP); + scratches.recycle(rt); + scratches.recycle(lb); + scratches.recycle(pixel); + scratches.recycle(txPtr.reg); + scratches.recycle(temp_reg1); + scratches.recycle(temp_reg2); + for (int i=0 ; i<4 ; i++) { + if (!texel.format.c[i].h) continue; + texel.format.c[i].h = FRAC_BITS*2+8; + texel.format.c[i].l = FRAC_BITS*2; // keeping 8 bits in enough + } + texel.format.size = 4; + texel.format.bitsPerPixel = 32; + texel.flags |= CLEAR_LO; +} + +void GGLX86Assembler::filter16( + const fragment_parts_t& parts, + pixel_t& texel, const texture_unit_t& tmu, + reg_t reg_U, reg_t reg_V, pointer_t& txPtr, + int FRAC_BITS, Scratch& scratches) +{ + // compute the mask + // XXX: it would be nice if the mask below could be computed + // automatically. + uint32_t mask = 0; + int shift = 0; + int prec = 0; + switch (tmu.format_idx) { + case GGL_PIXEL_FORMAT_RGB_565: + // source: 00000ggg.ggg00000 | rrrrr000.000bbbbb + // result: gggggggg.gggrrrrr | rrrrr0bb.bbbbbbbb + mask = 0x07E0F81F; + shift = 16; + prec = 5; + break; + case GGL_PIXEL_FORMAT_RGBA_4444: + // 0000,1111,0000,1111 | 0000,1111,0000,1111 + mask = 0x0F0F0F0F; + shift = 12; + prec = 4; + break; + case GGL_PIXEL_FORMAT_LA_88: + // 0000,0000,1111,1111 | 0000,0000,1111,1111 + // AALL -> 00AA | 00LL + mask = 0x00FF00FF; + shift = 8; + prec = 8; + break; + default: + // unsupported format, do something sensical... + ALOGE("Unsupported 16-bits texture format (%d)", tmu.format_idx); + MOVZX_MEM_TO_REG(OpndSize_16, txPtr.reg, 0, texel.reg); + MOV_REG_TO_MEM(texel.reg, texel.offset_ebp, EBP); + scratches.recycle(texel.reg); + scratches.recycle(txPtr.reg); + return; + } + + const int adjust = FRAC_BITS*2 - prec; + const int round = 0; + + // update the texel format + texel.format.size = 4; + texel.format.bitsPerPixel = 32; + texel.flags |= CLEAR_HI|CLEAR_LO; + for (int i=0 ; i<4 ; i++) { + if (!texel.format.c[i].h) continue; + const uint32_t offset = (mask & tmu.format.mask(i)) ? 0 : shift; + texel.format.c[i].h = tmu.format.c[i].h + offset + prec; + texel.format.c[i].l = texel.format.c[i].h - (tmu.format.bits(i) + prec); + } + + // ------------------------ + + scratches.recycle(texel.reg); + + int pixel= scratches.obtain(); + int u = scratches.obtain(); + int temp_reg1 = scratches.obtain(); + + // RB -> U * V + //printf("RB -> U * V \n"); + int offset = pixel; + mBuilderContext.Rctx = scratches.obtain(); + MOV_MEM_TO_REG(8, EBP, mBuilderContext.Rctx); + CONTEXT_LOAD(offset, generated_vars.rt); + CONTEXT_LOAD(u, generated_vars.lb); + ADD_REG_TO_REG(u, offset); + + //MOV_MEM_SCALE_TO_REG(txPtr.reg, offset, 1, temp_reg1, OpndSize_16); + ADD_REG_TO_REG(txPtr.reg, offset); + MOVZX_MEM_TO_REG(OpndSize_16, offset, 0, temp_reg1); + + MOV_REG_TO_REG(temp_reg1, pixel); + + MOVSX_MEM_TO_REG(OpndSize_16, EBP, reg_U.offset_ebp, u); + MOVSX_MEM_TO_REG(OpndSize_16, EBP, reg_V.offset_ebp, temp_reg1); + IMUL(temp_reg1, u); + MOV_REG_TO_REG(pixel, temp_reg1); + SHL(shift, temp_reg1); + OR_REG_TO_REG(temp_reg1, pixel); + build_and_immediate(pixel, pixel, mask, 32); + if (adjust) { + if (round) + ADD_IMM_TO_REG(1<<(adjust-1), u); + SHR(adjust, u); + } + int d = scratches.obtain(); + MOV_REG_TO_REG(u, d); + IMUL(pixel, d); + NEG(u); + ADD_IMM_TO_REG(1<<prec, u); + + + // LB -> (1-U) * V + //printf("LB -> (1- U) * V \n"); + MOV_MEM_TO_REG(reg_U.offset_ebp, EBP, temp_reg1); + NEG(temp_reg1); + ADD_IMM_TO_REG(1<<FRAC_BITS, temp_reg1); + MOV_REG_TO_MEM(temp_reg1, reg_U.offset_ebp, EBP); + MOVSX_REG_TO_REG(OpndSize_16, temp_reg1, temp_reg1); + + CONTEXT_LOAD(offset, generated_vars.lb); + scratches.recycle(mBuilderContext.Rctx); + //MOV_MEM_SCALE_TO_REG(txPtr.reg, offset, 1, pixel, OpndSize_16); + ADD_REG_TO_REG(txPtr.reg, offset); + MOVZX_MEM_TO_REG(OpndSize_16, offset, 0, pixel); + + int temp_reg2 = scratches.obtain(); + MOVSX_MEM_TO_REG(OpndSize_16, EBP, reg_V.offset_ebp, temp_reg2); + IMUL(temp_reg1, temp_reg2); + MOV_REG_TO_REG(pixel, temp_reg1); + SHL(shift, temp_reg1); + OR_REG_TO_REG(temp_reg1, pixel); + build_and_immediate(pixel, pixel, mask, 32); + if (adjust) { + if (round) + ADD_IMM_TO_REG(1<<(adjust-1), temp_reg2); + SHR(adjust, temp_reg2); + } + IMUL(temp_reg2, pixel); + ADD_REG_TO_REG(pixel, d); + SUB_REG_TO_REG(temp_reg2, u); + + + // LT -> (1-U)*(1-V) + //printf("LT -> (1- U)*(1-V) \n"); + MOVSX_MEM_TO_REG(OpndSize_16, EBP, reg_V.offset_ebp, temp_reg2); + NEG(temp_reg2); + ADD_IMM_TO_REG(1<<FRAC_BITS, temp_reg2); + MOV_REG_TO_MEM(temp_reg2, reg_V.offset_ebp, EBP); + MOVZX_MEM_TO_REG(OpndSize_16, txPtr.reg, 0, pixel); + MOVSX_MEM_TO_REG(OpndSize_16, EBP, reg_U.offset_ebp, temp_reg1); + IMUL(temp_reg1, temp_reg2); + MOV_REG_TO_REG(pixel, temp_reg1); + SHL(shift, temp_reg1); + OR_REG_TO_REG(temp_reg1, pixel); + build_and_immediate(pixel, pixel, mask, 32); + if (adjust) { + if (round) + ADD_IMM_TO_REG(1<<(adjust-1), temp_reg2); + SHR(adjust, temp_reg2); + } + IMUL(temp_reg2, pixel); + ADD_REG_TO_REG(pixel, d); + + + // RT -> U*(1-V) + //printf("RT -> U*(1-V) \n"); + SUB_REG_TO_REG(temp_reg2, u); + mBuilderContext.Rctx = temp_reg2; + MOV_MEM_TO_REG(8, EBP, mBuilderContext.Rctx); + CONTEXT_LOAD(temp_reg1, generated_vars.rt); + //MOV_MEM_SCALE_TO_REG(txPtr.reg, temp_reg1, 1, pixel, OpndSize_16); + ADD_REG_TO_REG(txPtr.reg, temp_reg1); + MOVZX_MEM_TO_REG(OpndSize_16, temp_reg1, 0, pixel); + + MOV_REG_TO_REG(pixel, temp_reg1); + SHL(shift, temp_reg1); + OR_REG_TO_REG(temp_reg1, pixel); + build_and_immediate(pixel, pixel, mask, 32); + IMUL(u, pixel); + ADD_REG_TO_REG(pixel, d); + MOV_REG_TO_MEM(d, texel.offset_ebp, EBP); + scratches.recycle(d); + scratches.recycle(pixel); + scratches.recycle(u); + scratches.recycle(txPtr.reg); + scratches.recycle(temp_reg1); + scratches.recycle(temp_reg2); +} + +void GGLX86Assembler::filter24( + const fragment_parts_t& parts, + pixel_t& texel, const texture_unit_t& tmu, + int U, int V, pointer_t& txPtr, + int FRAC_BITS) +{ + // not supported yet (currently disabled) + load(txPtr, texel, 0); +} + +void GGLX86Assembler::filter32( + const fragment_parts_t& parts, + pixel_t& texel, const texture_unit_t& tmu, + reg_t reg_U, reg_t reg_V, pointer_t& txPtr, + int FRAC_BITS, Scratch& scratches) +{ + const int adjust = FRAC_BITS*2 - 8; + const int round = 0; + + // ------------------------ + scratches.recycle(texel.reg); + int mask = scratches.obtain(); + int pixel= scratches.obtain(); + int u = scratches.obtain(); + + //int dh = scratches.obtain(); + //int k = scratches.obtain(); + //int temp = scratches.obtain(); + //int dl = scratches.obtain(); + + MOV_IMM_TO_REG(0xFF, mask); + OR_IMM_TO_REG(0xFF0000, mask); + + // RB -> U * V + int offset = pixel; + mBuilderContext.Rctx = scratches.obtain(); + MOV_MEM_TO_REG(8, EBP, mBuilderContext.Rctx); + CONTEXT_LOAD(offset, generated_vars.rt); + CONTEXT_LOAD(u, generated_vars.lb); + ADD_REG_TO_REG(u, offset); + scratches.recycle(mBuilderContext.Rctx); + + //MOV_MEM_SCALE_TO_REG(txPtr.reg, offset, 1, u); + ADD_REG_TO_REG(txPtr.reg, offset); + MOV_MEM_TO_REG(0, offset, u); + + MOV_REG_TO_REG(u, pixel); + + int temp_reg1 = scratches.obtain(); + MOVSX_MEM_TO_REG(OpndSize_16, EBP, reg_U.offset_ebp, temp_reg1); + MOVSX_MEM_TO_REG(OpndSize_16, EBP, reg_V.offset_ebp, u); + IMUL(temp_reg1, u); + MOV_REG_TO_REG(mask, temp_reg1); + AND_REG_TO_REG(pixel, temp_reg1); + if (adjust) { + if (round) + ADD_IMM_TO_REG(1<<(adjust-1), u); + SHR(adjust, u); + } + int temp_reg2 = scratches.obtain(); + MOV_REG_TO_REG(temp_reg1, temp_reg2); + IMUL(u, temp_reg2); + SHR(8, pixel); + AND_REG_TO_REG(mask, pixel); + IMUL(u, pixel); + NEG(u); + ADD_IMM_TO_REG(0x100, u); + mCurSp = mCurSp - 4; + int dh_offset_ebp = mCurSp; + MOV_REG_TO_MEM(temp_reg2, dh_offset_ebp, EBP); + mCurSp = mCurSp - 4; + int dl_offset_ebp = mCurSp; + MOV_REG_TO_MEM(pixel, dl_offset_ebp, EBP); + + // LB -> (1-U) * V + mBuilderContext.Rctx = temp_reg2; + MOV_MEM_TO_REG(8, EBP, mBuilderContext.Rctx); + CONTEXT_LOAD(offset, generated_vars.lb); + //MOV_MEM_SCALE_TO_REG(txPtr.reg, offset, 1, temp_reg2); + ADD_REG_TO_REG(txPtr.reg, offset); + MOV_MEM_TO_REG(0, offset, temp_reg2); + + MOV_REG_TO_REG(temp_reg2, pixel); + MOV_MEM_TO_REG(reg_U.offset_ebp, EBP, temp_reg1); + NEG(temp_reg1); + ADD_IMM_TO_REG(1<<FRAC_BITS, temp_reg1); + MOV_REG_TO_MEM(temp_reg1, reg_U.offset_ebp, EBP); + MOVSX_REG_TO_REG(OpndSize_16, temp_reg1, temp_reg1); + MOVSX_MEM_TO_REG(OpndSize_16, EBP, reg_V.offset_ebp, temp_reg2); + IMUL(temp_reg2, temp_reg1); + MOV_REG_TO_REG(mask, temp_reg2); + AND_REG_TO_REG(pixel, temp_reg2); + if (adjust) { + if (round) + ADD_IMM_TO_REG(1<<(adjust-1), temp_reg1); + SHR(adjust, temp_reg1); + } + // if we use push and pop txPtr.reg later, It will cause the bad locality, since the esp is already been subtracted before the loop. + // we will spill txPtr.reg due to the limited register + mCurSp = mCurSp - 4; + int txPtr_offset_ebp = mCurSp; + MOV_REG_TO_MEM(txPtr.reg, txPtr_offset_ebp, EBP); + //PUSH(txPtr.reg); + + int temp_reg3 = txPtr.reg; + MOV_REG_TO_REG(temp_reg2, temp_reg3); + IMUL(temp_reg1, temp_reg3); + ADD_REG_TO_MEM(temp_reg3, EBP, dh_offset_ebp); + SHR(8, pixel); + AND_REG_TO_REG(mask, pixel); + IMUL(temp_reg1, pixel); + ADD_REG_TO_MEM(pixel, EBP, dl_offset_ebp); + SUB_REG_TO_REG(temp_reg1, u); + + + // LT -> (1-U)*(1-V) + MOV_MEM_TO_REG(reg_V.offset_ebp, EBP, temp_reg1); + NEG(temp_reg1); + ADD_IMM_TO_REG(1<<FRAC_BITS, temp_reg1); + MOV_REG_TO_MEM(temp_reg1, reg_V.offset_ebp, EBP); + MOV_MEM_TO_REG(reg_U.offset_ebp, EBP, temp_reg2); + + MOV_MEM_TO_REG(txPtr_offset_ebp, EBP, txPtr.reg); + //POP(txPtr.reg); + + MOV_MEM_TO_REG(0, txPtr.reg, pixel); + IMUL(temp_reg2, temp_reg1); + //we have already saved txPtr.reg + temp_reg3 = txPtr.reg; + MOV_REG_TO_REG(pixel, temp_reg3); + AND_REG_TO_REG(mask, temp_reg3); + if (adjust) { + if (round) + ADD_IMM_TO_REG(1<<(adjust-1), temp_reg1); + SHR(adjust, temp_reg1); + } + IMUL(temp_reg1, temp_reg3); + ADD_REG_TO_MEM(temp_reg3, EBP, dh_offset_ebp); + SHR(8, pixel); + AND_REG_TO_REG(mask, pixel); + IMUL(temp_reg1, pixel); + ADD_REG_TO_MEM(pixel, EBP, dl_offset_ebp); + + // RT -> U*(1-V) + SUB_REG_TO_REG(temp_reg1, u); + mBuilderContext.Rctx = temp_reg2; + MOV_MEM_TO_REG(8, EBP, mBuilderContext.Rctx); + CONTEXT_LOAD(offset, generated_vars.rt); + + MOV_MEM_TO_REG(txPtr_offset_ebp, EBP, txPtr.reg); + //POP(txPtr.reg); + + //MOV_MEM_SCALE_TO_REG(txPtr.reg, offset, 1, temp_reg2); + ADD_REG_TO_REG(txPtr.reg, offset); + MOV_MEM_TO_REG(0, offset, temp_reg2); + + MOV_REG_TO_REG(temp_reg2, pixel); + AND_REG_TO_REG(mask, temp_reg2); + IMUL(u, temp_reg2); + ADD_REG_TO_MEM(temp_reg2, EBP, dh_offset_ebp); + SHR(8, pixel); + AND_REG_TO_REG(mask, pixel); + IMUL(u, pixel); + ADD_REG_TO_MEM(pixel, EBP, dl_offset_ebp); + MOV_MEM_TO_REG(dh_offset_ebp, EBP, temp_reg1); + MOV_MEM_TO_REG(dl_offset_ebp, EBP, temp_reg2); + SHR(8, temp_reg1); + AND_REG_TO_REG(mask, temp_reg1); + SHL(8, mask); + AND_REG_TO_REG(mask, temp_reg2); + OR_REG_TO_REG(temp_reg1, temp_reg2); + MOV_REG_TO_MEM(temp_reg2, texel.offset_ebp, EBP); + scratches.recycle(u); + scratches.recycle(mask); + scratches.recycle(pixel); + scratches.recycle(txPtr.reg); + scratches.recycle(temp_reg1); + scratches.recycle(temp_reg2); + +} + +void GGLX86Assembler::build_texture_environment( + component_t& fragment, + fragment_parts_t& parts, + int component, + Scratch& regs) +{ + const uint32_t component_mask = 1<<component; + const bool multiTexture = mTextureMachine.activeUnits > 1; + Scratch scratches(registerFile()); + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + texture_unit_t& tmu = mTextureMachine.tmu[i]; + + if (tmu.mask & component_mask) { + // replace or modulate with this texture + if ((tmu.replaced & component_mask) == 0) { + // not replaced by a later tmu... + + pixel_t texel(parts.texel[i]); + if (multiTexture && + tmu.swrap == GGL_NEEDS_WRAP_11 && + tmu.twrap == GGL_NEEDS_WRAP_11) + { + texel.reg = scratches.obtain(); + texel.flags |= CORRUPTIBLE; + mCurSp = mCurSp - 4; + texel.offset_ebp = mCurSp; + comment("fetch texel (multitexture 1:1)"); + parts.coords[i].ptr.reg = scratches.obtain(); + MOV_MEM_TO_REG(parts.coords[i].ptr.offset_ebp, EBP, parts.coords[i].ptr.reg); + load(parts.coords[i].ptr, texel, WRITE_BACK); + MOV_REG_TO_MEM(texel.reg, texel.offset_ebp, EBP); + scratches.recycle(parts.coords[i].ptr.reg); + } else { + // the texel is already loaded in building textures + texel.reg = scratches.obtain(); + MOV_MEM_TO_REG(texel.offset_ebp, EBP, texel.reg); + } + + component_t incoming(fragment); + modify(fragment, regs); + + switch (tmu.env) { + case GGL_REPLACE: + extract(fragment, texel, component); + break; + case GGL_MODULATE: + modulate(fragment, incoming, texel, component); + break; + case GGL_DECAL: + decal(fragment, incoming, texel, component); + break; + case GGL_BLEND: + blend(fragment, incoming, texel, component, i); + break; + case GGL_ADD: + add(fragment, incoming, texel, component); + break; + } + scratches.recycle(texel.reg); + } + } + } +} + +// --------------------------------------------------------------------------- + +void GGLX86Assembler::wrapping( + int d, + int coord, int size, + int tx_wrap, int tx_linear, Scratch& scratches) +{ + // coord is recycled after return, so it can be written. + // notes: + // if tx_linear is set, we need 4 extra bits of precision on the result + // SMULL/UMULL is 3 cycles + // coord is actually s.reg or t.reg which will not be used + int c = coord; + if (tx_wrap == GGL_NEEDS_WRAP_REPEAT) { + // UMULL takes 4 cycles (interlocked), and we can get away with + // 2 cycles using SMULWB, but we're loosing 16 bits of precision + // out of 32 (this is not a problem because the iterator keeps + // its full precision) + // UMULL(AL, 0, size, d, c, size); + // note: we can't use SMULTB because it's signed. + MOV_REG_TO_REG(c, d); + SHR(16-tx_linear, d); + int temp_reg; + if(c != EDX) + temp_reg = c; + else { + temp_reg = scratches.obtain(); + scratches.recycle(c); + } + int flag_push_edx = -1; + int flag_reserve_edx = -1; + int edx_offset_ebp = 0; + if(scratches.isUsed(EDX) == 1) { //not indicates that the registers are used up. Probably, previous allocated registers are recycled + if((d != EDX) && (size != EDX)) { + flag_push_edx = 1; + mCurSp = mCurSp - 4; + edx_offset_ebp = mCurSp; + MOV_REG_TO_MEM(EDX, edx_offset_ebp, EBP); + //PUSH(EDX); + } + } + else { + flag_reserve_edx = 1; + scratches.reserve(EDX); + } + if(scratches.isUsed(EAX)) { + if( size == EAX || d == EAX) { + // size is actually width and height, which will probably be used after wrapping + MOV_REG_TO_REG(size, temp_reg); + MOVSX_REG_TO_REG(OpndSize_16, size, size); + if(size == EAX) + IMUL(d); + else + IMUL(size); + SHL(16, EDX); + SHR(16, EAX); + MOV_REG_TO_REG(EAX, EDX, OpndSize_16); + MOV_REG_TO_REG(EDX, d); + + MOV_REG_TO_REG(temp_reg, size); + } + else { + if(temp_reg != EAX) + MOV_REG_TO_REG(EAX, temp_reg); + MOV_REG_TO_REG(size, EAX); + MOVSX_REG_TO_REG(OpndSize_16, EAX, EAX); + IMUL(d); + SHL(16, EDX); + SHR(16, EAX); + MOV_REG_TO_REG(EAX, EDX, OpndSize_16); + MOV_REG_TO_REG(EDX, d); + if(temp_reg != EAX) + MOV_REG_TO_REG(temp_reg, EAX); + } + } + else { + MOV_REG_TO_REG(size, EAX); + MOVSX_REG_TO_REG(OpndSize_16, EAX, EAX); + IMUL(d); + SHL(16, EDX); + SHR(16, EAX); + MOV_REG_TO_REG(EAX, EDX, OpndSize_16); + MOV_REG_TO_REG(EDX, d); + } + if(flag_push_edx == 1) { + MOV_MEM_TO_REG(edx_offset_ebp, EBP, EDX); + //POP(EDX); + } + if(flag_reserve_edx ==1) + scratches.recycle(EDX); + + scratches.recycle(temp_reg); + //IMUL(size, d) will cause segmentation fault with GlobalTime + } else if (tx_wrap == GGL_NEEDS_WRAP_CLAMP_TO_EDGE) { + if (tx_linear) { + // 1 cycle + MOV_REG_TO_REG(coord, d); + SAR(16-tx_linear, d); + } else { + SAR(16, coord); + MOV_REG_TO_REG(coord, d); + SAR(31, coord); + NOT(coord); + AND_REG_TO_REG(coord, d); + + MOV_REG_TO_REG(size, coord); + SUB_IMM_TO_REG(1, coord); + + CMP_REG_TO_REG(size, d); + CMOV_REG_TO_REG(Mnemonic_CMOVGE, coord, d); + + } + scratches.recycle(coord); + } +} + +// --------------------------------------------------------------------------- + +void GGLX86Assembler::modulate( + component_t& dest, + const component_t& incoming, + const pixel_t& incomingTexel, int component) +{ + Scratch locals(registerFile()); + integer_t texel(locals.obtain(), 32, CORRUPTIBLE); + extract(texel, incomingTexel, component); + + const int Nt = texel.size(); + // Nt should always be less than 10 bits because it comes + // from the TMU. + + int Ni = incoming.size(); + // Ni could be big because it comes from previous MODULATEs + + if (Nt == 1) { + // texel acts as a bit-mask + // dest = incoming & ((texel << incoming.h)-texel) + MOV_REG_TO_REG(texel.reg, dest.reg); + SHL(incoming.h, dest.reg); + SUB_REG_TO_REG(texel.reg, dest.reg); + dest.l = incoming.l; + dest.h = incoming.h; + dest.flags |= (incoming.flags & CLEAR_LO); + } else if (Ni == 1) { + SHL(31-incoming.h, incoming.reg); + MOV_REG_TO_REG(incoming.reg, dest.reg); + SAR(31, dest.reg); + AND_REG_TO_REG(texel.reg, dest.reg); + dest.l = 0; + dest.h = Nt; + } else { + int inReg = incoming.reg; + int shift = incoming.l; + if ((Nt + Ni) > 32) { + // we will overflow, reduce the precision of Ni to 8 bits + // (Note Nt cannot be more than 10 bits which happens with + // 565 textures and GGL_LINEAR) + shift += Ni-8; + Ni = 8; + } + + // modulate by the component with the lowest precision + if (Nt >= Ni) { + if (shift) { + // XXX: we should be able to avoid this shift + // when shift==16 && Nt<16 && Ni<16, in which + // we could use SMULBT below. + MOV_REG_TO_REG(inReg, dest.reg); + SHR(shift, inReg); + inReg = dest.reg; + shift = 0; + } + int temp_reg = locals.obtain(); + // operation: (Cf*Ct)/((1<<Ni)-1) + // approximated with: Cf*(Ct + Ct>>(Ni-1))>>Ni + // this operation doesn't change texel's size + MOV_REG_TO_REG(inReg, temp_reg); + SHR(Ni-1, temp_reg); + MOV_REG_TO_REG(inReg, dest.reg); + ADD_REG_TO_REG(temp_reg, dest.reg); + locals.recycle(temp_reg); + if (Nt<16 && Ni<16) { + MOVSX_REG_TO_REG(OpndSize_16, texel.reg, texel.reg); + MOVSX_REG_TO_REG(OpndSize_16, dest.reg, dest.reg); + IMUL(texel.reg, dest.reg); + } + else + IMUL(texel.reg, dest.reg); + dest.l = Ni; + dest.h = Nt + Ni; + } else { + if (shift && (shift != 16)) { + // if shift==16, we can use 16-bits mul instructions later + MOV_REG_TO_REG(inReg, dest.reg); + SHR(shift, dest.reg); + inReg = dest.reg; + shift = 0; + } + // operation: (Cf*Ct)/((1<<Nt)-1) + // approximated with: Ct*(Cf + Cf>>(Nt-1))>>Nt + // this operation doesn't change incoming's size + Scratch scratches(registerFile()); + int temp_reg = locals.obtain(); + int t = (texel.flags & CORRUPTIBLE) ? texel.reg : dest.reg; + if (t == inReg) + t = scratches.obtain(); + + MOV_REG_TO_REG(texel.reg, temp_reg); + SHR(Nt-1, temp_reg); + ADD_REG_TO_REG(temp_reg, texel.reg); + MOV_REG_TO_REG(texel.reg, t); + locals.recycle(temp_reg); + MOV_REG_TO_REG(inReg, dest.reg); + if (Nt<16 && Ni<16) { + if (shift==16) { + MOVSX_REG_TO_REG(OpndSize_16, t, t); + SHR(16, dest.reg); + MOVSX_REG_TO_REG(OpndSize_16, dest.reg, dest.reg); + IMUL(t, dest.reg); + } + else { + MOVSX_REG_TO_REG(OpndSize_16, dest.reg, dest.reg); + MOVSX_REG_TO_REG(OpndSize_16, t, t); + IMUL(t, dest.reg); + } + } else + IMUL(t, dest.reg); + dest.l = Nt; + dest.h = Nt + Ni; + } + + // low bits are not valid + dest.flags |= CLEAR_LO; + + // no need to keep more than 8 bits/component + if (dest.size() > 8) + dest.l = dest.h-8; + } +} + +void GGLX86Assembler::decal( + component_t& dest, + const component_t& incoming, + const pixel_t& incomingTexel, int component) +{ + // RGBA: + // Cv = Cf*(1 - At) + Ct*At = Cf + (Ct - Cf)*At + // Av = Af + Scratch locals(registerFile()); + integer_t texel(locals.obtain(), 32, CORRUPTIBLE); + integer_t factor(locals.obtain(), 32, CORRUPTIBLE); + extract(texel, incomingTexel, component); + extract(factor, incomingTexel, GGLFormat::ALPHA); + + // no need to keep more than 8-bits for decal + int Ni = incoming.size(); + int shift = incoming.l; + if (Ni > 8) { + shift += Ni-8; + Ni = 8; + } + integer_t incomingNorm(incoming.reg, Ni, incoming.flags); + if (shift) { + SHR(shift, incomingNorm.reg); + MOV_REG_TO_REG(incomingNorm.reg, dest.reg); + incomingNorm.reg = dest.reg; + incomingNorm.flags |= CORRUPTIBLE; + } + int temp = locals.obtain(); + MOV_REG_TO_REG(factor.reg, temp); + SHR(factor.s-1, temp); + ADD_REG_TO_REG(temp, factor.reg); + locals.recycle(temp); + build_blendOneMinusFF(dest, factor, incomingNorm, texel); +} + +void GGLX86Assembler::blend( + component_t& dest, + const component_t& incoming, + const pixel_t& incomingTexel, int component, int tmu) +{ + // RGBA: + // Cv = (1 - Ct)*Cf + Ct*Cc = Cf + (Cc - Cf)*Ct + // Av = At*Af + + if (component == GGLFormat::ALPHA) { + modulate(dest, incoming, incomingTexel, component); + return; + } + + Scratch locals(registerFile()); + int temp = locals.obtain(); + integer_t color(locals.obtain(), 8, CORRUPTIBLE); + integer_t factor(locals.obtain(), 32, CORRUPTIBLE); + mBuilderContext.Rctx = temp; + MOV_MEM_TO_REG(8, PhysicalReg_EBP, mBuilderContext.Rctx); + MOVZX_MEM_TO_REG(OpndSize_8, mBuilderContext.Rctx, GGL_OFFSETOF(state.texture[tmu].env_color[component]), color.reg); + extract(factor, incomingTexel, component); + + // no need to keep more than 8-bits for blend + int Ni = incoming.size(); + int shift = incoming.l; + if (Ni > 8) { + shift += Ni-8; + Ni = 8; + } + integer_t incomingNorm(incoming.reg, Ni, incoming.flags); + if (shift) { + MOV_REG_TO_REG(incomingNorm.reg, dest.reg); + SHR(shift, dest.reg); + incomingNorm.reg = dest.reg; + incomingNorm.flags |= CORRUPTIBLE; + } + MOV_REG_TO_REG(factor.reg, temp); + SHR(factor.s-1, temp); + ADD_REG_TO_REG(temp, factor.reg); + locals.recycle(temp); + build_blendOneMinusFF(dest, factor, incomingNorm, color); +} + +void GGLX86Assembler::add( + component_t& dest, + const component_t& incoming, + const pixel_t& incomingTexel, int component) +{ + // RGBA: + // Cv = Cf + Ct; + Scratch locals(registerFile()); + + component_t incomingTemp(incoming); + + // use "dest" as a temporary for extracting the texel, unless "dest" + // overlaps "incoming". + integer_t texel(dest.reg, 32, CORRUPTIBLE); + if (dest.reg == incomingTemp.reg) + texel.reg = locals.obtain(); + extract(texel, incomingTexel, component); + + if (texel.s < incomingTemp.size()) { + expand(texel, texel, incomingTemp.size()); + } else if (texel.s > incomingTemp.size()) { + if (incomingTemp.flags & CORRUPTIBLE) { + expand(incomingTemp, incomingTemp, texel.s); + } else { + incomingTemp.reg = locals.obtain(); + expand(incomingTemp, incoming, texel.s); + } + } + + if (incomingTemp.l) { + MOV_REG_TO_REG(incomingTemp.reg, dest.reg); + SHR(incomingTemp.l, dest.reg); + ADD_REG_TO_REG(texel.reg, dest.reg); + } else { + MOV_REG_TO_REG(incomingTemp.reg, dest.reg); + ADD_REG_TO_REG(texel.reg, dest.reg); + } + dest.l = 0; + dest.h = texel.size(); + int temp_reg = locals.obtain(); + component_sat(dest, temp_reg); + locals.recycle(temp_reg); +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/libpixelflinger/pixelflinger.cpp b/libpixelflinger/pixelflinger.cpp index fd449b2..f06154f 100644 --- a/libpixelflinger/pixelflinger.cpp +++ b/libpixelflinger/pixelflinger.cpp @@ -32,7 +32,11 @@ #include "scanline.h" #include "trap.h" +#if defined(__i386__) || defined(__x86_64__) +#include "codeflinger/x86/GGLX86Assembler.h" +#else #include "codeflinger/GGLAssembler.h" +#endif #include "codeflinger/CodeCache.h" #include <stdio.h> diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp index 3d14531..5ef932b 100644 --- a/libpixelflinger/scanline.cpp +++ b/libpixelflinger/scanline.cpp @@ -34,7 +34,12 @@ #include "scanline.h" #include "codeflinger/CodeCache.h" +#if defined(__i386__) || defined(__x86_64__) +#include "codeflinger/x86/GGLX86Assembler.h" +#include "codeflinger/x86/X86Assembler.h" +#else #include "codeflinger/GGLAssembler.h" +#endif #if defined(__arm__) #include "codeflinger/ARMAssembler.h" #elif defined(__aarch64__) @@ -61,6 +66,8 @@ #if defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__) # define ANDROID_ARM_CODEGEN 1 +#elif defined(__i386__) +# define ANDROID_IA32_CODEGEN 1 #else # define ANDROID_ARM_CODEGEN 0 #endif @@ -284,7 +291,7 @@ static const needs_filter_t fill16noblend = { // ---------------------------------------------------------------------------- -#if ANDROID_ARM_CODEGEN +#if ANDROID_ARM_CODEGEN || ANDROID_IA32_CODEGEN #if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6 static CodeCache gCodeCache(32 * 1024); @@ -316,7 +323,7 @@ void ggl_uninit_scanline(context_t* c) { if (c->state.buffers.coverage) free(c->state.buffers.coverage); -#if ANDROID_ARM_CODEGEN +#if ANDROID_ARM_CODEGEN || ANDROID_IA32_CODEGEN if (c->scanline_as) c->scanline_as->decStrong(c); #endif @@ -436,6 +443,39 @@ static void pick_scanline(context_t* c) c->scanline_as = assembly.get(); c->scanline_as->incStrong(c); // hold on to assembly c->scanline = (void(*)(context_t* c))assembly->base(); +#elif ANDROID_IA32_CODEGEN + const AssemblyKey<needs_t> key(c->state.needs); + sp<Assembly> assembly = gCodeCache.lookup(key); + if (assembly == 0) { + // create a new assembly region + sp<ScanlineAssembly> a = new ScanlineAssembly(c->state.needs, + ASSEMBLY_SCRATCH_SIZE); + // initialize our assembler + GGLX86Assembler assembler( a ); + // generate the scanline code for the given needs + int err = assembler.scanline(c->state.needs, c); + if (ggl_likely(!err)) { + // finally, cache this assembly + err = gCodeCache.cache(a->key(), a); + } + if (ggl_unlikely(err)) { + ALOGE("error generating or caching assembly. Reverting to NOP. cache_err: %d \n", err); + c->scanline = scanline_noop; + c->init_y = init_y_noop; + c->step_y = step_y__nop; + return; + } + assembly = a; + } + + // release the previous assembly + if (c->scanline_as) { + c->scanline_as->decStrong(c); + } + + c->scanline_as = assembly.get(); + c->scanline_as->incStrong(c); // hold on to assembly + c->scanline = (void(*)(context_t* c))assembly->base(); #else // ALOGW("using generic (slow) pixel-pipeline"); c->scanline = scanline; @@ -464,7 +504,7 @@ static void blend_factor(context_t* c, pixel_t* r, uint32_t factor, const pixel_t* src, const pixel_t* dst); static void rescale(uint32_t& u, uint8_t& su, uint32_t& v, uint8_t& sv); -#if ANDROID_ARM_CODEGEN && (ANDROID_CODEGEN == ANDROID_CODEGEN_GENERATED) +#if (ANDROID_ARM_CODEGEN || ANDROID_IA32_CODEGEN) && (ANDROID_CODEGEN == ANDROID_CODEGEN_GENERATED) // no need to compile the generic-pipeline, it can't be reached void scanline(context_t*) @@ -939,7 +979,7 @@ discard: } } -#endif // ANDROID_ARM_CODEGEN && (ANDROID_CODEGEN == ANDROID_CODEGEN_GENERATED) +#endif // (ANDROID_ARM_CODEGEN || ANDROID_IA32_CODEGEN) && (ANDROID_CODEGEN == ANDROID_CODEGEN_GENERATED) // ---------------------------------------------------------------------------- #if 0 diff --git a/libpixelflinger/tests/codegen/Android.mk b/libpixelflinger/tests/codegen/Android.mk index 2f9ca2f..045a68e 100644 --- a/libpixelflinger/tests/codegen/Android.mk +++ b/libpixelflinger/tests/codegen/Android.mk @@ -1,8 +1,13 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) +ifeq ($(TARGET_ARCH),x86) LOCAL_SRC_FILES:= \ - codegen.cpp.arm + codegen.cpp +else +LOCAL_SRC_FILES:= \ + codegen.cpp.arm +endif LOCAL_SHARED_LIBRARIES := \ libcutils \ @@ -11,6 +16,10 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/../.. +ifeq ($(TARGET_ARCH),x86) +LOCAL_STATIC_LIBRARIES := libenc +endif + LOCAL_MODULE:= test-opengl-codegen LOCAL_MODULE_TAGS := tests diff --git a/libpixelflinger/tests/codegen/codegen.cpp b/libpixelflinger/tests/codegen/codegen.cpp index 148b6f4..f5c7136 100644 --- a/libpixelflinger/tests/codegen/codegen.cpp +++ b/libpixelflinger/tests/codegen/codegen.cpp @@ -7,15 +7,22 @@ #include "scanline.h" #include "codeflinger/CodeCache.h" +#if defined(__i386__) || defined(__x86_64__) +#include "codeflinger/x86/GGLX86Assembler.h" +#include "codeflinger/x86/X86Assembler.h" +#else #include "codeflinger/GGLAssembler.h" #include "codeflinger/ARMAssembler.h" #if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6 #include "codeflinger/MIPSAssembler.h" #endif #include "codeflinger/Arm64Assembler.h" +#endif #if defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__) # define ANDROID_ARM_CODEGEN 1 +#elif defined(__i386__) +# define ANDROID_IA32_CODEGEN 1 #else # define ANDROID_ARM_CODEGEN 0 #endif @@ -40,7 +47,6 @@ public: static void ggl_test_codegen(uint32_t n, uint32_t p, uint32_t t0, uint32_t t1) { -#if ANDROID_ARM_CODEGEN GGLContext* c; gglInit(&c); needs_t needs; @@ -48,6 +54,7 @@ static void ggl_test_codegen(uint32_t n, uint32_t p, uint32_t t0, uint32_t t1) needs.p = p; needs.t[0] = t0; needs.t[1] = t1; +#if ANDROID_ARM_CODEGEN sp<ScanlineAssembly> a(new ScanlineAssembly(needs, ASSEMBLY_SCRATCH_SIZE)); #if defined(__arm__) @@ -66,10 +73,15 @@ static void ggl_test_codegen(uint32_t n, uint32_t p, uint32_t t0, uint32_t t1) if (err != 0) { printf("error %08x (%s)\n", err, strerror(-err)); } - gglUninit(c); -#else - printf("This test runs only on ARM, Arm64 or MIPS\n"); +#elif ANDROID_IA32_CODEGEN + sp<ScanlineAssembly> a(new ScanlineAssembly(needs, ASSEMBLY_SCRATCH_SIZE)); + GGLX86Assembler assembler( a ); + int err = assembler.scanline(needs, (context_t*)c); + if (err != 0) { + printf("error %08x (%s)\n", err, strerror(-err)); + } #endif + gglUninit(c); } int main(int argc, char** argv) diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk index 7bf53e3..7e44c7a 100644 --- a/libsysutils/Android.mk +++ b/libsysutils/Android.mk @@ -1,8 +1,6 @@ LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ +common_src_files := \ src/SocketListener.cpp \ src/FrameworkListener.cpp \ src/NetlinkListener.cpp \ @@ -12,14 +10,21 @@ LOCAL_SRC_FILES:= \ src/ServiceManager.cpp \ EventLogTags.logtags -LOCAL_MODULE:= libsysutils - -LOCAL_CFLAGS := -Werror - -LOCAL_SHARED_LIBRARIES := \ +common_shared_libraries := \ libcutils \ liblog \ libnl +include $(CLEAR_VARS) +LOCAL_SRC_FILES:= $(common_src_files) +LOCAL_MODULE:= libsysutils +LOCAL_CFLAGS := -Werror +LOCAL_SHARED_LIBRARIES := $(common_shared_libraries) include $(BUILD_SHARED_LIBRARY) +include $(CLEAR_VARS) +LOCAL_SRC_FILES:= $(common_src_files) +LOCAL_MODULE:= libsysutils +LOCAL_CFLAGS := -Werror +LOCAL_SHARED_LIBRARIES := $(common_shared_libraries) +include $(BUILD_STATIC_LIBRARY) diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp index ac3dd98..c3b5d74 100644 --- a/libutils/SystemClock.cpp +++ b/libutils/SystemClock.cpp @@ -40,6 +40,9 @@ namespace android { +static pthread_mutex_t clock_lock = PTHREAD_MUTEX_INITIALIZER; +static int clock_method = -1; + /* * native public static long uptimeMillis(); */ @@ -119,29 +122,47 @@ int64_t elapsedRealtimeNano() static int s_fd = -1; - if (s_fd == -1) { - int fd = open("/dev/alarm", O_RDONLY); - if (android_atomic_cmpxchg(-1, fd, &s_fd)) { - close(fd); - } + if (clock_method < 0) { + pthread_mutex_lock(&clock_lock); } - result = ioctl(s_fd, - ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), &ts); + if (clock_method < 0 || clock_method == METHOD_IOCTL) { + if (s_fd == -1) { + int fd = open("/dev/alarm", O_RDONLY); + if (android_atomic_cmpxchg(-1, fd, &s_fd)) { + close(fd); + } + } - if (result == 0) { - timestamp = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; - checkTimeStamps(timestamp, &prevTimestamp, &prevMethod, METHOD_IOCTL); - return timestamp; + if (s_fd > -1) { + result = ioctl(s_fd, + ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), &ts); + + if (result == 0) { + timestamp = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; + checkTimeStamps(timestamp, &prevTimestamp, &prevMethod, METHOD_IOCTL); + if (clock_method < 0) { + clock_method = METHOD_IOCTL; + pthread_mutex_unlock(&clock_lock); + } + return timestamp; + } + } } // /dev/alarm doesn't exist, fallback to CLOCK_BOOTTIME - result = clock_gettime(CLOCK_BOOTTIME, &ts); - if (result == 0) { - timestamp = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; - checkTimeStamps(timestamp, &prevTimestamp, &prevMethod, - METHOD_CLOCK_GETTIME); - return timestamp; + if (clock_method < 0 || clock_method == METHOD_CLOCK_GETTIME) { + result = clock_gettime(CLOCK_BOOTTIME, &ts); + if (result == 0) { + timestamp = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; + checkTimeStamps(timestamp, &prevTimestamp, &prevMethod, + METHOD_CLOCK_GETTIME); + if (clock_method < 0) { + clock_method = METHOD_CLOCK_GETTIME; + pthread_mutex_unlock(&clock_lock); + } + return timestamp; + } } // XXX: there was an error, probably because the driver didn't @@ -150,6 +171,10 @@ int64_t elapsedRealtimeNano() timestamp = systemTime(SYSTEM_TIME_MONOTONIC); checkTimeStamps(timestamp, &prevTimestamp, &prevMethod, METHOD_SYSTEMTIME); + if (clock_method < 0) { + clock_method = METHOD_SYSTEMTIME; + pthread_mutex_unlock(&clock_lock); + } return timestamp; #else return systemTime(SYSTEM_TIME_MONOTONIC); diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index e598bb8..47f0136 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp @@ -284,6 +284,7 @@ static void show_help(const char *cmd) " other pruning activity is oldest first. Special case ~!\n" " represents an automatic quicker pruning for the noisiest\n" " UID as determined by the current statistics.\n" + " -C colored output\n" " -P '<list> ...' set prune white and ~black list, using same format as\n" " printed above. Must be quoted.\n"); @@ -492,7 +493,7 @@ int main(int argc, char **argv) for (;;) { int ret; - ret = getopt(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:"); + ret = getopt(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpCP:"); if (ret < 0) { break; @@ -597,6 +598,10 @@ int main(int argc, char **argv) setPruneList = optarg; break; + case 'C': + setLogFormat ("color"); + break; + case 'b': { if (strcmp(optarg, "all") == 0) { while (devices) { @@ -649,7 +654,7 @@ int main(int argc, char **argv) break; case 'f': - if ((tail_time == log_time::EPOCH) && (tail_lines != 0)) { + if ((tail_time == log_time::EPOCH) && (tail_lines == 0)) { tail_time = lastLogTime(optarg); } // redirect output to a file diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp index de2db67..9455d87 100644 --- a/logcat/tests/logcat_test.cpp +++ b/logcat/tests/logcat_test.cpp @@ -15,10 +15,12 @@ */ #include <ctype.h> +#include <dirent.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/types.h> #include <gtest/gtest.h> #include <log/log.h> @@ -284,7 +286,7 @@ TEST(logcat, get_size) { while (fgets(buffer, sizeof(buffer), fp)) { int size, consumed, max, payload; - char size_mult[2], consumed_mult[2]; + char size_mult[3], consumed_mult[3]; long full_size, full_consumed; size = consumed = max = payload = 0; @@ -489,12 +491,12 @@ TEST(logcat, logrotate) { static const char comm[] = "logcat -b radio -b events -b system -b main" " -d -f %s/log.txt -n 7 -r 1"; char command[sizeof(buf) + sizeof(comm)]; - sprintf(command, comm, buf); + snprintf(command, sizeof(command), comm, buf); int ret; EXPECT_FALSE((ret = system(command))); if (!ret) { - sprintf(command, "ls -s %s 2>/dev/null", buf); + snprintf(command, sizeof(command), "ls -s %s 2>/dev/null", buf); FILE *fp; EXPECT_TRUE(NULL != (fp = popen(command, "r"))); @@ -503,16 +505,12 @@ TEST(logcat, logrotate) { int count = 0; while (fgets(buffer, sizeof(buffer), fp)) { - static const char match_1[] = "4 log.txt"; - static const char match_2[] = "8 log.txt"; - static const char match_3[] = "12 log.txt"; - static const char match_4[] = "16 log.txt"; static const char total[] = "total "; + int num; + char c; - if (!strncmp(buffer, match_1, sizeof(match_1) - 1) - || !strncmp(buffer, match_2, sizeof(match_2) - 1) - || !strncmp(buffer, match_3, sizeof(match_3) - 1) - || !strncmp(buffer, match_4, sizeof(match_4) - 1)) { + if ((2 == sscanf(buffer, "%d log.tx%c", &num, &c)) && + (num <= 24)) { ++count; } else if (strncmp(buffer, total, sizeof(total) - 1)) { fprintf(stderr, "WARNING: Parse error: %s", buffer); @@ -522,7 +520,7 @@ TEST(logcat, logrotate) { EXPECT_TRUE(count == 7 || count == 8); } } - sprintf(command, "rm -rf %s", buf); + snprintf(command, sizeof(command), "rm -rf %s", buf); EXPECT_FALSE(system(command)); } @@ -534,12 +532,12 @@ TEST(logcat, logrotate_suffix) { static const char logcat_cmd[] = "logcat -b radio -b events -b system -b main" " -d -f %s/log.txt -n 10 -r 1"; char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd)]; - sprintf(command, logcat_cmd, tmp_out_dir); + snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir); int ret; EXPECT_FALSE((ret = system(command))); if (!ret) { - sprintf(command, "ls %s 2>/dev/null", tmp_out_dir); + snprintf(command, sizeof(command), "ls %s 2>/dev/null", tmp_out_dir); FILE *fp; EXPECT_TRUE(NULL != (fp = popen(command, "r"))); @@ -575,7 +573,113 @@ TEST(logcat, logrotate_suffix) { pclose(fp); EXPECT_EQ(11, log_file_count); } - sprintf(command, "rm -rf %s", tmp_out_dir); + snprintf(command, sizeof(command), "rm -rf %s", tmp_out_dir); + EXPECT_FALSE(system(command)); +} + +TEST(logcat, logrotate_continue) { + static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX"; + char tmp_out_dir[sizeof(tmp_out_dir_form)]; + ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form))); + + static const char log_filename[] = "log.txt"; + static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n 256 -r 1024"; + static const char cleanup_cmd[] = "rm -rf %s"; + char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename)]; + snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename); + + int ret; + EXPECT_FALSE((ret = system(command))); + if (ret) { + snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir); + EXPECT_FALSE(system(command)); + return; + } + FILE *fp; + snprintf(command, sizeof(command), "%s/%s", tmp_out_dir, log_filename); + EXPECT_TRUE(NULL != ((fp = fopen(command, "r")))); + if (!fp) { + snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir); + EXPECT_FALSE(system(command)); + return; + } + char *line = NULL; + char *last_line = NULL; // this line is allowed to stutter, one-line overlap + char *second_last_line = NULL; + size_t len = 0; + while (getline(&line, &len, fp) != -1) { + free(second_last_line); + second_last_line = last_line; + last_line = line; + line = NULL; + } + fclose(fp); + free(line); + if (second_last_line == NULL) { + fprintf(stderr, "No second to last line, using last, test may fail\n"); + second_last_line = last_line; + last_line = NULL; + } + free(last_line); + EXPECT_TRUE(NULL != second_last_line); + if (!second_last_line) { + snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir); + EXPECT_FALSE(system(command)); + return; + } + // re-run the command, it should only add a few lines more content if it + // continues where it left off. + snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename); + EXPECT_FALSE((ret = system(command))); + if (ret) { + snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir); + EXPECT_FALSE(system(command)); + return; + } + DIR *dir; + EXPECT_TRUE(NULL != (dir = opendir(tmp_out_dir))); + if (!dir) { + snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir); + EXPECT_FALSE(system(command)); + return; + } + struct dirent *entry; + unsigned count = 0; + while ((entry = readdir(dir))) { + if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) { + continue; + } + snprintf(command, sizeof(command), "%s/%s", tmp_out_dir, entry->d_name); + EXPECT_TRUE(NULL != ((fp = fopen(command, "r")))); + if (!fp) { + fprintf(stderr, "%s ?\n", command); + continue; + } + line = NULL; + size_t number = 0; + while (getline(&line, &len, fp) != -1) { + ++number; + if (!strcmp(line, second_last_line)) { + EXPECT_TRUE(++count <= 1); + fprintf(stderr, "%s(%zu):\n", entry->d_name, number); + } + } + fclose(fp); + free(line); + unlink(command); + } + closedir(dir); + if (count > 1) { + char *brk = strpbrk(second_last_line, "\r\n"); + if (!brk) { + brk = second_last_line + strlen(second_last_line); + } + fprintf(stderr, "\"%.*s\" occured %u times\n", + (int)(brk - second_last_line), second_last_line, count); + } + free(second_last_line); + + snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir); EXPECT_FALSE(system(command)); } diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp index 5489cc9..489bea6 100644 --- a/logd/CommandListener.cpp +++ b/logd/CommandListener.cpp @@ -34,8 +34,7 @@ CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/, LogListener * /*swl*/) : - FrameworkListener(getLogSocket()), - mBuf(*buf) { + FrameworkListener(getLogSocket()) { // registerCmd(new ShutdownCmd(buf, writer, swl)); registerCmd(new ClearCmd(buf)); registerCmd(new GetBufSizeCmd(buf)); @@ -47,10 +46,9 @@ CommandListener::CommandListener(LogBuffer *buf, LogReader * /*reader*/, registerCmd(new ReinitCmd()); } -CommandListener::ShutdownCmd::ShutdownCmd(LogBuffer *buf, LogReader *reader, +CommandListener::ShutdownCmd::ShutdownCmd(LogReader *reader, LogListener *swl) : LogCommand("shutdown"), - mBuf(*buf), mReader(*reader), mSwl(*swl) { } diff --git a/logd/CommandListener.h b/logd/CommandListener.h index 83e06b4..3877675 100644 --- a/logd/CommandListener.h +++ b/logd/CommandListener.h @@ -27,7 +27,6 @@ void reinit_signal_handler(int /*signal*/); class CommandListener : public FrameworkListener { - LogBuffer &mBuf; public: CommandListener(LogBuffer *buf, LogReader *reader, LogListener *swl); @@ -37,12 +36,11 @@ private: static int getLogSocket(); class ShutdownCmd : public LogCommand { - LogBuffer &mBuf; LogReader &mReader; LogListener &mSwl; public: - ShutdownCmd(LogBuffer *buf, LogReader *reader, LogListener *swl); + ShutdownCmd(LogReader *reader, LogListener *swl); virtual ~ShutdownCmd() {} int runCommand(SocketClient *c, int argc, char ** argv); }; diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp index 4b3547c..808d17a 100644 --- a/logd/LogAudit.cpp +++ b/logd/LogAudit.cpp @@ -24,6 +24,7 @@ #include <sys/uio.h> #include <syslog.h> +#include <log/logger.h> #include <private/android_filesystem_config.h> #include <private/android_logger.h> @@ -153,15 +154,16 @@ int LogAudit::logPrint(const char *fmt, ...) { // log to events - size_t l = strlen(str); + size_t l = strnlen(str, LOGGER_ENTRY_MAX_PAYLOAD); size_t n = l + sizeof(android_log_event_string_t); bool notify = false; - android_log_event_string_t *event = static_cast<android_log_event_string_t *>(malloc(n)); - if (!event) { - rc = -ENOMEM; - } else { + { // begin scope for event buffer + uint32_t buffer[(n + sizeof(uint32_t) - 1) / sizeof(uint32_t)]; + + android_log_event_string_t *event + = reinterpret_cast<android_log_event_string_t *>(buffer); event->header.tag = htole32(AUDITD_LOG_TAG); event->type = EVENT_TYPE_STRING; event->length = htole32(l); @@ -170,11 +172,10 @@ int LogAudit::logPrint(const char *fmt, ...) { rc = logbuf->log(LOG_ID_EVENTS, now, uid, pid, tid, reinterpret_cast<char *>(event), (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX); - free(event); - if (rc >= 0) { notify = true; } + // end scope for event buffer } // log to main @@ -206,24 +207,28 @@ int LogAudit::logPrint(const char *fmt, ...) { l = strlen(comm) + 1; ecomm = ""; } - n = (estr - str) + strlen(ecomm) + l + 2; + size_t b = estr - str; + if (b > LOGGER_ENTRY_MAX_PAYLOAD) { + b = LOGGER_ENTRY_MAX_PAYLOAD; + } + size_t e = strnlen(ecomm, LOGGER_ENTRY_MAX_PAYLOAD - b); + n = b + e + l + 2; + + { // begin scope for main buffer + char newstr[n]; - char *newstr = static_cast<char *>(malloc(n)); - if (!newstr) { - rc = -ENOMEM; - } else { *newstr = info ? ANDROID_LOG_INFO : ANDROID_LOG_WARN; strlcpy(newstr + 1, comm, l); - strncpy(newstr + 1 + l, str, estr - str); - strcpy(newstr + 1 + l + (estr - str), ecomm); + strncpy(newstr + 1 + l, str, b); + strncpy(newstr + 1 + l + b, ecomm, e); rc = logbuf->log(LOG_ID_MAIN, now, uid, pid, tid, newstr, (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX); - free(newstr); if (rc >= 0) { notify = true; } + // end scope for main buffer } free(commfree); @@ -239,9 +244,9 @@ int LogAudit::logPrint(const char *fmt, ...) { return rc; } -int LogAudit::log(char *buf) { +int LogAudit::log(char *buf, size_t len) { char *audit = strstr(buf, " audit("); - if (!audit) { + if (!audit || (audit >= &buf[len])) { return 0; } @@ -249,7 +254,7 @@ int LogAudit::log(char *buf) { int rc; char *type = strstr(buf, "type="); - if (type) { + if (type && (type < &buf[len])) { rc = logPrint("%s %s", type, audit + 1); } else { rc = logPrint("%s", audit + 1); diff --git a/logd/LogAudit.h b/logd/LogAudit.h index f977be9..2342822 100644 --- a/logd/LogAudit.h +++ b/logd/LogAudit.h @@ -28,7 +28,7 @@ class LogAudit : public SocketListener { public: LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg); - int log(char *buf); + int log(char *buf, size_t len); protected: virtual bool onDataAvailable(SocketClient *cli); diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp index 9fb1439..150ce22 100644 --- a/logd/LogBufferElement.cpp +++ b/logd/LogBufferElement.cpp @@ -91,7 +91,8 @@ char *android::tidToName(pid_t tid) { size_t retval_len = strlen(retval); size_t name_len = strlen(name); // KISS: ToDo: Only checks prefix truncated, not suffix, or both - if ((retval_len < name_len) && !strcmp(retval, name + name_len - retval_len)) { + if ((retval_len < name_len) + && !fast<strcmp>(retval, name + name_len - retval_len)) { free(retval); retval = name; } else { @@ -123,14 +124,16 @@ size_t LogBufferElement::populateDroppedMessage(char *&buffer, commName = parent->pidToName(mPid); parent->unlock(); } - size_t len = name ? strlen(name) : 0; - if (len && commName && !strncmp(name, commName, len)) { - if (commName[len] == '\0') { - free(commName); - commName = NULL; - } else { - free(name); - name = NULL; + if (name && name[0] && commName && (name[0] == commName[0])) { + size_t len = strlen(name + 1); + if (!strncmp(name + 1, commName + 1, len)) { + if (commName[len + 1] == '\0') { + free(commName); + commName = NULL; + } else { + free(name); + name = NULL; + } } } if (name) { @@ -150,9 +153,9 @@ size_t LogBufferElement::populateDroppedMessage(char *&buffer, } } // identical to below to calculate the buffer size required - len = snprintf(NULL, 0, format_uid, mUid, name ? name : "", - commName ? commName : "", - mDropped, (mDropped > 1) ? "s" : ""); + size_t len = snprintf(NULL, 0, format_uid, mUid, name ? name : "", + commName ? commName : "", + mDropped, (mDropped > 1) ? "s" : ""); size_t hdrLen; if (mLogId == LOG_ID_EVENTS) { diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h index ca2c3a6..30e43c6 100644 --- a/logd/LogBufferElement.h +++ b/logd/LogBufferElement.h @@ -25,27 +25,6 @@ #include <log/log.h> #include <log/log_read.h> -// Hijack this header as a common include file used by most all sources -// to report some utilities defined here and there. - -namespace android { - -// Furnished in main.cpp. Caller must own and free returned value -char *uidToName(uid_t uid); - -// Furnished in LogStatistics.cpp. Caller must own and free returned value -char *pidToName(pid_t pid); -char *tidToName(pid_t tid); - -// Furnished in main.cpp. Thread safe. -const char *tagToName(uint32_t tag); - -} - -static inline bool worstUidEnabledForLogid(log_id_t id) { - return (id != LOG_ID_CRASH) && (id != LOG_ID_KERNEL) && (id != LOG_ID_EVENTS); -} - class LogBuffer; #define EXPIRE_HOUR_THRESHOLD 24 // Only expire chatty UID logs to preserve @@ -85,7 +64,7 @@ public: unsigned short getDropped(void) const { return mMsg ? 0 : mDropped; } unsigned short setDropped(unsigned short value) { if (mMsg) { - free(mMsg); + delete [] mMsg; mMsg = NULL; } return mDropped = value; diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp index 0a5df24..d28161e 100644 --- a/logd/LogKlog.cpp +++ b/logd/LogKlog.cpp @@ -39,14 +39,15 @@ static const char priority_message[] = { KMSG_PRIORITY(LOG_INFO), '\0' }; // Parsing is hard // called if we see a '<', s is the next character, returns pointer after '>' -static char *is_prio(char *s) { - if (!isdigit(*s++)) { +static char *is_prio(char *s, size_t len) { + if (!len || !isdigit(*s++)) { return NULL; } - static const size_t max_prio_len = 4; - size_t len = 0; + --len; + static const size_t max_prio_len = (len < 4) ? len : 4; + size_t priolen = 0; char c; - while (((c = *s++)) && (++len <= max_prio_len)) { + while (((c = *s++)) && (++priolen <= max_prio_len)) { if (!isdigit(c)) { return ((c == '>') && (*s == '[')) ? s : NULL; } @@ -55,16 +56,19 @@ static char *is_prio(char *s) { } // called if we see a '[', s is the next character, returns pointer after ']' -static char *is_timestamp(char *s) { - while (*s == ' ') { +static char *is_timestamp(char *s, size_t len) { + while (len && (*s == ' ')) { ++s; + --len; } - if (!isdigit(*s++)) { + if (!len || !isdigit(*s++)) { return NULL; } + --len; bool first_period = true; char c; - while ((c = *s++)) { + while (len && ((c = *s++))) { + --len; if ((c == '.') && first_period) { first_period = false; } else if (!isdigit(c)) { @@ -77,6 +81,8 @@ static char *is_timestamp(char *s) { // Like strtok_r with "\r\n" except that we look for log signatures (regex) // \(\(<[0-9]\{1,4\}>\)\([[] *[0-9]+[.][0-9]+[]] \)\{0,1\}\|[[] *[0-9]+[.][0-9]+[]] \) // and split if we see a second one without a newline. +// We allow nuls in content, monitoring the overall length and sub-length of +// the discovered tokens. #define SIGNATURE_MASK 0xF0 // <digit> following ('0' to '9' masked with ~SIGNATURE_MASK) added to signature @@ -85,7 +91,11 @@ static char *is_timestamp(char *s) { // space is one more than <digit> of 9 #define OPEN_BRACKET_SPACE ((char)(OPEN_BRACKET_SIG | 10)) -char *log_strtok_r(char *s, char **last) { +char *log_strntok_r(char *s, size_t *len, char **last, size_t *sublen) { + *sublen = 0; + if (!*len) { + return NULL; + } if (!s) { if (!(s = *last)) { return NULL; @@ -95,6 +105,7 @@ char *log_strtok_r(char *s, char **last) { if ((*s & SIGNATURE_MASK) == LESS_THAN_SIG) { *s = (*s & ~SIGNATURE_MASK) + '0'; *--s = '<'; + ++*len; } // fixup for log signature split [, // OPEN_BRACKET_SPACE is space, OPEN_BRACKET_SIG + <digit> @@ -105,24 +116,30 @@ char *log_strtok_r(char *s, char **last) { *s = (*s & ~SIGNATURE_MASK) + '0'; } *--s = '['; + ++*len; } } - s += strspn(s, "\r\n"); + while (*len && ((*s == '\r') || (*s == '\n'))) { + ++s; + --*len; + } - if (!*s) { // no non-delimiter characters + if (!*len) { *last = NULL; return NULL; } char *peek, *tok = s; for (;;) { - char c = *s++; - switch (c) { - case '\0': + if (*len == 0) { *last = NULL; return tok; - + } + char c = *s++; + --*len; + size_t adjust; + switch (c) { case '\r': case '\n': s[-1] = '\0'; @@ -130,7 +147,7 @@ char *log_strtok_r(char *s, char **last) { return tok; case '<': - peek = is_prio(s); + peek = is_prio(s, *len); if (!peek) { break; } @@ -141,14 +158,26 @@ char *log_strtok_r(char *s, char **last) { *last = s; return tok; } + adjust = peek - s; + if (adjust > *len) { + adjust = *len; + } + *sublen += adjust; + *len -= adjust; s = peek; - if ((*s == '[') && ((peek = is_timestamp(s + 1)))) { + if ((*s == '[') && ((peek = is_timestamp(s + 1, *len - 1)))) { + adjust = peek - s; + if (adjust > *len) { + adjust = *len; + } + *sublen += adjust; + *len -= adjust; s = peek; } break; case '[': - peek = is_timestamp(s); + peek = is_timestamp(s, *len); if (!peek) { break; } @@ -163,9 +192,16 @@ char *log_strtok_r(char *s, char **last) { *last = s; return tok; } + adjust = peek - s; + if (adjust > *len) { + adjust = *len; + } + *sublen += adjust; + *len -= adjust; s = peek; break; } + ++*sublen; } // NOTREACHED } @@ -177,8 +213,6 @@ LogKlog::LogKlog(LogBuffer *buf, LogReader *reader, int fdWrite, int fdRead, boo logbuf(buf), reader(reader), signature(CLOCK_MONOTONIC), - fdWrite(fdWrite), - fdRead(fdRead), initialized(false), enableLogging(true), auditd(auditd) { @@ -214,17 +248,17 @@ bool LogKlog::onDataAvailable(SocketClient *cli) { bool full = len == (sizeof(buffer) - 1); char *ep = buffer + len; *ep = '\0'; - len = 0; + size_t sublen; for(char *ptr = NULL, *tok = buffer; - ((tok = log_strtok_r(tok, &ptr))); + ((tok = log_strntok_r(tok, &len, &ptr, &sublen))); tok = NULL) { - if (((tok + strlen(tok)) == ep) && (retval != 0) && full) { - len = strlen(tok); - memmove(buffer, tok, len); + if (((tok + sublen) >= ep) && (retval != 0) && full) { + memmove(buffer, tok, sublen); + len = sublen; break; } if (*tok) { - log(tok); + log(tok, sublen); } } } @@ -234,9 +268,11 @@ bool LogKlog::onDataAvailable(SocketClient *cli) { void LogKlog::calculateCorrection(const log_time &monotonic, - const char *real_string) { + const char *real_string, + size_t len) { log_time real; - if (!real.strptime(real_string, "%Y-%m-%d %H:%M:%S.%09q UTC")) { + const char *ep = real.strptime(real_string, "%Y-%m-%d %H:%M:%S.%09q UTC"); + if (!ep || (ep > &real_string[len])) { return; } // kernel report UTC, log_time::strptime is localtime from calendar. @@ -251,36 +287,85 @@ void LogKlog::calculateCorrection(const log_time &monotonic, correction = real - monotonic; } -void LogKlog::sniffTime(log_time &now, const char **buf, bool reverse) { - const char *cp; - if ((cp = now.strptime(*buf, "[ %s.%q]"))) { - static const char suspend[] = "PM: suspend entry "; - static const char resume[] = "PM: suspend exit "; - static const char healthd[] = "healthd: battery "; - static const char suspended[] = "Suspended for "; +static const char suspendStr[] = "PM: suspend entry "; +static const char resumeStr[] = "PM: suspend exit "; +static const char suspendedStr[] = "Suspended for "; + +static const char *strnstr(const char *s, size_t len, const char *needle) { + char c; + + if (!len) { + return NULL; + } + if ((c = *needle++) != 0) { + size_t needleLen = strlen(needle); + do { + do { + if (len <= needleLen) { + return NULL; + } + --len; + } while (*s++ != c); + } while (fast<memcmp>(s, needle, needleLen)); + s--; + } + return s; +} + +void LogKlog::sniffTime(log_time &now, + const char **buf, size_t len, + bool reverse) { + const char *cp = now.strptime(*buf, "[ %s.%q]"); + if (cp && (cp >= &(*buf)[len])) { + cp = NULL; + } + len -= cp - *buf; + if (cp) { + static const char healthd[] = "healthd"; + static const char battery[] = ": battery "; - if (isspace(*cp)) { + if (len && isspace(*cp)) { ++cp; + --len; } - if (!strncmp(cp, suspend, sizeof(suspend) - 1)) { - calculateCorrection(now, cp + sizeof(suspend) - 1); - } else if (!strncmp(cp, resume, sizeof(resume) - 1)) { - calculateCorrection(now, cp + sizeof(resume) - 1); - } else if (!strncmp(cp, healthd, sizeof(healthd) - 1)) { + *buf = cp; + + const char *b; + if (((b = strnstr(cp, len, suspendStr))) + && ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) { + len -= b - cp; + calculateCorrection(now, b, len); + } else if (((b = strnstr(cp, len, resumeStr))) + && ((size_t)((b += sizeof(resumeStr) - 1) - cp) < len)) { + len -= b - cp; + calculateCorrection(now, b, len); + } else if (((b = strnstr(cp, len, healthd))) + && ((size_t)((b += sizeof(healthd) - 1) - cp) < len) + && ((b = strnstr(b, len -= b - cp, battery))) + && ((size_t)((b += sizeof(battery) - 1) - cp) < len)) { + len -= b - cp; + // NB: healthd is roughly 150us late, worth the price to deal with + // ntp-induced or hardware clock drift. // look for " 2???-??-?? ??:??:??.????????? ???" - const char *tp; - for (tp = cp + sizeof(healthd) - 1; *tp && (*tp != '\n'); ++tp) { - if ((tp[0] == ' ') && (tp[1] == '2') && (tp[5] == '-')) { - calculateCorrection(now, tp + 1); + for (; len && *b && (*b != '\n'); ++b, --len) { + if ((b[0] == ' ') && (b[1] == '2') && (b[5] == '-')) { + calculateCorrection(now, b + 1, len - 1); break; } } - } else if (!strncmp(cp, suspended, sizeof(suspended) - 1)) { + } else if (((b = strnstr(cp, len, suspendedStr))) + && ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) { + len -= b - cp; log_time real; char *endp; - real.tv_sec = strtol(cp + sizeof(suspended) - 1, &endp, 10); - if (*endp == '.') { - real.tv_nsec = strtol(endp + 1, &endp, 10) * 1000000L; + real.tv_sec = strtol(b, &endp, 10); + if ((*endp == '.') && ((size_t)(endp - b) < len)) { + unsigned long multiplier = NS_PER_SEC; + real.tv_nsec = 0; + len -= endp - b; + while (--len && isdigit(*++endp) && (multiplier /= 10)) { + real.tv_nsec += (*endp - '0') * multiplier; + } if (reverse) { correction -= real; } else { @@ -290,14 +375,13 @@ void LogKlog::sniffTime(log_time &now, const char **buf, bool reverse) { } convertMonotonicToReal(now); - *buf = cp; } else { now = log_time(CLOCK_REALTIME); } } -pid_t LogKlog::sniffPid(const char *cp) { - while (*cp) { +pid_t LogKlog::sniffPid(const char *cp, size_t len) { + while (len) { // Mediatek kernels with modified printk if (*cp == '[') { int pid = 0; @@ -308,26 +392,59 @@ pid_t LogKlog::sniffPid(const char *cp) { break; // Only the first one } ++cp; + --len; } return 0; } +// kernel log prefix, convert to a kernel log priority number +static int parseKernelPrio(const char **buf, size_t len) { + int pri = LOG_USER | LOG_INFO; + const char *cp = *buf; + if (len && (*cp == '<')) { + pri = 0; + while(--len && isdigit(*++cp)) { + pri = (pri * 10) + *cp - '0'; + } + if (len && (*cp == '>')) { + ++cp; + } else { + cp = *buf; + pri = LOG_USER | LOG_INFO; + } + *buf = cp; + } + return pri; +} + // Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a // compensated start time. -void LogKlog::synchronize(const char *buf) { - const char *cp = strstr(buf, "] PM: suspend e"); +void LogKlog::synchronize(const char *buf, size_t len) { + const char *cp = strnstr(buf, len, suspendStr); if (!cp) { - return; + cp = strnstr(buf, len, resumeStr); + if (!cp) { + return; + } + } else { + const char *rp = strnstr(buf, len, resumeStr); + if (rp && (rp < cp)) { + cp = rp; + } } do { --cp; - } while ((cp > buf) && (isdigit(*cp) || isspace(*cp) || (*cp == '.'))); + } while ((cp > buf) && (*cp != '\n')); + if (*cp == '\n') { + ++cp; + } + parseKernelPrio(&cp, len - (cp - buf)); log_time now; - sniffTime(now, &cp, true); + sniffTime(now, &cp, len - (cp - buf), true); - char *suspended = strstr(buf, "] Suspended for "); + const char *suspended = strnstr(buf, len, suspendedStr); if (!suspended || (suspended > cp)) { return; } @@ -335,29 +452,13 @@ void LogKlog::synchronize(const char *buf) { do { --cp; - } while ((cp > buf) && (isdigit(*cp) || isspace(*cp) || (*cp == '.'))); - - sniffTime(now, &cp, true); -} - -// kernel log prefix, convert to a kernel log priority number -static int parseKernelPrio(const char **buf) { - int pri = LOG_USER | LOG_INFO; - const char *cp = *buf; - if (*cp == '<') { - pri = 0; - while(isdigit(*++cp)) { - pri = (pri * 10) + *cp - '0'; - } - if (*cp == '>') { - ++cp; - } else { - cp = *buf; - pri = LOG_USER | LOG_INFO; - } - *buf = cp; + } while ((cp > buf) && (*cp != '\n')); + if (*cp == '\n') { + ++cp; } - return pri; + parseKernelPrio(&cp, len - (cp - buf)); + + sniffTime(now, &cp, len - (cp - buf), true); } // Convert kernel log priority number into an Android Logger priority number @@ -390,6 +491,16 @@ static int convertKernelPrioToAndroidPrio(int pri) { return ANDROID_LOG_INFO; } +static const char *strnrchr(const char *s, size_t len, char c) { + const char *save = NULL; + for (;len; ++s, len--) { + if (*s == c) { + save = s; + } + } + return save; +} + // // log a message into the kernel log buffer // @@ -423,19 +534,20 @@ static int convertKernelPrioToAndroidPrio(int pri) { // logd.klogd: // return -1 if message logd.klogd: <signature> // -int LogKlog::log(const char *buf) { - if (auditd && strstr(buf, " audit(")) { +int LogKlog::log(const char *buf, size_t len) { + if (auditd && strnstr(buf, len, " audit(")) { return 0; } - int pri = parseKernelPrio(&buf); + const char *p = buf; + int pri = parseKernelPrio(&p, len); log_time now; - sniffTime(now, &buf, false); + sniffTime(now, &p, len - (p - buf), false); // sniff for start marker const char klogd_message[] = "logd.klogd: "; - const char *start = strstr(buf, klogd_message); + const char *start = strnstr(p, len - (p - buf), klogd_message); if (start) { uint64_t sig = strtoll(start + sizeof(klogd_message) - 1, NULL, 10); if (sig == signature.nsec()) { @@ -454,7 +566,7 @@ int LogKlog::log(const char *buf) { } // Parse pid, tid and uid - const pid_t pid = sniffPid(buf); + const pid_t pid = sniffPid(p, len - (p - buf)); const pid_t tid = pid; const uid_t uid = pid ? logbuf->pidToUid(pid) : 0; @@ -462,109 +574,116 @@ int LogKlog::log(const char *buf) { // 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(*buf)) { - ++buf; + while ((isspace(*p) || !*p) && (p < &buf[len])) { + ++p; } - if (!*buf) { + if (p >= &buf[len]) { // timestamp, no content return 0; } - start = buf; + start = p; const char *tag = ""; const char *etag = tag; - if (!isspace(*buf)) { + size_t taglen = len - (p - buf); + if (!isspace(*p) && *p) { const char *bt, *et, *cp; - bt = buf; - if (!strncmp(buf, "[INFO]", 6)) { + bt = p; + if (!fast<strncmp>(p, "[INFO]", 6)) { // <PRI>[<TIME>] "[INFO]"<tag> ":" message - bt = buf + 6; + bt = p + 6; + taglen -= 6; } - for(et = bt; *et && (*et != ':') && !isspace(*et); ++et) { + for(et = bt; taglen && *et && (*et != ':') && !isspace(*et); ++et, --taglen) { // skip ':' within [ ... ] if (*et == '[') { - while (*et && *et != ']') { + while (taglen && *et && *et != ']') { ++et; + --taglen; } } } - for(cp = et; isspace(*cp); ++cp); + for(cp = et; taglen && isspace(*cp); ++cp, --taglen); size_t size; if (*cp == ':') { // One Word tag = bt; etag = et; - buf = cp + 1; - } else { + p = cp + 1; + } else if (taglen) { size = et - bt; - if (strncmp(bt, cp, size)) { + if ((*bt == *cp) && fast<strncmp>(bt + 1, cp + 1, size - 1)) { // <PRI>[<TIME>] <tag>_host '<tag>.<num>' : message - if (!strncmp(bt + size - 5, "_host", 5) - && !strncmp(bt, cp, size - 5)) { + if (!fast<strncmp>(bt + size - 5, "_host", 5) + && !fast<strncmp>(bt + 1, cp + 1, size - 6)) { const char *b = cp; cp += size - 5; + taglen -= size - 5; if (*cp == '.') { - while (!isspace(*++cp) && (*cp != ':')); + while (--taglen && !isspace(*++cp) && (*cp != ':')); const char *e; - for(e = cp; isspace(*cp); ++cp); + for(e = cp; taglen && isspace(*cp); ++cp, --taglen); if (*cp == ':') { tag = b; etag = e; - buf = cp + 1; + p = cp + 1; } } } else { - while (!isspace(*++cp) && (*cp != ':')); + while (--taglen && !isspace(*++cp) && (*cp != ':')); const char *e; - for(e = cp; isspace(*cp); ++cp); + for(e = cp; taglen && isspace(*cp); ++cp, --taglen); // Two words if (*cp == ':') { tag = bt; etag = e; - buf = cp + 1; + p = cp + 1; } } } else if (isspace(cp[size])) { - const char *b = cp; cp += size; - while (isspace(*++cp)); + taglen -= size; + while (--taglen && isspace(*++cp)); // <PRI>[<TIME>] <tag> <tag> : message if (*cp == ':') { tag = bt; etag = et; - buf = cp + 1; + p = cp + 1; } } else if (cp[size] == ':') { // <PRI>[<TIME>] <tag> <tag> : message tag = bt; etag = et; - buf = cp + size + 1; + p = cp + size + 1; } else if ((cp[size] == '.') || isdigit(cp[size])) { // <PRI>[<TIME>] <tag> '<tag>.<num>' : message // <PRI>[<TIME>] <tag> '<tag><num>' : message const char *b = cp; cp += size; - while (!isspace(*++cp) && (*cp != ':')); + taglen -= size; + while (--taglen && !isspace(*++cp) && (*cp != ':')); const char *e = cp; - while (isspace(*cp)) { + while (taglen && isspace(*cp)) { ++cp; + --taglen; } if (*cp == ':') { tag = b; etag = e; - buf = cp + 1; + p = cp + 1; } } else { - while (!isspace(*++cp) && (*cp != ':')); + while (--taglen && !isspace(*++cp) && (*cp != ':')); const char *e = cp; - while (isspace(*cp)) { + while (taglen && isspace(*cp)) { ++cp; + --taglen; } // Two words if (*cp == ':') { tag = bt; etag = e; - buf = cp + 1; + p = cp + 1; } } } @@ -575,68 +694,69 @@ int LogKlog::log(const char *buf) { // register names like x18 but not driver names like en0 || ((size == 3) && (isdigit(tag[1]) && isdigit(tag[2]))) // blacklist - || ((size == 3) && !strncmp(tag, "CPU", 3)) - || ((size == 7) && !strncmp(tag, "WARNING", 7)) - || ((size == 5) && !strncmp(tag, "ERROR", 5)) - || ((size == 4) && !strncmp(tag, "INFO", 4))) { - buf = start; + || ((size == 3) && !fast<strncmp>(tag, "CPU", 3)) + || ((size == 7) && !fast<strncasecmp>(tag, "WARNING", 7)) + || ((size == 5) && !fast<strncasecmp>(tag, "ERROR", 5)) + || ((size == 4) && !fast<strncasecmp>(tag, "INFO", 4))) { + p = start; etag = tag = ""; } } // Suppress additional stutter in tag: // eg: [143:healthd]healthd -> [143:healthd] - size_t taglen = etag - tag; + taglen = etag - tag; // Mediatek-special printk induced stutter - char *np = strrchr(tag, ']'); - if (np && (++np < etag)) { - size_t s = etag - np; - if (((s + s) < taglen) && !strncmp(np, np - 1 - s, s)) { - taglen = np - tag; + const char *mp = strnrchr(tag, ']', taglen); + if (mp && (++mp < etag)) { + size_t s = etag - mp; + if (((s + s) < taglen) && !fast<memcmp>(mp, mp - 1 - s, s)) { + taglen = mp - tag; } } // skip leading space - while (isspace(*buf)) { - ++buf; + while ((isspace(*p) || !*p) && (p < &buf[len])) { + ++p; } - // truncate trailing space - size_t b = strlen(buf); - while (b && isspace(buf[b-1])) { + // truncate trailing space or nuls + size_t b = len - (p - buf); + while (b && (isspace(p[b-1]) || !p[b-1])) { --b; } // trick ... allow tag with empty content to be logged. log() drops empty if (!b && taglen) { - buf = " "; + p = " "; b = 1; } + if (b > LOGGER_ENTRY_MAX_PAYLOAD) { + b = LOGGER_ENTRY_MAX_PAYLOAD; + } size_t n = 1 + taglen + 1 + b + 1; - - // Allocate a buffer to hold the interpreted log message int rc = n; - char *newstr = reinterpret_cast<char *>(malloc(n)); - if (!newstr) { - rc = -ENOMEM; + if ((taglen > n) || (b > n)) { // Can not happen ... + rc = -EINVAL; return rc; } - np = newstr; + + char newstr[n]; + char *np = newstr; // Convert priority into single-byte Android logger priority *np = convertKernelPrioToAndroidPrio(pri); ++np; // Copy parsed tag following priority - strncpy(np, tag, taglen); + memcpy(np, tag, taglen); np += taglen; *np = '\0'; ++np; // Copy main message to the remainder - strncpy(np, buf, b); + memcpy(np, p, b); np[b] = '\0'; // Log message rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr, (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX); - free(newstr); // notify readers if (!rc) { diff --git a/logd/LogKlog.h b/logd/LogKlog.h index 081d344..469affd 100644 --- a/logd/LogKlog.h +++ b/logd/LogKlog.h @@ -21,14 +21,12 @@ #include <log/log_read.h> #include "LogReader.h" -char *log_strtok_r(char *str, char **saveptr); +char *log_strntok_r(char *s, size_t *len, char **saveptr, size_t *sublen); class LogKlog : public SocketListener { LogBuffer *logbuf; LogReader *reader; const log_time signature; - const int fdWrite; // /dev/kmsg - const int fdRead; // /proc/kmsg // Set once thread is started, separates KLOG_ACTION_READ_ALL // and KLOG_ACTION_READ phases. bool initialized; @@ -42,15 +40,16 @@ class LogKlog : public SocketListener { public: LogKlog(LogBuffer *buf, LogReader *reader, int fdWrite, int fdRead, bool auditd); - int log(const char *buf); - void synchronize(const char *buf); + int log(const char *buf, size_t len); + void synchronize(const char *buf, size_t len); static void convertMonotonicToReal(log_time &real) { real += correction; } protected: - void sniffTime(log_time &now, const char **buf, bool reverse); - pid_t sniffPid(const char *buf); - void calculateCorrection(const log_time &monotonic, const char *real_string); + void sniffTime(log_time &now, const char **buf, size_t len, bool reverse); + pid_t sniffPid(const char *buf, size_t len); + void calculateCorrection(const log_time &monotonic, + const char *real_string, size_t len); virtual bool onDataAvailable(SocketClient *cli); }; diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp index c7deec0..3833843 100644 --- a/logd/LogReader.cpp +++ b/logd/LogReader.cpp @@ -95,7 +95,7 @@ bool LogReader::onDataAvailable(SocketClient *cli) { } bool nonBlock = false; - if (strncmp(buffer, "dumpAndClose", 12) == 0) { + if (!fast<strncmp>(buffer, "dumpAndClose", 12)) { // Allow writer to get some cycles, and wait for pending notifications sched_yield(); LogTimeEntry::lock(); diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp index 48c2fe6..099a4c4 100644 --- a/logd/LogStatistics.cpp +++ b/logd/LogStatistics.cpp @@ -51,7 +51,7 @@ char *pidToName(pid_t pid) { if (ret > 0) { buffer[sizeof(buffer)-1] = '\0'; // frameworks intermediate state - if (strcmp(buffer, "<pre-initialized>")) { + if (fast<strcmp>(buffer, "<pre-initialized>")) { retval = strdup(buffer); } } @@ -170,7 +170,7 @@ char *LogStatistics::uidToName(uid_t uid) { if (n) { if (!name) { name = strdup(n); - } else if (strcmp(name, n)) { + } else if (fast<strcmp>(name, n)) { free(name); name = NULL; break; diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h index 760d6b2..42084de 100644 --- a/logd/LogStatistics.h +++ b/logd/LogStatistics.h @@ -26,6 +26,7 @@ #include <log/log.h> #include "LogBufferElement.h" +#include "LogUtils.h" #define log_id_for_each(i) \ for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t) (i + 1)) @@ -188,7 +189,7 @@ struct PidEntry : public EntryBaseDropped { const char*getName() const { return name; } inline void add(pid_t p) { - if (name && !strncmp(name, "zygote", 6)) { + if (name && !fast<strncmp>(name, "zygote", 6)) { free(name); name = NULL; } @@ -240,7 +241,7 @@ struct TidEntry : public EntryBaseDropped { const char*getName() const { return name; } inline void add(pid_t t) { - if (name && !strncmp(name, "zygote", 6)) { + if (name && !fast<strncmp>(name, "zygote", 6)) { free(name); name = NULL; } diff --git a/logd/LogUtils.h b/logd/LogUtils.h new file mode 100644 index 0000000..533eb1c --- /dev/null +++ b/logd/LogUtils.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012-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 _LOGD_LOG_UTILS_H__ +#define _LOGD_LOG_UTILS_H__ + +#include <sys/types.h> + +#include <log/log.h> + +// Hijack this header as a common include file used by most all sources +// to report some utilities defined here and there. + +namespace android { + +// Furnished in main.cpp. Caller must own and free returned value +char *uidToName(uid_t uid); + +// Furnished in LogStatistics.cpp. Caller must own and free returned value +char *pidToName(pid_t pid); +char *tidToName(pid_t tid); + +// Furnished in main.cpp. Thread safe. +const char *tagToName(uint32_t tag); + +} + +static inline bool worstUidEnabledForLogid(log_id_t id) { + return (id != LOG_ID_CRASH) && (id != LOG_ID_KERNEL) && (id != LOG_ID_EVENTS); +} + +template <int (*cmp)(const char *l, const char *r, const size_t s)> +static inline int fast(const char *l, const char *r, const size_t s) { + return (*l != *r) || cmp(l + 1, r + 1, s - 1); +} + +template <int (*cmp)(const void *l, const void *r, const size_t s)> +static inline int fast(const void *lv, const void *rv, const size_t s) { + const char *l = static_cast<const char *>(lv); + const char *r = static_cast<const char *>(rv); + return (*l != *r) || cmp(l + 1, r + 1, s - 1); +} + +template <int (*cmp)(const char *l, const char *r)> +static inline int fast(const char *l, const char *r) { + return (*l != *r) || cmp(l + 1, r + 1); +} + +#endif // _LOGD_LOG_UTILS_H__ diff --git a/logd/LogWhiteBlackList.h b/logd/LogWhiteBlackList.h index 57cd03b..99e3d82 100644 --- a/logd/LogWhiteBlackList.h +++ b/logd/LogWhiteBlackList.h @@ -21,7 +21,7 @@ #include <list> -#include <LogBufferElement.h> +#include "LogBufferElement.h" // White and Blacklist diff --git a/logd/main.cpp b/logd/main.cpp index a3241d0..2ed52be 100644 --- a/logd/main.cpp +++ b/logd/main.cpp @@ -47,6 +47,7 @@ #include "LogListener.h" #include "LogAudit.h" #include "LogKlog.h" +#include "LogUtils.h" #define KMSG_PRIORITY(PRI) \ '<', \ @@ -286,36 +287,37 @@ static void readDmesg(LogAudit *al, LogKlog *kl) { return; } - int len = klogctl(KLOG_SIZE_BUFFER, NULL, 0); - if (len <= 0) { + int rc = klogctl(KLOG_SIZE_BUFFER, NULL, 0); + if (rc <= 0) { return; } - len += 1024; // Margin for additional input race or trailing nul + size_t len = rc + 1024; // Margin for additional input race or trailing nul std::unique_ptr<char []> buf(new char[len]); - int rc = klogctl(KLOG_READ_ALL, buf.get(), len); + rc = klogctl(KLOG_READ_ALL, buf.get(), len); if (rc <= 0) { return; } - if (rc < len) { + if ((size_t)rc < len) { len = rc + 1; } - buf[len - 1] = '\0'; + buf[--len] = '\0'; if (kl) { - kl->synchronize(buf.get()); + kl->synchronize(buf.get(), len); } + size_t sublen; for (char *ptr = NULL, *tok = buf.get(); - (rc >= 0) && ((tok = log_strtok_r(tok, &ptr))); + (rc >= 0) && ((tok = log_strntok_r(tok, &len, &ptr, &sublen))); tok = NULL) { if (al) { - rc = al->log(tok); + rc = al->log(tok, sublen); } if (kl) { - rc = kl->log(tok); + rc = kl->log(tok, sublen); } } } diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c index 83576fb..44455d1 100644 --- a/logwrapper/logwrap.c +++ b/logwrapper/logwrap.c @@ -355,7 +355,8 @@ static int parent(const char *tag, int parent_read, pid_t pid, } if (poll_fds[0].revents & POLLIN) { - sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b); + sz = TEMP_FAILURE_RETRY( + read(parent_read, &buffer[b], sizeof(buffer) - 1 - b)); sz += b; // Log one line at a time @@ -490,7 +491,7 @@ int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int } /* Use ptty instead of socketpair so that STDOUT is not buffered */ - parent_ptty = open("/dev/ptmx", O_RDWR); + parent_ptty = TEMP_FAILURE_RETRY(open("/dev/ptmx", O_RDWR)); if (parent_ptty < 0) { ERROR("Cannot create parent ptty\n"); rc = -1; @@ -505,7 +506,7 @@ int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int goto err_ptty; } - child_ptty = open(child_devname, O_RDWR); + child_ptty = TEMP_FAILURE_RETRY(open(child_devname, O_RDWR)); if (child_ptty < 0) { ERROR("Cannot open child_ptty\n"); rc = -1; diff --git a/mkbootimg/Android.mk b/mkbootimg/Android.mk index 0c9b0c6..c020323 100644 --- a/mkbootimg/Android.mk +++ b/mkbootimg/Android.mk @@ -10,4 +10,33 @@ LOCAL_MODULE := mkbootimg include $(BUILD_HOST_EXECUTABLE) +include $(CLEAR_VARS) +LOCAL_SRC_FILES := unpackbootimg.c +LOCAL_MODULE := unpackbootimg +include $(BUILD_HOST_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := mkbootimg.c +LOCAL_STATIC_LIBRARIES := libmincrypt libcutils libc +LOCAL_MODULE := utility_mkbootimg +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_STEM := mkbootimg +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities +LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities +LOCAL_FORCE_STATIC_EXECUTABLE := true +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := unpackbootimg.c +LOCAL_STATIC_LIBRARIES := libcutils libc +LOCAL_MODULE := utility_unpackbootimg +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_STEM := unpackbootimg +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities +LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities +LOCAL_FORCE_STATIC_EXECUTABLE := true +include $(BUILD_EXECUTABLE) + $(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE)) diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h index 5ab6195..e66e269 100644 --- a/mkbootimg/bootimg.h +++ b/mkbootimg/bootimg.h @@ -43,7 +43,8 @@ struct boot_img_hdr uint32_t tags_addr; /* physical addr for kernel tags */ uint32_t page_size; /* flash page size we assume */ - uint32_t unused[2]; /* future expansion: should be 0 */ + uint32_t dt_size; /* device tree in bytes */ + uint32_t unused; /* future expansion: should be 0 */ uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */ @@ -66,10 +67,13 @@ struct boot_img_hdr ** +-----------------+ ** | second stage | o pages ** +-----------------+ +** | device tree | p pages +** +-----------------+ ** ** n = (kernel_size + page_size - 1) / page_size ** m = (ramdisk_size + page_size - 1) / page_size ** o = (second_size + page_size - 1) / page_size +** p = (dt_size + page_size - 1) / page_size ** ** 0. all entities are page_size aligned in flash ** 1. kernel and ramdisk are required (size != 0) diff --git a/mkbootimg/mkbootimg.c b/mkbootimg/mkbootimg.c index b6a2801..ec7c61f 100644 --- a/mkbootimg/mkbootimg.c +++ b/mkbootimg/mkbootimg.c @@ -66,6 +66,10 @@ int usage(void) " [ --board <boardname> ]\n" " [ --base <address> ]\n" " [ --pagesize <pagesize> ]\n" + " [ --dt <filename> ]\n" + " [ --ramdisk_offset <address> ]\n" + " [ --second_offset <address> ]\n" + " [ --tags_offset <address> ]\n" " [ --id ]\n" " -o|--output <filename>\n" ); @@ -74,11 +78,12 @@ int usage(void) -static unsigned char padding[16384] = { 0, }; +static unsigned char padding[131072] = { 0, }; static void print_id(const uint8_t *id, size_t id_len) { printf("0x"); - for (unsigned i = 0; i < id_len; i++) { + unsigned i = 0; + for (i = 0; i < id_len; i++) { printf("%02x", id[i]); } printf("\n"); @@ -115,6 +120,8 @@ int main(int argc, char **argv) char *cmdline = ""; char *bootimg = NULL; char *board = ""; + char *dt_fn = 0; + void *dt_data = 0; uint32_t pagesize = 2048; int fd; SHA_CTX ctx; @@ -167,10 +174,14 @@ int main(int argc, char **argv) } else if(!strcmp(arg,"--pagesize")) { pagesize = strtoul(val, 0, 10); if ((pagesize != 2048) && (pagesize != 4096) - && (pagesize != 8192) && (pagesize != 16384)) { + && (pagesize != 8192) && (pagesize != 16384) + && (pagesize != 32768) && (pagesize != 65536) + && (pagesize != 131072)) { fprintf(stderr,"error: unsupported page size %d\n", pagesize); return -1; } + } else if(!strcmp(arg, "--dt")) { + dt_fn = val; } else { return usage(); } @@ -243,6 +254,14 @@ int main(int argc, char **argv) } } + if(dt_fn) { + dt_data = load_file(dt_fn, &hdr.dt_size); + if (dt_data == 0) { + fprintf(stderr,"error: could not load device tree image '%s'\n", dt_fn); + return 1; + } + } + /* put a hash of the contents in the header so boot images can be * differentiated based on their first 2k. */ @@ -253,6 +272,10 @@ int main(int argc, char **argv) SHA_update(&ctx, &hdr.ramdisk_size, sizeof(hdr.ramdisk_size)); SHA_update(&ctx, second_data, hdr.second_size); SHA_update(&ctx, &hdr.second_size, sizeof(hdr.second_size)); + if(dt_data) { + SHA_update(&ctx, dt_data, hdr.dt_size); + SHA_update(&ctx, &hdr.dt_size, sizeof(hdr.dt_size)); + } sha = SHA_final(&ctx); memcpy(hdr.id, sha, SHA_DIGEST_SIZE > sizeof(hdr.id) ? sizeof(hdr.id) : SHA_DIGEST_SIZE); @@ -281,6 +304,10 @@ int main(int argc, char **argv) print_id((uint8_t *) hdr.id, sizeof(hdr.id)); } + if(dt_data) { + if(write(fd, dt_data, hdr.dt_size) != (ssize_t) hdr.dt_size) goto fail; + if(write_padding(fd, pagesize, hdr.dt_size)) goto fail; + } return 0; fail: diff --git a/mkbootimg/unpackbootimg.c b/mkbootimg/unpackbootimg.c new file mode 100644 index 0000000..3d2fda7 --- /dev/null +++ b/mkbootimg/unpackbootimg.c @@ -0,0 +1,211 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <libgen.h> + +#include "mincrypt/sha.h" +#include "bootimg.h" + +typedef unsigned char byte; + +int read_padding(FILE* f, unsigned itemsize, int pagesize) +{ + byte* buf = (byte*)malloc(sizeof(byte) * pagesize); + unsigned pagemask = pagesize - 1; + unsigned count; + + if((itemsize & pagemask) == 0) { + free(buf); + return 0; + } + + count = pagesize - (itemsize & pagemask); + + fread(buf, count, 1, f); + free(buf); + return count; +} + +void write_string_to_file(char* file, char* string) +{ + FILE* f = fopen(file, "w"); + fwrite(string, strlen(string), 1, f); + fwrite("\n", 1, 1, f); + fclose(f); +} + +int usage() { + printf("usage: unpackbootimg\n"); + printf("\t-i|--input boot.img\n"); + printf("\t[ -o|--output output_directory]\n"); + printf("\t[ -p|--pagesize <size-in-hexadecimal> ]\n"); + return 0; +} + +int main(int argc, char** argv) +{ + char tmp[PATH_MAX]; + char* directory = "./"; + char* filename = NULL; + int pagesize = 0; + + argc--; + argv++; + while(argc > 0){ + char *arg = argv[0]; + char *val = argv[1]; + argc -= 2; + argv += 2; + if(!strcmp(arg, "--input") || !strcmp(arg, "-i")) { + filename = val; + } else if(!strcmp(arg, "--output") || !strcmp(arg, "-o")) { + directory = val; + } else if(!strcmp(arg, "--pagesize") || !strcmp(arg, "-p")) { + pagesize = strtoul(val, 0, 16); + } else { + return usage(); + } + } + + if (filename == NULL) { + return usage(); + } + + int total_read = 0; + FILE* f = fopen(filename, "rb"); + boot_img_hdr header; + + //printf("Reading header...\n"); + int i; + for (i = 0; i <= 512; i++) { + fseek(f, i, SEEK_SET); + fread(tmp, BOOT_MAGIC_SIZE, 1, f); + if (memcmp(tmp, BOOT_MAGIC, BOOT_MAGIC_SIZE) == 0) + break; + } + total_read = i; + if (i > 512) { + printf("Android boot magic not found.\n"); + return 1; + } + fseek(f, i, SEEK_SET); + printf("Android magic found at: %d\n", i); + + fread(&header, sizeof(header), 1, f); + printf("BOARD_KERNEL_CMDLINE %s\n", header.cmdline); + printf("BOARD_KERNEL_BASE %08x\n", header.kernel_addr - 0x00008000); + printf("BOARD_RAMDISK_OFFSET %08x\n", header.ramdisk_addr - header.kernel_addr + 0x00008000); + printf("BOARD_SECOND_OFFSET %08x\n", header.second_addr - header.kernel_addr + 0x00008000); + printf("BOARD_TAGS_OFFSET %08x\n",header.tags_addr - header.kernel_addr + 0x00008000); + printf("BOARD_PAGE_SIZE %d\n", header.page_size); + printf("BOARD_SECOND_SIZE %d\n", header.second_size); + printf("BOARD_DT_SIZE %d\n", header.dt_size); + + if (pagesize == 0) { + pagesize = header.page_size; + } + + //printf("cmdline...\n"); + sprintf(tmp, "%s/%s", directory, basename(filename)); + strcat(tmp, "-cmdline"); + write_string_to_file(tmp, header.cmdline); + + //printf("base...\n"); + sprintf(tmp, "%s/%s", directory, basename(filename)); + strcat(tmp, "-base"); + char basetmp[200]; + sprintf(basetmp, "%08x", header.kernel_addr - 0x00008000); + write_string_to_file(tmp, basetmp); + + //printf("ramdisk_offset...\n"); + sprintf(tmp, "%s/%s", directory, basename(filename)); + strcat(tmp, "-ramdisk_offset"); + char ramdisktmp[200]; + sprintf(ramdisktmp, "%08x", header.ramdisk_addr - header.kernel_addr + 0x00008000); + write_string_to_file(tmp, ramdisktmp); + + //printf("second_offset...\n"); + sprintf(tmp, "%s/%s", directory, basename(filename)); + strcat(tmp, "-second_offset"); + char secondtmp[200]; + sprintf(secondtmp, "%08x", header.second_addr - header.kernel_addr + 0x00008000); + write_string_to_file(tmp, secondtmp); + + //printf("tags_offset...\n"); + sprintf(tmp, "%s/%s", directory, basename(filename)); + strcat(tmp, "-tags_offset"); + char tagstmp[200]; + sprintf(tagstmp, "%08x", header.tags_addr - header.kernel_addr + 0x00008000); + write_string_to_file(tmp, tagstmp); + + //printf("pagesize...\n"); + sprintf(tmp, "%s/%s", directory, basename(filename)); + strcat(tmp, "-pagesize"); + char pagesizetmp[200]; + sprintf(pagesizetmp, "%d", header.page_size); + write_string_to_file(tmp, pagesizetmp); + + total_read += sizeof(header); + //printf("total read: %d\n", total_read); + total_read += read_padding(f, sizeof(header), pagesize); + + sprintf(tmp, "%s/%s", directory, basename(filename)); + strcat(tmp, "-zImage"); + FILE *k = fopen(tmp, "wb"); + byte* kernel = (byte*)malloc(header.kernel_size); + //printf("Reading kernel...\n"); + fread(kernel, header.kernel_size, 1, f); + total_read += header.kernel_size; + fwrite(kernel, header.kernel_size, 1, k); + fclose(k); + + //printf("total read: %d\n", header.kernel_size); + total_read += read_padding(f, header.kernel_size, pagesize); + + + byte* ramdisk = (byte*)malloc(header.ramdisk_size); + //printf("Reading ramdisk...\n"); + fread(ramdisk, header.ramdisk_size, 1, f); + total_read += header.ramdisk_size; + sprintf(tmp, "%s/%s", directory, basename(filename)); + if(ramdisk[0] == 0x02 && ramdisk[1]== 0x21) + strcat(tmp, "-ramdisk.lz4"); + else + strcat(tmp, "-ramdisk.gz"); + FILE *r = fopen(tmp, "wb"); + fwrite(ramdisk, header.ramdisk_size, 1, r); + fclose(r); + + total_read += read_padding(f, header.ramdisk_size, pagesize); + + sprintf(tmp, "%s/%s", directory, basename(filename)); + strcat(tmp, "-second"); + FILE *s = fopen(tmp, "wb"); + byte* second = (byte*)malloc(header.second_size); + //printf("Reading second...\n"); + fread(second, header.second_size, 1, f); + total_read += header.second_size; + fwrite(second, header.second_size, 1, r); + fclose(s); + + total_read += read_padding(f, header.second_size, pagesize); + + sprintf(tmp, "%s/%s", directory, basename(filename)); + strcat(tmp, "-dt"); + FILE *d = fopen(tmp, "wb"); + byte* dt = (byte*)malloc(header.dt_size); + //printf("Reading dt...\n"); + fread(dt, header.dt_size, 1, f); + total_read += header.dt_size; + fwrite(dt, header.dt_size, 1, r); + fclose(d); + + fclose(f); + + //printf("Total Read: %d\n", total_read); + return 0; +} diff --git a/rootdir/Android.mk b/rootdir/Android.mk index 7ab76b8..836e585 100644 --- a/rootdir/Android.mk +++ b/rootdir/Android.mk @@ -30,16 +30,21 @@ LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \ include $(BUILD_SYSTEM)/base_rules.mk -# Regenerate init.environ.rc if PRODUCT_BOOTCLASSPATH has changed. -bcp_md5 := $(word 1, $(shell echo $(PRODUCT_BOOTCLASSPATH) $(PRODUCT_SYSTEM_SERVER_CLASSPATH) | $(MD5SUM))) +# Regenerate init.environ.rc if PRODUCT_BOOTCLASSPATH or TARGET_LDPRELOAD has changed. +bcp_md5 := $(word 1, $(shell echo $(PRODUCT_BOOTCLASSPATH) $(PRODUCT_SYSTEM_SERVER_CLASSPATH) $(TARGET_LDPRELOAD) | $(MD5SUM))) bcp_dep := $(intermediates)/$(bcp_md5).bcp.dep $(bcp_dep) : $(hide) mkdir -p $(dir $@) && rm -rf $(dir $@)*.bcp.dep && touch $@ +ifneq ($(strip $(TARGET_LDPRELOAD)),) + TARGET_LDPRELOAD_STR := :$(TARGET_LDPRELOAD) +endif + $(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in $(bcp_dep) @echo "Generate: $< -> $@" @mkdir -p $(dir $@) - $(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@ + $(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g'\ + -e 's?%TARGET_LDPRELOAD%?$(TARGET_LDPRELOAD_STR)?g' $< >$@ $(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@ bcp_md5 := diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in index b34ea01..46ec1fb 100644 --- a/rootdir/init.environ.rc.in +++ b/rootdir/init.environ.rc.in @@ -9,3 +9,4 @@ on init export ASEC_MOUNTPOINT /mnt/asec export BOOTCLASSPATH %BOOTCLASSPATH% export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH% + export LD_PRELOAD libsigchain.so%TARGET_LDPRELOAD% diff --git a/rootdir/init.rc b/rootdir/init.rc index 317207c..ec5a2f6 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -10,6 +10,9 @@ import /init.${ro.hardware}.rc import /init.usb.configfs.rc import /init.${ro.zygote}.rc import /init.trace.rc +# Include CM's extra init file +import /init.cm.rc + on early-init # Set init and its forked children's oom_adj. @@ -18,6 +21,8 @@ on early-init # Set the security context of /adb_keys if present. restorecon /adb_keys + mount debugfs /sys/kernel/debug /sys/kernel/debug mode=755 + start ueventd on init @@ -47,6 +52,7 @@ on init write /sys/fs/cgroup/memory/sw/memory.move_charge_at_immigrate 1 chown root system /sys/fs/cgroup/memory/sw/tasks chmod 0660 /sys/fs/cgroup/memory/sw/tasks + chmod 0220 /sys/fs/cgroup/memory/cgroup.event_control mkdir /system mkdir /data 0771 system system @@ -165,13 +171,19 @@ on init chown system system /dev/cpuset/foreground chown system system /dev/cpuset/foreground/boost chown system system /dev/cpuset/background + chown system system /dev/cpuset/system-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 + chown system system /dev/cpuset/system-background/tasks + + # set system-background to 0775 so SurfaceFlinger can touch it + chmod 0775 /dev/cpuset/system-background chmod 0664 /dev/cpuset/foreground/tasks chmod 0664 /dev/cpuset/foreground/boost/tasks chmod 0664 /dev/cpuset/background/tasks + chmod 0664 /dev/cpuset/system-background/tasks chmod 0664 /dev/cpuset/tasks @@ -390,6 +402,9 @@ on post-fs-data # Set SELinux security contexts on upgrade or policy update. restorecon_recursive /data + restorecon /data/data + restorecon /data/user + restorecon /data/user/0 # Check any timezone data in /data is newer than the copy in /system, delete if not. exec - system system -- /system/bin/tzdatacheck /system/usr/share/zoneinfo /data/misc/zoneinfo @@ -639,14 +654,13 @@ service ril-daemon /system/bin/rild socket sap_uim_socket1 stream 660 bluetooth bluetooth socket rild-debug stream 660 radio system user root - group radio cache inet misc audio log + group radio cache inet misc audio log qcom_diag service surfaceflinger /system/bin/surfaceflinger class core user system group graphics drmrpc onrestart restart zygote - writepid /dev/cpuset/system-background/tasks service drm /system/bin/drmserver class main @@ -656,7 +670,7 @@ service drm /system/bin/drmserver service media /system/bin/mediaserver class main user media - group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm + group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm qcom_diag ioprio rt 4 # One shot invocation to deal with encrypted volume. @@ -679,6 +693,7 @@ service bootanim /system/bin/bootanimation group graphics audio disabled oneshot + writepid /dev/cpuset/system-background/tasks service gatekeeperd /system/bin/gatekeeperd /data/misc/gatekeeper class late_start @@ -691,6 +706,11 @@ service installd /system/bin/installd service flash_recovery /system/bin/install-recovery.sh class main oneshot + disabled + +# update recovery if enabled +on property:persist.sys.recovery_update=true + start flash_recovery service racoon /system/bin/racoon class main @@ -747,10 +767,10 @@ on property:persist.logd.logpersistd=logcatd # all exec/services are called with umask(077), so no gain beyond 0700 mkdir /data/misc/logd 0700 logd log # logd for write to /data/misc/logd, log group for read from pstore (-L) - exec - logd log -- /system/bin/logcat -L -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 64 -n 256 + exec - logd log -- /system/bin/logcat -L -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n 256 start logcatd -service logcatd /system/bin/logcat -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 64 -n 256 +service logcatd /system/bin/logcat -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n 256 class late_start disabled # logd for write to /data/misc/logd, log group for read from log daemon diff --git a/rootdir/init.trace.rc b/rootdir/init.trace.rc index 50944e6..4933156 100644 --- a/rootdir/init.trace.rc +++ b/rootdir/init.trace.rc @@ -12,6 +12,7 @@ on boot chown root shell /sys/kernel/debug/tracing/options/print-tgid chown root shell /sys/kernel/debug/tracing/events/sched/sched_switch/enable chown root shell /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable + chown root shell /sys/kernel/debug/tracing/events/sched/sched_blocked_reason/enable chown root shell /sys/kernel/debug/tracing/events/power/cpu_frequency/enable chown root shell /sys/kernel/debug/tracing/events/power/cpu_idle/enable chown root shell /sys/kernel/debug/tracing/events/power/clock_set_rate/enable @@ -24,6 +25,7 @@ on boot chmod 0664 /sys/kernel/debug/tracing/options/print-tgid chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_switch/enable chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable + chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_blocked_reason/enable chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_frequency/enable chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_idle/enable chmod 0664 /sys/kernel/debug/tracing/events/power/clock_set_rate/enable diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc index b735dc3..67eaabc 100644 --- a/rootdir/ueventd.rc +++ b/rootdir/ueventd.rc @@ -28,7 +28,7 @@ subsystem adf /dev/dri/* 0666 root graphics # these should not be world writable -/dev/diag 0660 radio radio +/dev/diag 0660 system qcom_diag /dev/diag_arm9 0660 radio radio /dev/android_adb 0660 adb adb /dev/android_adb_enable 0660 adb adb @@ -98,3 +98,4 @@ subsystem adf # DVB API device nodes /dev/dvb* 0660 root system + diff --git a/sdcard/Android.mk b/sdcard/Android.mk index cb3a8fb..b50a818 100644 --- a/sdcard/Android.mk +++ b/sdcard/Android.mk @@ -1,11 +1,21 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) +ifeq ($(call is-vendor-board-platform,QCOM),true) +LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include +LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr +endif LOCAL_SRC_FILES := sdcard.c -LOCAL_MODULE := sdcard +LOCAL_MODULE := libsdcard LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror +LOCAL_MODULE_TAGS := optional +include $(BUILD_STATIC_LIBRARY) -LOCAL_SHARED_LIBRARIES := libcutils - +include $(CLEAR_VARS) +LOCAL_SRC_FILES := main.c +LOCAL_MODULE := sdcard +LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror +LOCAL_STATIC_LIBRARIES := libsdcard +LOCAL_SHARED_LIBRARIES := libc libcutils include $(BUILD_EXECUTABLE) diff --git a/sdcard/main.c b/sdcard/main.c new file mode 100644 index 0000000..ad2405b --- /dev/null +++ b/sdcard/main.c @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2010 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. + */ + +extern int sdcard_main(int argc, char **argv); + +int main(int argc, char **argv) { + return sdcard_main(argc, argv); +} diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index 123fce6..98af250 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -1214,7 +1214,13 @@ static int handle_open(struct fuse* fuse, struct fuse_handler* handler, } out.fh = ptr_to_id(h); out.open_flags = 0; + +#if defined(FUSE_STACKED_IO) || defined(FUSE_SHORTCIRCUIT) + out.lower_fd = h->fd; +#else out.padding = 0; +#endif + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); return NO_STATUS; } @@ -1378,7 +1384,13 @@ static int handle_opendir(struct fuse* fuse, struct fuse_handler* handler, } out.fh = ptr_to_id(h); out.open_flags = 0; + +#if defined(FUSE_STACKED_IO) || defined(FUSE_SHORTCIRCUIT) + out.lower_fd = -1; +#else out.padding = 0; +#endif + fuse_reply(fuse, hdr->unique, &out, sizeof(out)); return NO_STATUS; } @@ -1460,6 +1472,14 @@ static int handle_init(struct fuse* fuse, struct fuse_handler* handler, out.major = FUSE_KERNEL_VERSION; out.max_readahead = req->max_readahead; out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES; + +#ifdef FUSE_SHORTCIRCUIT + out.flags |= FUSE_SHORTCIRCUIT; +#endif +#ifdef FUSE_STACKED_IO + out.flags |= FUSE_STACKED_IO; +#endif + out.max_background = 32; out.congestion_threshold = 32; out.max_write = MAX_WRITE; @@ -1903,7 +1923,7 @@ static void run(const char* source_path, const char* label, uid_t uid, exit(1); } -int main(int argc, char **argv) { +int sdcard_main(int argc, char **argv) { const char *source_path = NULL; const char *label = NULL; uid_t uid = 0; diff --git a/toolbox/Android.mk b/toolbox/Android.mk index ad99a39..b17408a 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -25,40 +25,27 @@ LOCAL_CFLAGS += $(common_cflags) -Dmain=dd_main -DNO_CONV LOCAL_MODULE := libtoolbox_dd include $(BUILD_STATIC_LIBRARY) -include $(CLEAR_VARS) -LOCAL_SRC_FILES := upstream-netbsd/usr.bin/du/du.c -LOCAL_CFLAGS += $(common_cflags) -Dmain=du_main -LOCAL_MODULE := libtoolbox_du -include $(BUILD_STATIC_LIBRARY) - include $(CLEAR_VARS) BSD_TOOLS := \ dd \ - du \ OUR_TOOLS := \ - df \ getevent \ iftop \ ioctl \ - ionice \ log \ ls \ - lsof \ - mount \ nandread \ newfs_msdos \ ps \ prlimit \ - renice \ + restart \ sendevent \ start \ stop \ top \ - uptime \ - watchprops \ ALL_TOOLS = $(BSD_TOOLS) $(OUR_TOOLS) diff --git a/toolbox/df.c b/toolbox/df.c deleted file mode 100644 index 9cd0743..0000000 --- a/toolbox/df.c +++ /dev/null @@ -1,85 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <sys/statfs.h> - -static int ok = EXIT_SUCCESS; - -static void printsize(long long n) -{ - char unit = 'K'; - long long t; - - n *= 10; - - if (n > 1024*1024*10) { - n /= 1024; - unit = 'M'; - } - - if (n > 1024*1024*10) { - n /= 1024; - unit = 'G'; - } - - t = (n + 512) / 1024; - printf("%4lld.%1lld%c", t/10, t%10, unit); -} - -static void df(char *s, int always) { - struct statfs st; - - if (statfs(s, &st) < 0) { - fprintf(stderr, "%s: %s\n", s, strerror(errno)); - ok = EXIT_FAILURE; - } else { - if (st.f_blocks == 0 && !always) - return; - printf("%-20s ", s); - printsize((long long)st.f_blocks * (long long)st.f_bsize); - printf(" "); - printsize((long long)(st.f_blocks - (long long)st.f_bfree) * st.f_bsize); - printf(" "); - printsize((long long)st.f_bfree * (long long)st.f_bsize); - printf(" %d\n", (int) st.f_bsize); - } -} - -int df_main(int argc, char *argv[]) { - printf("Filesystem Size Used Free Blksize\n"); - if (argc == 1) { - char s[2000]; - FILE *f = fopen("/proc/mounts", "r"); - - while (fgets(s, 2000, f)) { - char *c, *e = s; - - for (c = s; *c; c++) { - if (*c == ' ') { - e = c + 1; - break; - } - } - - for (c = e; *c; c++) { - if (*c == ' ') { - *c = '\0'; - break; - } - } - - df(e, 0); - } - - fclose(f); - } else { - int i; - - for (i = 1; i < argc; i++) { - df(argv[i], 1); - } - } - - exit(ok); -} diff --git a/toolbox/ionice.c b/toolbox/ionice.c deleted file mode 100644 index 7abc261..0000000 --- a/toolbox/ionice.c +++ /dev/null @@ -1,58 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> - -#include <cutils/iosched_policy.h> - -static char *classes[] = {"none", "rt", "be", "idle", NULL}; - -int ionice_main(int argc, char *argv[]) -{ - IoSchedClass clazz = IoSchedClass_NONE; - int ioprio = 0; - int pid; - - if(argc != 2 && argc != 4) { - fprintf(stderr, "usage: ionice <pid> [none|rt|be|idle] [prio]\n"); - return 1; - } - - if (!(pid = atoi(argv[1]))) { - fprintf(stderr, "Invalid pid specified\n"); - return 1; - } - - if (argc == 2) { - if (android_get_ioprio(pid, &clazz, &ioprio)) { - fprintf(stderr, "Failed to read priority (%s)\n", strerror(errno)); - return 1; - } - fprintf(stdout, "Pid %d, class %s (%d), prio %d\n", pid, classes[clazz], clazz, ioprio); - return 0; - } - - if (!strcmp(argv[2], "none")) { - clazz = IoSchedClass_NONE; - } else if (!strcmp(argv[2], "rt")) { - clazz = IoSchedClass_RT; - } else if (!strcmp(argv[2], "be")) { - clazz = IoSchedClass_BE; - } else if (!strcmp(argv[2], "idle")) { - clazz = IoSchedClass_IDLE; - } else { - fprintf(stderr, "Unsupported class '%s'\n", argv[2]); - return 1; - } - - ioprio = atoi(argv[3]); - - printf("Setting pid %d i/o class to %d, prio %d\n", pid, clazz, ioprio); - if (android_set_ioprio(pid, clazz, ioprio)) { - fprintf(stderr, "Failed to set priority (%s)\n", strerror(errno)); - return 1; - } - - return 0; -} diff --git a/toolbox/lsof.c b/toolbox/lsof.c deleted file mode 100644 index 982f5aa..0000000 --- a/toolbox/lsof.c +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (c) 2010, 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. - * * Neither the name of Google, Inc. 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 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 <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <libgen.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include <pwd.h> -#include <sys/stat.h> - -#define BUF_MAX 1024 -#define CMD_DISPLAY_MAX (9 + 1) -#define USER_DISPLAY_MAX (10 + 1) - -struct pid_info_t { - pid_t pid; - char user[USER_DISPLAY_MAX]; - - char cmdline[CMD_DISPLAY_MAX]; - - char path[PATH_MAX]; - ssize_t parent_length; -}; - -static void print_header() -{ - printf("%-9s %5s %10s %4s %9s %18s %9s %10s %s\n", - "COMMAND", - "PID", - "USER", - "FD", - "TYPE", - "DEVICE", - "SIZE/OFF", - "NODE", - "NAME"); -} - -static void print_type(char *type, struct pid_info_t* info) -{ - static ssize_t link_dest_size; - static char link_dest[PATH_MAX]; - - strlcat(info->path, type, sizeof(info->path)); - if ((link_dest_size = readlink(info->path, link_dest, sizeof(link_dest)-1)) < 0) { - if (errno == ENOENT) - goto out; - - snprintf(link_dest, sizeof(link_dest), "%s (readlink: %s)", info->path, strerror(errno)); - } else { - link_dest[link_dest_size] = '\0'; - } - - // Things that are just the root filesystem are uninteresting (we already know) - if (!strcmp(link_dest, "/")) - goto out; - - printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n", - info->cmdline, info->pid, info->user, type, - "???", "???", "???", "???", link_dest); - -out: - info->path[info->parent_length] = '\0'; -} - -// Prints out all file that have been memory mapped -static void print_maps(struct pid_info_t* info) -{ - FILE *maps; - size_t offset; - char device[10]; - long int inode; - char file[PATH_MAX]; - - strlcat(info->path, "maps", sizeof(info->path)); - - maps = fopen(info->path, "r"); - if (!maps) - goto out; - - while (fscanf(maps, "%*x-%*x %*s %zx %s %ld %s\n", &offset, device, &inode, - file) == 4) { - // We don't care about non-file maps - if (inode == 0 || !strcmp(device, "00:00")) - continue; - - printf("%-9s %5d %10s %4s %9s %18s %9zd %10ld %s\n", - info->cmdline, info->pid, info->user, "mem", - "???", device, offset, inode, file); - } - - fclose(maps); - -out: - info->path[info->parent_length] = '\0'; -} - -// Prints out all open file descriptors -static void print_fds(struct pid_info_t* info) -{ - static char* fd_path = "fd/"; - strlcat(info->path, fd_path, sizeof(info->path)); - - int previous_length = info->parent_length; - info->parent_length += strlen(fd_path); - - DIR *dir = opendir(info->path); - if (dir == NULL) { - char msg[BUF_MAX]; - snprintf(msg, sizeof(msg), "%s (opendir: %s)", info->path, strerror(errno)); - printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n", - info->cmdline, info->pid, info->user, "FDS", - "", "", "", "", msg); - goto out; - } - - struct dirent* de; - while ((de = readdir(dir))) { - if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) - continue; - - print_type(de->d_name, info); - } - closedir(dir); - -out: - info->parent_length = previous_length; - info->path[info->parent_length] = '\0'; -} - -static void lsof_dumpinfo(pid_t pid) -{ - int fd; - struct pid_info_t info; - struct stat pidstat; - struct passwd *pw; - - info.pid = pid; - snprintf(info.path, sizeof(info.path), "/proc/%d/", pid); - info.parent_length = strlen(info.path); - - // Get the UID by calling stat on the proc/pid directory. - if (!stat(info.path, &pidstat)) { - pw = getpwuid(pidstat.st_uid); - if (pw) { - strlcpy(info.user, pw->pw_name, sizeof(info.user)); - } else { - snprintf(info.user, USER_DISPLAY_MAX, "%d", (int)pidstat.st_uid); - } - } else { - strcpy(info.user, "???"); - } - - // Read the command line information; each argument is terminated with NULL. - strlcat(info.path, "cmdline", sizeof(info.path)); - fd = open(info.path, O_RDONLY); - if (fd < 0) { - fprintf(stderr, "Couldn't read %s\n", info.path); - return; - } - - char cmdline[PATH_MAX]; - int numRead = read(fd, cmdline, sizeof(cmdline) - 1); - close(fd); - - if (numRead < 0) { - fprintf(stderr, "Error reading cmdline: %s: %s\n", info.path, strerror(errno)); - return; - } - - cmdline[numRead] = '\0'; - - // We only want the basename of the cmdline - strlcpy(info.cmdline, basename(cmdline), sizeof(info.cmdline)); - - // Read each of these symlinks - print_type("cwd", &info); - print_type("exe", &info); - print_type("root", &info); - - print_fds(&info); - print_maps(&info); -} - -int lsof_main(int argc, char *argv[]) -{ - long int pid = 0; - char* endptr; - if (argc == 2) { - pid = strtol(argv[1], &endptr, 10); - } - - print_header(); - - if (pid) { - lsof_dumpinfo(pid); - } else { - DIR *dir = opendir("/proc"); - if (dir == NULL) { - fprintf(stderr, "Couldn't open /proc\n"); - return -1; - } - - struct dirent* de; - while ((de = readdir(dir))) { - if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) - continue; - - // Only inspect directories that are PID numbers - pid = strtol(de->d_name, &endptr, 10); - if (*endptr != '\0') - continue; - - lsof_dumpinfo(pid); - } - closedir(dir); - } - - return 0; -} diff --git a/toolbox/mount.c b/toolbox/mount.c deleted file mode 100644 index 66ae8b1..0000000 --- a/toolbox/mount.c +++ /dev/null @@ -1,360 +0,0 @@ -/* - * mount.c, by rmk - */ - -#include <sys/mount.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include <linux/loop.h> - -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) - -#define DEFAULT_LOOP_DEVICE "/dev/block/loop0" -#define LOOPDEV_MAXLEN 64 - -struct mount_opts { - const char str[16]; - unsigned long rwmask; - unsigned long rwset; - unsigned long rwnoset; -}; - -struct extra_opts { - char *str; - char *end; - int used_size; - int alloc_size; -}; - -/* - * These options define the function of "mount(2)". - */ -#define MS_TYPE (MS_REMOUNT|MS_BIND|MS_MOVE) - - -static const struct mount_opts options[] = { - /* name mask set noset */ - { "async", MS_SYNCHRONOUS, 0, MS_SYNCHRONOUS }, - { "atime", MS_NOATIME, 0, MS_NOATIME }, - { "bind", MS_TYPE, MS_BIND, 0, }, - { "dev", MS_NODEV, 0, MS_NODEV }, - { "diratime", MS_NODIRATIME, 0, MS_NODIRATIME }, - { "dirsync", MS_DIRSYNC, MS_DIRSYNC, 0 }, - { "exec", MS_NOEXEC, 0, MS_NOEXEC }, - { "move", MS_TYPE, MS_MOVE, 0 }, - { "recurse", MS_REC, MS_REC, 0 }, - { "rec", MS_REC, MS_REC, 0 }, - { "remount", MS_TYPE, MS_REMOUNT, 0 }, - { "ro", MS_RDONLY, MS_RDONLY, 0 }, - { "rw", MS_RDONLY, 0, MS_RDONLY }, - { "suid", MS_NOSUID, 0, MS_NOSUID }, - { "sync", MS_SYNCHRONOUS, MS_SYNCHRONOUS, 0 }, - { "verbose", MS_VERBOSE, MS_VERBOSE, 0 }, - { "unbindable", MS_UNBINDABLE, MS_UNBINDABLE, 0 }, - { "private", MS_PRIVATE, MS_PRIVATE, 0 }, - { "slave", MS_SLAVE, MS_SLAVE, 0 }, - { "shared", MS_SHARED, MS_SHARED, 0 }, -}; - -static void add_extra_option(struct extra_opts *extra, char *s) -{ - int len = strlen(s); - int newlen; - - if (extra->str) - len++; /* +1 for ',' */ - newlen = extra->used_size + len; - - if (newlen >= extra->alloc_size) { - char *new; - - new = realloc(extra->str, newlen + 1); /* +1 for NUL */ - if (!new) - return; - - extra->str = new; - extra->end = extra->str + extra->used_size; - extra->alloc_size = newlen + 1; - } - - if (extra->used_size) { - *extra->end = ','; - extra->end++; - } - strcpy(extra->end, s); - extra->used_size += len; - -} - -static unsigned long -parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra, int* loop, char *loopdev) -{ - char *s; - - *loop = 0; - while ((s = strsep(&arg, ",")) != NULL) { - char *opt = s; - unsigned int i; - int res, no = s[0] == 'n' && s[1] == 'o'; - - if (no) - s += 2; - - if (strncmp(s, "loop=", 5) == 0) { - *loop = 1; - strlcpy(loopdev, s + 5, LOOPDEV_MAXLEN); - continue; - } - - if (strcmp(s, "loop") == 0) { - *loop = 1; - strlcpy(loopdev, DEFAULT_LOOP_DEVICE, LOOPDEV_MAXLEN); - continue; - } - for (i = 0, res = 1; i < ARRAY_SIZE(options); i++) { - res = strcmp(s, options[i].str); - - if (res == 0) { - rwflag &= ~options[i].rwmask; - if (no) - rwflag |= options[i].rwnoset; - else - rwflag |= options[i].rwset; - } - if (res <= 0) - break; - } - - if (res != 0 && s[0]) - add_extra_option(extra, opt); - } - - return rwflag; -} - -/* - * Mark the given block device as read-write, using the BLKROSET ioctl. - */ -static void fs_set_blk_rw(const char *blockdev) -{ - int fd; - int OFF = 0; - - fd = open(blockdev, O_RDONLY); - if (fd < 0) { - // should never happen - return; - } - - ioctl(fd, BLKROSET, &OFF); - close(fd); -} - -static char *progname; - -static struct extra_opts extra; -static unsigned long rwflag; - -static int -do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data, int loop, - char *loopdev) -{ - char *s; - int error = 0; - - if (loop) { - int file_fd, device_fd; - int flags; - - flags = (rwflag & MS_RDONLY) ? O_RDONLY : O_RDWR; - - file_fd = open(dev, flags); - if (file_fd < 0) { - perror("open backing file failed"); - return 1; - } - device_fd = open(loopdev, flags); - if (device_fd < 0) { - perror("open loop device failed"); - close(file_fd); - return 1; - } - if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0) { - perror("ioctl LOOP_SET_FD failed"); - close(file_fd); - close(device_fd); - return 1; - } - - close(file_fd); - close(device_fd); - dev = loopdev; - } - - if ((rwflag & MS_RDONLY) == 0) { - fs_set_blk_rw(dev); - } - - while ((s = strsep(&type, ",")) != NULL) { -retry: - if (mount(dev, dir, s, rwflag, data) == -1) { - error = errno; - /* - * If the filesystem is not found, or the - * superblock is invalid, try the next. - */ - if (error == ENODEV || error == EINVAL) - continue; - - /* - * If we get EACCESS, and we're trying to - * mount readwrite and this isn't a remount, - * try read only. - */ - if (error == EACCES && - (rwflag & (MS_REMOUNT|MS_RDONLY)) == 0) { - rwflag |= MS_RDONLY; - goto retry; - } - break; - } - } - - if (error) { - errno = error; - perror("mount"); - return 255; - } - - return 0; -} - -static int print_mounts() -{ - FILE* f; - int length; - char buffer[100]; - - f = fopen("/proc/mounts", "r"); - if (!f) { - fprintf(stdout, "could not open /proc/mounts\n"); - return -1; - } - - do { - length = fread(buffer, 1, 100, f); - if (length > 0) - fwrite(buffer, 1, length, stdout); - } while (length > 0); - - fclose(f); - return 0; -} - -static int get_mounts_dev_dir(const char *arg, char **dev, char **dir) -{ - FILE *f; - char mount_dev[256]; - char mount_dir[256]; - char mount_type[256]; - char mount_opts[256]; - int mount_freq; - int mount_passno; - int match; - - f = fopen("/proc/mounts", "r"); - if (!f) { - fprintf(stdout, "could not open /proc/mounts\n"); - return -1; - } - - do { - match = fscanf(f, "%255s %255s %255s %255s %d %d\n", - mount_dev, mount_dir, mount_type, - mount_opts, &mount_freq, &mount_passno); - mount_dev[255] = 0; - mount_dir[255] = 0; - mount_type[255] = 0; - mount_opts[255] = 0; - if (match == 6 && - (strcmp(arg, mount_dev) == 0 || - strcmp(arg, mount_dir) == 0)) { - *dev = strdup(mount_dev); - *dir = strdup(mount_dir); - fclose(f); - return 0; - } - } while (match != EOF); - - fclose(f); - return -1; -} - -int mount_main(int argc, char *argv[]) -{ - char *type = NULL; - char *dev = NULL; - char *dir = NULL; - int c; - int loop = 0; - char loopdev[LOOPDEV_MAXLEN]; - - progname = argv[0]; - rwflag = MS_VERBOSE; - - // mount with no arguments is equivalent to "cat /proc/mounts" - if (argc == 1) return print_mounts(); - - do { - c = getopt(argc, argv, "o:rt:w"); - if (c == EOF) - break; - switch (c) { - case 'o': - rwflag = parse_mount_options(optarg, rwflag, &extra, &loop, loopdev); - break; - case 'r': - rwflag |= MS_RDONLY; - break; - case 't': - type = optarg; - break; - case 'w': - rwflag &= ~MS_RDONLY; - break; - case '?': - fprintf(stderr, "%s: invalid option -%c\n", - progname, optopt); - exit(1); - } - } while (1); - - /* - * If remount, bind or move was specified, then we don't - * have a "type" as such. Use the dummy "none" type. - */ - if (rwflag & MS_TYPE) - type = "none"; - - if (optind + 2 == argc) { - dev = argv[optind]; - dir = argv[optind + 1]; - } else if (optind + 1 == argc && rwflag & MS_REMOUNT) { - get_mounts_dev_dir(argv[optind], &dev, &dir); - } - - if (dev == NULL || dir == NULL || type == NULL) { - fprintf(stderr, "Usage: %s [-r] [-w] [-o options] [-t type] " - "device directory\n", progname); - exit(1); - } - - return do_mount(dev, dir, type, rwflag, extra.str, loop, loopdev); - /* We leak dev and dir in some cases, but we're about to exit */ -} diff --git a/toolbox/renice.c b/toolbox/renice.c deleted file mode 100644 index 99a06f4..0000000 --- a/toolbox/renice.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (c) 2008, 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. - * * Neither the name of Google, Inc. 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 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 <unistd.h> -#include <stdlib.h> -#include <string.h> -#include <sys/time.h> -#include <sys/resource.h> -#include <sched.h> -#include <getopt.h> - -static void -usage(const char *s) -{ - fprintf(stderr, "USAGE: %s [[-r] [-t TYPE] priority pids ...] [-g pid]\n", s); - exit(EXIT_FAILURE); -} - -void print_prio(pid_t pid) -{ - int sched; - struct sched_param sp; - - printf("pid %d's priority: %d\n", pid, getpriority(PRIO_PROCESS, pid)); - - printf("scheduling class: "); - sched = sched_getscheduler(pid); - switch (sched) { - case SCHED_FIFO: - printf("FIFO\n"); - break; - case SCHED_RR: - printf("RR\n"); - break; - case SCHED_OTHER: - printf("Normal\n"); - break; - case -1: - perror("sched_getscheduler"); - break; - default: - printf("Unknown\n"); - } - - sched_getparam(pid, &sp); - printf("RT prio: %d (of %d to %d)\n", sp.sched_priority, - sched_get_priority_min(sched), sched_get_priority_max(sched)); -} - -int get_sched(char *str) -{ - if (strcasecmp(str, "RR") == 0) - return SCHED_RR; - else if (strcasecmp(str, "FIFO") == 0) - return SCHED_FIFO; - else if (strcasecmp(str, "NORMAL") == 0) - return SCHED_OTHER; - else if (strcasecmp(str, "OTHER") == 0) - return SCHED_OTHER; - return SCHED_RR; -} - -int renice_main(int argc, char *argv[]) -{ - int prio; - int realtime = 0; - int opt; - int sched = SCHED_RR; - char *cmd = argv[0]; - - do { - opt = getopt(argc, argv, "rt:g:"); - if (opt == -1) - break; - switch (opt) { - case 'r': - // do realtime priority adjustment - realtime = 1; - break; - case 't': - sched = get_sched(optarg); - break; - case 'g': - print_prio(atoi(optarg)); - return 0; - default: - usage(cmd); - } - } while (1); - - argc -= optind; - argv += optind; - - if (argc < 1) - usage(cmd); - - prio = atoi(argv[0]); - argc--; - argv++; - - if (argc < 1) - usage(cmd); - - while(argc) { - pid_t pid; - - pid = atoi(argv[0]); - argc--; - argv++; - - if (realtime) { - struct sched_param sp = { .sched_priority = prio }; - int ret; - - ret = sched_setscheduler(pid, sched, &sp); - if (ret) { - perror("sched_set_scheduler"); - exit(EXIT_FAILURE); - } - } else { - int ret; - - ret = setpriority(PRIO_PROCESS, pid, prio); - if (ret) { - perror("setpriority"); - exit(EXIT_FAILURE); - } - } - } - - return 0; -} diff --git a/toolbox/restart.c b/toolbox/restart.c new file mode 100644 index 0000000..9d803de --- /dev/null +++ b/toolbox/restart.c @@ -0,0 +1,27 @@ +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include <cutils/properties.h> + +int restart_main(int argc, char *argv[]) +{ + char buf[1024]; + + if(argc > 1) { + property_set("ctl.stop", argv[1]); + property_set("ctl.start", argv[1]); + } else { + /* defaults to stopping and starting the common services */ + property_set("ctl.stop", "zygote_secondary"); + property_set("ctl.stop", "zygote"); + property_set("ctl.stop", "surfaceflinger"); + property_set("ctl.stop", "netd"); + property_set("ctl.start", "netd"); + property_set("ctl.start", "surfaceflinger"); + property_set("ctl.start", "zygote"); + property_set("ctl.start", "zygote_secondary"); + } + + return 0; +} diff --git a/toolbox/upstream-netbsd/usr.bin/du/du.c b/toolbox/upstream-netbsd/usr.bin/du/du.c deleted file mode 100644 index 086ac4a..0000000 --- a/toolbox/upstream-netbsd/usr.bin/du/du.c +++ /dev/null @@ -1,364 +0,0 @@ -/* $NetBSD: du.c,v 1.36 2012/03/11 11:23:20 shattered Exp $ */ - -/* - * Copyright (c) 1989, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Chris Newcomb. - * - * 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. - */ - -#include <sys/cdefs.h> -#ifndef lint -__COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\ - The Regents of the University of California. All rights reserved."); -#endif /* not lint */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: du.c,v 1.36 2012/03/11 11:23:20 shattered Exp $"); -#endif -#endif /* not lint */ - -#include <sys/param.h> -#include <sys/types.h> -#include <sys/stat.h> - -#include <dirent.h> -#include <err.h> -#include <errno.h> -#include <fts.h> -#include <inttypes.h> -#include <util.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <limits.h> - -/* Count inodes or file size */ -#define COUNT (iflag ? 1 : p->fts_statp->st_blocks) - -static int linkchk(dev_t, ino_t); -static void prstat(const char *, int64_t); -__dead static void usage(void); - -static int hflag, iflag; -static long blocksize; - -int -main(int argc, char *argv[]) -{ - FTS *fts; - FTSENT *p; - int64_t totalblocks; - int ftsoptions, listfiles; - int depth; - int Hflag, Lflag, aflag, ch, cflag, dflag, gkmflag, nflag, rval, sflag; - const char *noargv[2]; - - Hflag = Lflag = aflag = cflag = dflag = gkmflag = nflag = sflag = 0; - totalblocks = 0; - ftsoptions = FTS_PHYSICAL; - depth = INT_MAX; - while ((ch = getopt(argc, argv, "HLPacd:ghikmnrsx")) != -1) - switch (ch) { - case 'H': - Hflag = 1; - Lflag = 0; - break; - case 'L': - Lflag = 1; - Hflag = 0; - break; - case 'P': - Hflag = Lflag = 0; - break; - case 'a': - aflag = 1; - break; - case 'c': - cflag = 1; - break; - case 'd': - dflag = 1; - depth = atoi(optarg); - if (depth < 0 || depth > SHRT_MAX) { - warnx("invalid argument to option d: %s", - optarg); - usage(); - } - break; - case 'g': - blocksize = 1024 * 1024 * 1024; - gkmflag = 1; - break; - case 'h': - hflag = 1; - break; - case 'i': - iflag = 1; - break; - case 'k': - blocksize = 1024; - gkmflag = 1; - break; - case 'm': - blocksize = 1024 * 1024; - gkmflag = 1; - break; - case 'n': - nflag = 1; - break; - case 'r': - break; - case 's': - sflag = 1; - break; - case 'x': - ftsoptions |= FTS_XDEV; - break; - case '?': - default: - usage(); - } - argc -= optind; - argv += optind; - - /* - * XXX - * Because of the way that fts(3) works, logical walks will not count - * the blocks actually used by symbolic links. We rationalize this by - * noting that users computing logical sizes are likely to do logical - * copies, so not counting the links is correct. The real reason is - * that we'd have to re-implement the kernel's symbolic link traversing - * algorithm to get this right. If, for example, you have relative - * symbolic links referencing other relative symbolic links, it gets - * very nasty, very fast. The bottom line is that it's documented in - * the man page, so it's a feature. - */ - if (Hflag) - ftsoptions |= FTS_COMFOLLOW; - if (Lflag) { - ftsoptions &= ~FTS_PHYSICAL; - ftsoptions |= FTS_LOGICAL; - } - - listfiles = 0; - if (aflag) { - if (sflag || dflag) - usage(); - listfiles = 1; - } else if (sflag) { - if (dflag) - usage(); - depth = 0; - } - - if (!*argv) { - noargv[0] = "."; - noargv[1] = NULL; - argv = __UNCONST(noargv); - } - - if (!gkmflag) - (void)getbsize(NULL, &blocksize); - blocksize /= 512; - - if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL) - err(1, "fts_open `%s'", *argv); - - for (rval = 0; (p = fts_read(fts)) != NULL;) { -#ifndef __ANDROID__ - if (nflag) { - switch (p->fts_info) { - case FTS_NS: - case FTS_SLNONE: - /* nothing */ - break; - default: - if (p->fts_statp->st_flags & UF_NODUMP) { - fts_set(fts, p, FTS_SKIP); - continue; - } - } - } -#endif - switch (p->fts_info) { - case FTS_D: /* Ignore. */ - break; - case FTS_DP: - p->fts_parent->fts_number += - p->fts_number += COUNT; - if (cflag) - totalblocks += COUNT; - /* - * If listing each directory, or not listing files - * or directories and this is post-order of the - * root of a traversal, display the total. - */ - if (p->fts_level <= depth - || (!listfiles && !p->fts_level)) - prstat(p->fts_path, p->fts_number); - break; - case FTS_DC: /* Ignore. */ - break; - case FTS_DNR: /* Warn, continue. */ - case FTS_ERR: - case FTS_NS: - warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); - rval = 1; - break; - default: - if (p->fts_statp->st_nlink > 1 && - linkchk(p->fts_statp->st_dev, p->fts_statp->st_ino)) - break; - /* - * If listing each file, or a non-directory file was - * the root of a traversal, display the total. - */ - if (listfiles || !p->fts_level) - prstat(p->fts_path, COUNT); - p->fts_parent->fts_number += COUNT; - if (cflag) - totalblocks += COUNT; - } - } - if (errno) - err(1, "fts_read"); - if (cflag) - prstat("total", totalblocks); - exit(rval); -} - -static void -prstat(const char *fname, int64_t blocks) -{ - if (iflag) { - (void)printf("%" PRId64 "\t%s\n", blocks, fname); - return; - } - - if (hflag) { - char buf[5]; - int64_t sz = blocks * 512; - - humanize_number(buf, sizeof(buf), sz, "", HN_AUTOSCALE, - HN_B | HN_NOSPACE | HN_DECIMAL); - - (void)printf("%s\t%s\n", buf, fname); - } else - (void)printf("%" PRId64 "\t%s\n", - howmany(blocks, (int64_t)blocksize), - fname); -} - -static int -linkchk(dev_t dev, ino_t ino) -{ - static struct entry { - dev_t dev; - ino_t ino; - } *htable; - static int htshift; /* log(allocated size) */ - static int htmask; /* allocated size - 1 */ - static int htused; /* 2*number of insertions */ - static int sawzero; /* Whether zero is in table or not */ - int h, h2; - uint64_t tmp; - /* this constant is (1<<64)/((1+sqrt(5))/2) - * aka (word size)/(golden ratio) - */ - const uint64_t HTCONST = 11400714819323198485ULL; - const int HTBITS = CHAR_BIT * sizeof(tmp); - - /* Never store zero in hashtable */ - if (dev == 0 && ino == 0) { - h = sawzero; - sawzero = 1; - return h; - } - - /* Extend hash table if necessary, keep load under 0.5 */ - if (htused<<1 >= htmask) { - struct entry *ohtable; - - if (!htable) - htshift = 10; /* starting hashtable size */ - else - htshift++; /* exponential hashtable growth */ - - htmask = (1 << htshift) - 1; - htused = 0; - - ohtable = htable; - htable = calloc(htmask+1, sizeof(*htable)); - if (!htable) - err(1, "calloc"); - - /* populate newly allocated hashtable */ - if (ohtable) { - int i; - for (i = 0; i <= htmask>>1; i++) - if (ohtable[i].ino || ohtable[i].dev) - linkchk(ohtable[i].dev, ohtable[i].ino); - free(ohtable); - } - } - - /* multiplicative hashing */ - tmp = dev; - tmp <<= HTBITS>>1; - tmp |= ino; - tmp *= HTCONST; - h = tmp >> (HTBITS - htshift); - h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */ - - /* open address hashtable search with double hash probing */ - while (htable[h].ino || htable[h].dev) { - if ((htable[h].ino == ino) && (htable[h].dev == dev)) - return 1; - h = (h + h2) & htmask; - } - - /* Insert the current entry into hashtable */ - htable[h].dev = dev; - htable[h].ino = ino; - htused++; - return 0; -} - -static void -usage(void) -{ - - (void)fprintf(stderr, - "usage: du [-H | -L | -P] [-a | -d depth | -s] [-cghikmnrx] [file ...]\n"); - exit(1); -} diff --git a/toolbox/uptime.c b/toolbox/uptime.c deleted file mode 100644 index ebfb15e..0000000 --- a/toolbox/uptime.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2010, 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. - * * Neither the name of Google, Inc. 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 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 <errno.h> -#include <stdio.h> -#include <string.h> -#include <time.h> - -static void format_time(int time, char* buffer) { - int seconds = time % 60; - time /= 60; - int minutes = time % 60; - time /= 60; - int hours = time % 24; - int days = time / 24; - - if (days > 0) { - sprintf(buffer, "%d day%s, %02d:%02d:%02d", days, (days == 1) ? "" : "s", hours, minutes, seconds); - } else { - sprintf(buffer, "%02d:%02d:%02d", hours, minutes, seconds); - } -} - -int uptime_main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) { - FILE* file = fopen("/proc/uptime", "r"); - if (!file) { - fprintf(stderr, "Could not open /proc/uptime\n"); - return -1; - } - float idle_time; - if (fscanf(file, "%*f %f", &idle_time) != 1) { - fprintf(stderr, "Could not parse /proc/uptime\n"); - fclose(file); - return -1; - } - fclose(file); - - struct timespec up_timespec; - if (clock_gettime(CLOCK_MONOTONIC, &up_timespec) == -1) { - fprintf(stderr, "Could not get monotonic time: %s\n", strerror(errno)); - return -1; - } - float up_time = up_timespec.tv_sec + up_timespec.tv_nsec / 1e9; - - struct timespec elapsed_timespec; - if (clock_gettime(CLOCK_BOOTTIME, &elapsed_timespec) == -1) { - fprintf(stderr, "Could not get boot time: %s\n", strerror(errno)); - return -1; - } - int elapsed = elapsed_timespec.tv_sec; - - char up_string[100], idle_string[100], sleep_string[100]; - format_time(elapsed, up_string); - format_time((int)idle_time, idle_string); - format_time((int)(elapsed - up_time), sleep_string); - printf("up time: %s, idle time: %s, sleep time: %s\n", up_string, idle_string, sleep_string); - - return 0; -} diff --git a/toolbox/watchprops.c b/toolbox/watchprops.c deleted file mode 100644 index cd62922..0000000 --- a/toolbox/watchprops.c +++ /dev/null @@ -1,92 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <errno.h> - -#include <cutils/properties.h> -#include <cutils/hashmap.h> - -#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ -#include <sys/_system_properties.h> - -static int str_hash(void *key) -{ - return hashmapHash(key, strlen(key)); -} - -static bool str_equals(void *keyA, void *keyB) -{ - return strcmp(keyA, keyB) == 0; -} - -static void announce(char *name, char *value) -{ - unsigned char *x; - - for(x = (unsigned char *)value; *x; x++) { - if((*x < 32) || (*x > 127)) *x = '.'; - } - - fprintf(stderr,"%10d %s = '%s'\n", (int) time(0), name, value); -} - -static void add_to_watchlist(Hashmap *watchlist, const char *name, - const prop_info *pi) -{ - char *key = strdup(name); - unsigned *value = malloc(sizeof(unsigned)); - if (!key || !value) - exit(1); - - *value = __system_property_serial(pi); - hashmapPut(watchlist, key, value); -} - -static void populate_watchlist(const prop_info *pi, void *cookie) -{ - Hashmap *watchlist = cookie; - char name[PROP_NAME_MAX]; - char value_unused[PROP_VALUE_MAX]; - - __system_property_read(pi, name, value_unused); - add_to_watchlist(watchlist, name, pi); -} - -static void update_watchlist(const prop_info *pi, void *cookie) -{ - Hashmap *watchlist = cookie; - char name[PROP_NAME_MAX]; - char value[PROP_VALUE_MAX]; - unsigned *serial; - - __system_property_read(pi, name, value); - serial = hashmapGet(watchlist, name); - if (!serial) { - add_to_watchlist(watchlist, name, pi); - announce(name, value); - } else { - unsigned tmp = __system_property_serial(pi); - if (*serial != tmp) { - *serial = tmp; - announce(name, value); - } - } -} - -int watchprops_main(int argc, char *argv[]) -{ - unsigned serial; - - Hashmap *watchlist = hashmapCreate(1024, str_hash, str_equals); - if (!watchlist) - exit(1); - - __system_property_foreach(populate_watchlist, watchlist); - - for(serial = 0;;) { - serial = __system_property_wait_any(serial); - __system_property_foreach(update_watchlist, watchlist); - } - return 0; -} |