am e6e5daac: am edeed28b: am 4a280e3d: Merge "Don\'t use control requests to read device serial numbers."
* commit 'e6e5daace55672b1ec8fb6c5c17a85ee1531f7ad':
Don't use control requests to read device serial numbers.
diff --git a/fastbootd/Android.mk b/fastbootd/Android.mk
index 76b28e2..f7c67a9 100644
--- a/fastbootd/Android.mk
+++ b/fastbootd/Android.mk
@@ -16,23 +16,82 @@
include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+ external/openssl/include \
+ $(LOCAL_PATH)/include \
+ external/zlib/ \
+
LOCAL_SRC_FILES := \
config.c \
commands.c \
+ commands/boot.c \
+ commands/flash.c \
+ commands/partitions.c \
+ commands/virtual_partitions.c \
fastbootd.c \
protocol.c \
transport.c \
- usb_linux_client.c
+ trigger.c \
+ usb_linux_client.c \
+ utils.c
LOCAL_MODULE := fastbootd
LOCAL_MODULE_TAGS := optional
LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
+LOCAL_LDFLAGS := -ldl
+
+LOCAL_SHARED_LIBRARIES := \
+ libhardware \
+ libhardware_legacy
+
+LOCAL_STATIC_LIBRARIES := \
+ libsparse_static \
+ libc \
+ libcutils \
+ libz
+
+#LOCAL_FORCE_STATIC_EXECUTABLE := true
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+ external/zlib/
+
+LOCAL_SRC_FILES := \
+ commands/partitions.c \
+ other/gptedit.c \
+ utils.c
+
+LOCAL_MODULE := gptedit
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
LOCAL_STATIC_LIBRARIES := \
libsparse_static \
libc \
- libcutils
+ libcutils \
+ libz
LOCAL_FORCE_STATIC_EXECUTABLE := true
include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include \
+
+LOCAL_STATIC_LIBRARIES := \
+ $(EXTRA_STATIC_LIBS) \
+ libcutils
+
+LOCAL_SRC_FILES := \
+ other/vendor_trigger.c
+
+LOCAL_MODULE := libvendortrigger.default
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
+
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/fastbootd/commands.c b/fastbootd/commands.c
index 252f655..83e86b0 100644
--- a/fastbootd/commands.c
+++ b/fastbootd/commands.c
@@ -32,114 +32,279 @@
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/reboot.h>
+#include <fcntl.h>
#include "bootimg.h"
+#include "commands/boot.h"
+#include "commands/flash.h"
+#include "commands/partitions.h"
+#include "commands/virtual_partitions.h"
#include "debug.h"
#include "protocol.h"
+#include "trigger.h"
+#include "utils.h"
+
+#define ATAGS_LOCATION "/proc/atags"
static void cmd_boot(struct protocol_handle *phandle, const char *arg)
{
-#if 0
unsigned kernel_actual;
unsigned ramdisk_actual;
- static struct boot_img_hdr hdr;
- char *ptr = ((char*) data);
+ unsigned second_actual;
+ void *kernel_ptr;
+ void *ramdisk_ptr;
+ void *second_ptr;
+ struct boot_img_hdr *hdr;
+ int sz, atags_sz, new_atags_sz;
+ int rv;
+ char *ptr = NULL, *atags_ptr = NULL, *new_atags = NULL;
+ D(DEBUG, "cmd_boot %s\n", arg);
- if (sz < sizeof(hdr)) {
+ if (phandle->download_fd < 0) {
+ fastboot_fail(phandle, "no kernel file");
+ return;
+ }
+
+ atags_ptr = read_atags(ATAGS_LOCATION, &atags_sz);
+ if (atags_ptr == NULL) {
+ fastboot_fail(phandle, "atags read error");
+ goto error;
+ }
+
+ sz = get_file_size(phandle->download_fd);
+ ptr = (char *) mmap(NULL, sz, PROT_READ,
+ MAP_POPULATE | MAP_PRIVATE, phandle->download_fd, 0);
+ hdr = (struct boot_img_hdr *) ptr;
+
+ if (ptr == MAP_FAILED) {
+ fastboot_fail(phandle, "internal fastbootd error");
+ goto error;
+ }
+
+ if ((size_t) sz < sizeof(*hdr)) {
fastboot_fail(phandle, "invalid bootimage header");
- return;
+ goto error;
}
- memcpy(&hdr, data, sizeof(hdr));
+ kernel_actual = ROUND_TO_PAGE(hdr->kernel_size, hdr->page_size);
+ ramdisk_actual = ROUND_TO_PAGE(hdr->ramdisk_size, hdr->page_size);
+ second_actual = ROUND_TO_PAGE(hdr->second_size, hdr->page_size);
- /* ensure commandline is terminated */
- hdr.cmdline[BOOT_ARGS_SIZE-1] = 0;
+ new_atags = (char *) create_atags((unsigned *) atags_ptr, atags_sz, hdr, &new_atags_sz);
- kernel_actual = ROUND_TO_PAGE(hdr.kernel_size);
- ramdisk_actual = ROUND_TO_PAGE(hdr.ramdisk_size);
+ if (new_atags == NULL) {
+ fastboot_fail(phandle, "atags generate error");
+ goto error;
+ }
+ if (new_atags_sz > 0x4000) {
+ fastboot_fail(phandle, "atags file to large");
+ goto error;
+ }
- if (2048 + kernel_actual + ramdisk_actual < sz) {
+ if ((int) (hdr->page_size + kernel_actual + ramdisk_actual) < sz) {
fastboot_fail(phandle, "incomplete bootimage");
- return;
+ goto error;
}
- /*memmove((void*) KERNEL_ADDR, ptr + 2048, hdr.kernel_size);
- memmove((void*) RAMDISK_ADDR, ptr + 2048 + kernel_actual, hdr.ramdisk_size);*/
+ kernel_ptr = (void *)((unsigned) ptr + hdr->page_size);
+ ramdisk_ptr = (void *)((unsigned) kernel_ptr + kernel_actual);
+ second_ptr = (void *)((unsigned) ramdisk_ptr + ramdisk_actual);
+
+ D(INFO, "preparing to boot");
+ // Prepares boot physical addresses from header are ignored
+ rv = prepare_boot_linux(hdr->kernel_addr, kernel_ptr, kernel_actual,
+ hdr->ramdisk_addr, ramdisk_ptr, ramdisk_actual,
+ hdr->second_addr, second_ptr, second_actual,
+ hdr->tags_addr, new_atags, ROUND_TO_PAGE(new_atags_sz, hdr->page_size));
+ if (rv < 0) {
+ fastboot_fail(phandle, "kexec prepare failed");
+ goto error;
+ }
fastboot_okay(phandle, "");
- udc_stop();
+ free(atags_ptr);
+ munmap(ptr, sz);
+ free(new_atags);
- /*boot_linux((void*) KERNEL_ADDR, (void*) TAGS_ADDR,
- (const char*) hdr.cmdline, LINUX_MACHTYPE,
- (void*) RAMDISK_ADDR, hdr.ramdisk_size);*/
-#endif
+ D(INFO, "Kexec going to reboot");
+ reboot(LINUX_REBOOT_CMD_KEXEC);
+
+ fastboot_fail(phandle, "reboot error");
+
+ return;
+
+error:
+
+ if (atags_ptr != NULL)
+ free(atags_ptr);
+ if (ptr != NULL)
+ munmap(ptr, sz);
+
}
static void cmd_erase(struct protocol_handle *phandle, const char *arg)
{
-#if 0
- struct ptentry *ptn;
- struct ptable *ptable;
+ int partition_fd;
+ char path[PATH_MAX];
+ D(DEBUG, "cmd_erase %s\n", arg);
- ptable = flash_get_ptable();
- if (ptable == NULL) {
+ if (flash_find_entry(arg, path, PATH_MAX)) {
fastboot_fail(phandle, "partition table doesn't exist");
return;
}
- ptn = ptable_find(ptable, arg);
- if (ptn == NULL) {
- fastboot_fail(phandle, "unknown partition name");
+ if (path == NULL) {
+ fastboot_fail(phandle, "Couldn't find partition");
return;
}
- if (flash_erase(ptn)) {
+ partition_fd = flash_get_partiton(path);
+ if (partition_fd < 0) {
+ fastboot_fail(phandle, "partiton file does not exists");
+ }
+
+ if (flash_erase(partition_fd)) {
+ fastboot_fail(phandle, "failed to erase partition");
+ flash_close(partition_fd);
+ return;
+ }
+
+ if (flash_close(partition_fd) < 0) {
+ D(ERR, "could not close device %s", strerror(errno));
fastboot_fail(phandle, "failed to erase partition");
return;
}
fastboot_okay(phandle, "");
-#endif
+}
+
+static int GPT_header_location() {
+ const char *location_str = fastboot_getvar("gpt_sector");
+ char *str;
+ int location;
+
+ if (!strcmp("", location_str)) {
+ D(INFO, "GPT location not specified using second sector");
+ return 1;
+ }
+ else {
+ location = strtoul(location_str, &str, 10);
+ D(INFO, "GPT location specified as %d", location);
+
+ if (*str != '\0')
+ return -1;
+
+ return location - 1;
+ }
+}
+
+static void cmd_gpt_layout(struct protocol_handle *phandle, const char *arg) {
+ struct GPT_entry_table *oldtable;
+ int location;
+ struct GPT_content content;
+ const char *device;
+ device = fastboot_getvar("blockdev");
+
+ if (!strcmp(device, "")) {
+ fastboot_fail(phandle, "blockdev not defined in config file");
+ return;
+ }
+
+ if (phandle->download_fd < 0) {
+ fastboot_fail(phandle, "no layout file");
+ return;
+ }
+
+ location = GPT_header_location();
+ oldtable = GPT_get_device(device, location);
+
+ GPT_default_content(&content, oldtable);
+ if (oldtable == NULL)
+ D(WARN, "Could not get old gpt table");
+ else
+ GPT_release_device(oldtable);
+
+ if (!GPT_parse_file(phandle->download_fd, &content)) {
+ fastboot_fail(phandle, "Could not parse partition config file");
+ return;
+ }
+
+ if (trigger_gpt_layout(&content)) {
+ fastboot_fail(phandle, "Vendor forbids this opperation");
+ GPT_release_content(&content);
+ return;
+ }
+
+ if (!GPT_write_content(device, &content)) {
+ fastboot_fail(phandle, "Unable to write gpt file");
+ GPT_release_content(&content);
+ return;
+ }
+
+ GPT_release_content(&content);
+ fastboot_okay(phandle, "");
}
static void cmd_flash(struct protocol_handle *phandle, const char *arg)
{
-#if 0
- struct ptentry *ptn;
- struct ptable *ptable;
- unsigned extra = 0;
+ int partition;
+ uint64_t sz;
+ char data[BOOT_MAGIC_SIZE];
+ char path[PATH_MAX];
+ ssize_t header_sz = 0;
- ptable = flash_get_ptable();
- if (ptable == NULL) {
+ D(DEBUG, "cmd_flash %s\n", arg);
+
+ if (try_handle_virtual_partition(phandle, arg)) {
+ return;
+ }
+
+ if (phandle->download_fd < 0) {
+ fastboot_fail(phandle, "no kernel file");
+ return;
+ }
+
+ if (flash_find_entry(arg, path, PATH_MAX)) {
fastboot_fail(phandle, "partition table doesn't exist");
return;
}
- ptn = ptable_find(ptable, arg);
- if (ptn == NULL) {
- fastboot_fail(phandle, "unknown partition name");
- return;
- }
-
- if (!strcmp(ptn->name, "boot") || !strcmp(ptn->name, "recovery")) {
+ // TODO: Maybe its goot idea to check whether the partition is just bootable partition
+ if (!strcmp(arg, "boot") || !strcmp(arg, "recovery")) {
+ if (read_data_once(phandle->download_fd, data, BOOT_MAGIC_SIZE) < BOOT_MAGIC_SIZE) {
+ fastboot_fail(phandle, "incoming data read error, cannot read boot header");
+ return;
+ }
if (memcmp((void *)data, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
fastboot_fail(phandle, "image is not a boot image");
return;
}
}
- if (!strcmp(ptn->name, "system") || !strcmp(ptn->name, "userdata"))
- extra = 64;
- else
- sz = ROUND_TO_PAGE(sz);
+ partition = flash_get_partiton(path);
- D(INFO, "writing %d bytes to '%s'\n", sz, ptn->name);
- if (flash_write(ptn, extra, data, sz)) {
+ sz = get_file_size64(phandle->download_fd);
+ if (sz > get_file_size64(partition)) {
+ flash_close(partition);
+ D(WARN, "size of file too large");
+ fastboot_fail(phandle, "size of file too large");
+ return;
+ }
+
+ D(INFO, "writing %lld bytes to '%s'\n", sz, arg);
+
+ if (flash_write(partition, phandle->download_fd, sz, header_sz)) {
fastboot_fail(phandle, "flash write failure");
return;
}
- D(INFO, "partition '%s' updated\n", ptn->name);
-#endif
+ D(INFO, "partition '%s' updated\n", arg);
+
+ flash_close(partition);
+
fastboot_okay(phandle, "");
}
@@ -192,14 +357,26 @@
fastboot_okay(phandle, "");
}
+static void cmd_oem(struct protocol_handle *phandle, const char *arg) {
+ const char *response = "";
+
+ if (trigger_oem_cmd(arg, &response))
+ fastboot_fail(phandle, response);
+ else
+ fastboot_okay(phandle, response);
+}
+
void commands_init()
{
+ virtual_partition_register("partition-table", cmd_gpt_layout);
+
fastboot_register("boot", cmd_boot);
fastboot_register("erase:", cmd_erase);
fastboot_register("flash:", cmd_flash);
fastboot_register("continue", cmd_continue);
fastboot_register("getvar:", cmd_getvar);
fastboot_register("download:", cmd_download);
+ fastboot_register("oem", cmd_oem);
//fastboot_publish("version", "0.5");
//fastboot_publish("product", "swordfish");
//fastboot_publish("kernel", "lk");
diff --git a/fastbootd/commands/boot.c b/fastbootd/commands/boot.c
new file mode 100644
index 0000000..8da9a28
--- /dev/null
+++ b/fastbootd/commands/boot.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * 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 <sys/syscall.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "boot.h"
+#include "debug.h"
+#include "utils.h"
+#include "bootimg.h"
+
+
+#define KEXEC_ARM_ATAGS_OFFSET 0x1000
+#define KEXEC_ARM_ZIMAGE_OFFSET 0x8000
+
+#define MEMORY_SIZE 0x0800000
+#define START_ADDRESS 0x44000000
+#define KERNEL_START (START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET)
+
+#define ATAG_NONE_TYPE 0x00000000
+#define ATAG_CORE_TYPE 0x54410001
+#define ATAG_RAMDISK_TYPE 0x54410004
+#define ATAG_INITRD2_TYPE 0x54420005
+#define ATAG_CMDLINE_TYPE 0x54410009
+
+#define MAX_ATAG_SIZE 0x4000
+
+struct atag_info {
+ unsigned size;
+ unsigned type;
+};
+
+struct atag_initrd2 {
+ unsigned start;
+ unsigned size;
+};
+
+struct atag_cmdline {
+ char cmdline[0];
+};
+
+struct atag {
+ struct atag_info info;
+ union {
+ struct atag_initrd2 initrd2;
+ struct atag_cmdline cmdline;
+ } data;
+};
+
+
+long kexec_load(unsigned int entry, unsigned long nr_segments,
+ struct kexec_segment *segment, unsigned long flags) {
+ return syscall(__NR_kexec_load, entry, nr_segments, segment, flags);
+}
+
+/*
+ * Prepares arguments for kexec
+ * Kernel address is not set into kernel_phys
+ * Ramdisk is set to position relative to kernel
+ */
+int prepare_boot_linux(unsigned kernel_phys, void *kernel_addr, int kernel_size,
+ unsigned ramdisk_phys, void *ramdisk_addr, int ramdisk_size,
+ unsigned second_phys, void *second_addr, int second_size,
+ unsigned atags_phys, void *atags_addr, int atags_size) {
+ struct kexec_segment segment[4];
+ int segment_count = 2;
+ unsigned entry = START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET;
+ int rv;
+ int page_size = getpagesize();
+
+ segment[0].buf = kernel_addr;
+ segment[0].bufsz = kernel_size;
+ segment[0].mem = (void *) KERNEL_START;
+ segment[0].memsz = ROUND_TO_PAGE(kernel_size, page_size);
+
+ if (kernel_size > MEMORY_SIZE - KEXEC_ARM_ZIMAGE_OFFSET) {
+ D(INFO, "Kernel image too big");
+ return -1;
+ }
+
+ segment[1].buf = atags_addr;
+ segment[1].bufsz = atags_size;
+ segment[1].mem = (void *) (START_ADDRESS + KEXEC_ARM_ATAGS_OFFSET);
+ segment[1].memsz = ROUND_TO_PAGE(atags_size, page_size);
+
+ D(INFO, "Ramdisk size is %d", ramdisk_size);
+
+ if (ramdisk_size != 0) {
+ segment[segment_count].buf = ramdisk_addr;
+ segment[segment_count].bufsz = ramdisk_size;
+ segment[segment_count].mem = (void *) (KERNEL_START + ramdisk_phys - kernel_phys);
+ segment[segment_count].memsz = ROUND_TO_PAGE(ramdisk_phys, page_size);
+ ++segment_count;
+ }
+
+ D(INFO, "Ramdisk size is %d", ramdisk_size);
+ if (second_size != 0) {
+ segment[segment_count].buf = second_addr;
+ segment[segment_count].bufsz = second_size;
+ segment[segment_count].mem = (void *) (KERNEL_START + second_phys - kernel_phys);
+ segment[segment_count].memsz = ROUND_TO_PAGE(second_size, page_size);
+ entry = second_phys;
+ ++segment_count;
+ }
+
+ rv = kexec_load(entry, segment_count, segment, KEXEC_ARCH_DEFAULT);
+
+ if (rv != 0) {
+ D(INFO, "Kexec_load returned non-zero exit code: %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 1;
+
+}
+
+unsigned *create_atags(unsigned *atags_position, int atag_size, const struct boot_img_hdr *hdr, int *size) {
+ struct atag *current_tag = (struct atag *) atags_position;
+ unsigned *current_tag_raw = atags_position;
+ unsigned *new_atags = malloc(ROUND_TO_PAGE(atag_size + BOOT_ARGS_SIZE * sizeof(char),
+ hdr->page_size));
+ //This pointer will point into the beggining of buffer free space
+ unsigned *natags_raw_buff = new_atags;
+ int new_atags_size = 0;
+ int current_size;
+ int cmdl_length;
+
+ // copy tags from current atag file
+ while (current_tag->info.type != ATAG_NONE_TYPE) {
+ switch (current_tag->info.type) {
+ case ATAG_CMDLINE_TYPE:
+ case ATAG_RAMDISK_TYPE:
+ case ATAG_INITRD2_TYPE: break;
+ default:
+ memcpy((void *)natags_raw_buff, (void *)current_tag_raw, current_tag->info.size * sizeof(unsigned));
+ natags_raw_buff += current_tag->info.size;
+ new_atags_size += current_tag->info.size;
+ }
+
+ current_tag_raw += current_tag->info.size;
+ current_tag = (struct atag *) current_tag_raw;
+
+ if (current_tag_raw >= atags_position + atag_size) {
+ D(ERR, "Critical error in atags");
+ return NULL;
+ }
+ }
+
+ // set INITRD2 tag
+ if (hdr->ramdisk_size > 0) {
+ current_size = (sizeof(struct atag_info) + sizeof(struct atag_initrd2)) / sizeof(unsigned);
+ *((struct atag *) natags_raw_buff) = (struct atag) {
+ .info = {
+ .size = current_size,
+ .type = ATAG_INITRD2_TYPE
+ },
+ .data = {
+ .initrd2 = (struct atag_initrd2) {
+ .start = hdr->ramdisk_addr,
+ .size = hdr->ramdisk_size
+ }
+ }
+ };
+
+ new_atags_size += current_size;
+ natags_raw_buff += current_size;
+ }
+
+ // set ATAG_CMDLINE
+ cmdl_length = strnlen((char *) hdr->cmdline, BOOT_ARGS_SIZE - 1);
+ current_size = sizeof(struct atag_info) + (1 + cmdl_length);
+ current_size = (current_size + sizeof(unsigned) - 1) / sizeof(unsigned);
+ *((struct atag *) natags_raw_buff) = (struct atag) {
+ .info = {
+ .size = current_size,
+ .type = ATAG_CMDLINE_TYPE
+ },
+ };
+
+ //copy cmdline and ensure that there is null character
+ memcpy(((struct atag *) natags_raw_buff)->data.cmdline.cmdline,
+ (char *) hdr->cmdline, cmdl_length);
+ ((struct atag *) natags_raw_buff)->data.cmdline.cmdline[cmdl_length] = '\0';
+
+ new_atags_size += current_size;
+ natags_raw_buff += current_size;
+
+ // set ATAG_NONE
+ *((struct atag *) natags_raw_buff) = (struct atag) {
+ .info = {
+ .size = 0,
+ .type = ATAG_NONE_TYPE
+ },
+ };
+ new_atags_size += sizeof(struct atag_info) / sizeof(unsigned);
+ natags_raw_buff += sizeof(struct atag_info) / sizeof(unsigned);
+
+ *size = new_atags_size * sizeof(unsigned);
+ return new_atags;
+}
+
+char *read_atags(const char * path, int *atags_sz) {
+ int afd = -1;
+ char *atags_ptr = NULL;
+
+ afd = open(path, O_RDONLY);
+ if (afd < 0) {
+ D(ERR, "wrong atags file");
+ return 0;
+ }
+
+ atags_ptr = (char *) malloc(MAX_ATAG_SIZE);
+ if (atags_ptr == NULL) {
+ D(ERR, "insufficient memory");
+ return 0;
+ }
+
+ *atags_sz = read(afd, atags_ptr, MAX_ATAG_SIZE);
+
+ close(afd);
+ return atags_ptr;
+}
+
diff --git a/fastbootd/commands/boot.h b/fastbootd/commands/boot.h
new file mode 100644
index 0000000..bfbf1de
--- /dev/null
+++ b/fastbootd/commands/boot.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * 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.
+ */
+
+#ifndef __FASTBOOT_BOOT_H
+#define __FASTBOOT_BOOT_H
+
+#include <sys/cdefs.h>
+#include <uapi/linux/kexec.h>
+
+#include "bootimg.h"
+
+#define KEXEC_SEGMENT_MAX 16
+
+#define KEXEC_TYPE_DEFAULT 0
+#define KEXEC_TYPE_CRASH 1
+
+int prepare_boot_linux(unsigned, void *, int, unsigned, void *, int,
+ unsigned, void *, int, unsigned, void *, int);
+unsigned *create_atags(unsigned *, int, const struct boot_img_hdr *, int *);
+long kexec_load(unsigned int, unsigned long, struct kexec_segment *, unsigned long);
+char *read_atags(const char *, int *);
+
+#endif /* _SYS_KEXEC_H */
+
diff --git a/fastbootd/commands/flash.c b/fastbootd/commands/flash.c
new file mode 100644
index 0000000..5f8b931
--- /dev/null
+++ b/fastbootd/commands/flash.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * 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 <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include "flash.h"
+#include "protocol.h"
+#include "debug.h"
+#include "utils.h"
+#include "commands/partitions.h"
+
+
+#define ALLOWED_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-."
+#define BUFFER_SIZE 1024 * 1024
+#define MIN(a, b) (a > b ? b : a)
+
+
+int flash_find_entry(const char *name, char *out, size_t outlen)
+{
+//TODO: Assumption: All the partitions has they unique name
+
+ const char *path = fastboot_getvar("device-directory");
+ size_t length;
+ if (strcmp(path, "") == 0) {
+ D(ERR, "device-directory: not defined in config file");
+ return -1;
+ }
+
+ length = strspn(name, ALLOWED_CHARS);
+ if (length != strlen(name)) {
+ D(ERR, "Not allowed char in name: %c", name[length]);
+ return -1;
+ }
+
+ if (snprintf(out, outlen, "%s%s", path, name) >= (int) outlen) {
+ D(ERR, "Too long path to partition file");
+ return -1;
+ }
+
+ if (access(out, F_OK ) == -1) {
+ D(ERR, "could not find partition file %s", name);
+ return -1;
+ }
+
+ return 0;
+}
+
+int flash_erase(int fd)
+{
+ int64_t size;
+ size = get_block_device_size(fd);
+ D(DEBUG, "erase %llu data from %d\n", size, fd);
+
+ return wipe_block_device(fd, size);
+}
+
+int flash_write(int partition_fd, int data_fd, ssize_t size, ssize_t skip)
+{
+ ssize_t written = 0;
+ struct GPT_mapping input;
+ struct GPT_mapping output;
+
+ while (written < size) {
+ int current_size = MIN(size - written, BUFFER_SIZE);
+
+ if (gpt_mmap(&input, written + skip, current_size, data_fd)) {
+ D(ERR, "Error in writing data, unable to map data file %d at %d size %d", size, skip, current_size);
+ return -1;
+ }
+ if (gpt_mmap(&output, written, current_size, partition_fd)) {
+ D(ERR, "Error in writing data, unable to map output partition");
+ return -1;
+ }
+
+ memcpy(output.ptr, input.ptr, current_size);
+
+ gpt_unmap(&input);
+ gpt_unmap(&output);
+
+ written += current_size;
+ }
+
+ return 0;
+}
diff --git a/fastbootd/commands/flash.h b/fastbootd/commands/flash.h
new file mode 100644
index 0000000..8ffd688
--- /dev/null
+++ b/fastbootd/commands/flash.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * 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.
+ */
+
+#ifndef _FASTBOOTD_ERASE_H
+#define _FASTBOOTD_ERASE_H
+
+int flash_find_entry(const char *, char *, size_t);
+int flash_erase(int fd);
+
+static inline int flash_get_partiton(const char *path) {
+ return open(path, O_RDWR);
+}
+
+static inline int flash_close(int fd) {
+ return close(fd);
+}
+
+int flash_write(int partition, int data, ssize_t size, ssize_t skip);
+
+static inline ssize_t read_data_once(int fd, char *buffer, ssize_t size) {
+ ssize_t readcount = 0;
+ ssize_t len;
+
+ while ((len = TEMP_FAILURE_RETRY(read(fd, (void *) &buffer[readcount], size - readcount))) > 0) {
+ readcount -= len;
+ }
+ if (len < 0)
+ return len;
+
+ return readcount;
+}
+
+#endif
+
diff --git a/fastbootd/commands/partitions.c b/fastbootd/commands/partitions.c
new file mode 100644
index 0000000..3bfc298
--- /dev/null
+++ b/fastbootd/commands/partitions.c
@@ -0,0 +1,738 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <endian.h>
+#include <zlib.h>
+#include <linux/hdreg.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <cutils/config_utils.h>
+
+#include "partitions.h"
+#include "debug.h"
+#include "utils.h"
+#include "protocol.h"
+
+#define MMAP2_SHIFT 12
+
+#define BLKRRPART _IO(0x12,95) /* re-read partition table */
+#define BLKSSZGET _IO(0x12,104)
+
+#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
+#define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
+#define ALIGN_DOWN(x, y) ((y) * ((x) / (y)))
+
+
+const uint16_t partition_type_uuid[16] = {
+ 0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44,
+ 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7,
+};
+
+//TODO: If both blocks are invalid should I leave everything to vendor (through libvendor)
+
+static void GPT_entry_clear(struct GPT_entry_raw *entry)
+{
+ memset(entry, 0, sizeof(*entry));
+}
+
+extern void* __mmap2(void *, size_t, int, int, int, off_t);
+
+static inline void *mmap64(void *addr, size_t length, int prot,
+ int flags, int fd, uint64_t offset) {
+ return __mmap2(addr, length, prot, flags, fd, offset >> MMAP2_SHIFT);
+}
+
+/*
+ * returns mapped location to choosen area
+ * mapped_ptr is pointer to whole area mapped (it can be bigger then requested)
+ */
+int gpt_mmap(struct GPT_mapping *mapping, uint64_t location, int size, int fd)
+{
+ unsigned int location_diff = location & ~PAGE_MASK;
+
+ mapping->size = ALIGN(size + location_diff, PAGE_SIZE);
+
+ uint64_t sz = get_file_size64(fd);
+ if (sz < size + location) {
+ D(ERR, "the location of mapping area is outside of the device size %lld", sz);
+ return 1;
+ }
+
+ mapping->map_ptr = mmap64(NULL, mapping->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, location);
+
+ if (mapping->map_ptr == MAP_FAILED) {
+ mapping->ptr = MAP_FAILED;
+ D(ERR, "map failed %d", (int) mapping->map_ptr);
+ return 1;
+ }
+
+ mapping->ptr = (void *)((char *) mapping->map_ptr + location_diff);
+ return 0;
+}
+
+void gpt_unmap(struct GPT_mapping *mapping) {
+ munmap(mapping->map_ptr, mapping->size);
+}
+
+
+#define LBA_ADDR(table, value) ((uint64_t) (table)->sector_size * (value))
+
+int GPT_map_from_content(struct GPT_entry_table *table, const struct GPT_content *content)
+{
+
+ // Mapping header
+ if (gpt_mmap(&table->header_map, LBA_ADDR(table, content->header.current_lba),
+ table->sector_size, table->fd)) {
+ D(ERR, "unable to map header:%s\n", strerror(errno));
+ goto error_header;
+ }
+
+ table->header = (struct GPT_header *) table->header_map.ptr;
+
+ table->partition_table_size = ROUND_UP(content->header.entries_count * sizeof(*table->entries),
+ table->sector_size);
+
+ // Mapping entry table
+ if (gpt_mmap(&table->entries_map, LBA_ADDR(table, content->header.entries_lba),
+ table->partition_table_size, table->fd)) {
+ D(ERR, "unable to map entries");
+ goto error_signature;
+ }
+
+ table->entries = (struct GPT_entry_raw *) table->entries_map.ptr;
+
+ // Mapping secondary header
+ if (gpt_mmap(&table->sec_header_map, LBA_ADDR(table, content->header.backup_lba),
+ table->sector_size, table->fd)) {
+ D(ERR, "unable to map backup gpt header");
+ goto error_sec_header;
+ }
+
+ // Mapping secondary entries table
+ if (gpt_mmap(&table->sec_entries_map,
+ LBA_ADDR(table, content->header.backup_lba) - table->partition_table_size,
+ table->partition_table_size, table->fd)) {
+ D(ERR, "unable to map secondary gpt table");
+ goto error_sec_entries;
+ }
+
+ table->second_header = (struct GPT_header *) table->sec_header_map.ptr;
+ table->second_entries = (struct GPT_entry_raw *) table->sec_entries_map.ptr;
+ table->second_valid = strcmp("EFI PART", (char *) table->second_header->signature) == 0;
+
+ return 0;
+
+error_sec_entries:
+ gpt_unmap(&table->sec_header_map);
+error_sec_header:
+ gpt_unmap(&table->entries_map);
+error_signature:
+ gpt_unmap(&table->header_map);
+error_header:
+ return 1;
+}
+
+int GPT_map(struct GPT_entry_table *table, unsigned header_lba)
+{
+ struct GPT_content content;
+ struct GPT_mapping mapping;
+ struct GPT_header *header;
+
+ if (gpt_mmap(&mapping, LBA_ADDR(table, header_lba), table->sector_size, table->fd)) {
+ D(ERR, "unable to map header: %s", strerror(errno));
+ goto error_header;
+ }
+
+ header = (struct GPT_header *) mapping.ptr;
+
+ if (strcmp("EFI PART", (char *) header->signature)) {
+ D(ERR, "GPT entry not valid");
+ goto error_signature;
+ }
+
+ content.header = *header;
+
+ gpt_unmap(&mapping);
+
+ return GPT_map_from_content(table, &content);
+
+error_signature:
+ gpt_unmap(&table->header_map);
+error_header:
+ return 1;
+}
+
+struct GPT_entry_table* GPT_get_device(const char *path, unsigned header_lba)
+{
+ struct GPT_entry_table *table;
+ size_t sector_bytes;
+
+ table = (struct GPT_entry_table *) malloc(sizeof(*table));
+ table->fd = open(path, O_RDWR);
+
+ if (table->fd < 0) {
+ D(ERR, "unable to open file %s:%s\n", path, strerror(errno));
+ return NULL;
+ }
+
+ if (!ioctl(table->fd, BLKSSZGET, §or_bytes)) {
+ table->sector_size = (unsigned) sector_bytes;
+ D(INFO, "Got sector size %d", table->sector_size);
+ } else {
+ D(WARN, "unable to get sector size, assuming 512");
+ table->sector_size = 512;
+ }
+
+ if (GPT_map(table, header_lba)) {
+ D(ERR, "Could not map gpt");
+ return NULL;
+ }
+
+ return table;
+}
+
+static struct GPT_entry_table* GPT_get_from_content(const char *path, const struct GPT_content *content)
+{
+ struct GPT_entry_table *table;
+ size_t sector_bytes;
+
+ table = (struct GPT_entry_table *) malloc(sizeof(*table));
+ table->fd = open(path, O_RDWR);
+
+ if (table->fd < 0) {
+ D(ERR, "unable to open file %s:%s\n", path, strerror(errno));
+ return NULL;
+ }
+
+ if (!ioctl(table->fd, BLKSSZGET, §or_bytes)) {
+ table->sector_size = (unsigned) sector_bytes;
+ D(INFO, "Got sector size %d", table->sector_size);
+ } else {
+ D(WARN, "unable to get sector size %s, assuming 512", strerror(errno));
+ table->sector_size = 512;
+ }
+
+ if (GPT_map_from_content(table, content)) {
+ D(ERR, "Could not map gpt");
+ return NULL;
+ }
+
+ return table;
+}
+
+
+void GPT_release_device(struct GPT_entry_table *table)
+{
+ gpt_unmap(&table->header_map);
+ gpt_unmap(&table->entries_map);
+ gpt_unmap(&table->sec_header_map);
+ gpt_unmap(&table->sec_entries_map);
+ close(table->fd);
+ free(table);
+}
+
+static int GPT_check_overlap(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+static int GPT_check_overlap_except(struct GPT_entry_table *table,
+ struct GPT_entry_raw *entry,
+ struct GPT_entry_raw *exclude);
+
+void GPT_edit_entry(struct GPT_entry_table *table,
+ struct GPT_entry_raw *old_entry,
+ struct GPT_entry_raw *new_entry)
+{
+ struct GPT_entry_raw *current_entry = GPT_get_pointer(table, old_entry);
+
+ if (GPT_check_overlap_except(table, new_entry, current_entry)) {
+ D(ERR, "Couldn't add overlaping partition");
+ return;
+ }
+
+ if (current_entry == NULL) {
+ D(ERR, "Couldn't find entry");
+ return;
+ }
+
+ *current_entry = *new_entry;
+}
+
+int GPT_delete_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+ struct GPT_entry_raw *raw = GPT_get_pointer(table, entry);
+
+ if (raw == NULL) {
+ D(ERR, "could not find entry");
+ return 1;
+ }
+ D(DEBUG, "Deleting gpt entry '%s'\n", raw->partition_guid);
+
+ // This entry can be empty in the middle
+ GPT_entry_clear(raw);
+
+ return 0;
+}
+
+void GPT_add_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+ unsigned i;
+ int inserted = 0;
+ if (GPT_check_overlap(table, entry)) {
+ D(ERR, "Couldn't add overlaping partition");
+ return;
+ }
+
+ if (GPT_get_pointer(table, entry) != NULL) {
+ D(WARN, "Add entry fault, this entry already exists");
+ return;
+ }
+
+ struct GPT_entry_raw *entries = table->entries;
+
+ for (i = 0; i < table->header->entries_count; ++i) {
+ if (!entries[i].type_guid[0]) {
+ inserted = 1;
+ D(DEBUG, "inserting");
+ memcpy(&entries[i], entry, sizeof(entries[i]));
+ break;
+ }
+ }
+
+ if (!inserted) {
+ D(ERR, "Unable to find empty partion entry");
+ }
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_UTFname(struct GPT_entry_table *table, const uint16_t *name);
+
+struct GPT_entry_raw *GPT_get_pointer(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+ if (entry->partition_guid[0] != 0)
+ return GPT_get_pointer_by_guid(table, (const char *) entry->partition_guid);
+ else if (entry->name[0] != 0)
+ return GPT_get_pointer_by_UTFname(table, entry->name);
+
+ D(WARN, "Name or guid needed to find entry");
+ return NULL;
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_guid(struct GPT_entry_table *table, const char *name)
+{
+ int current = (int) table->header->entries_count;
+
+ for (current = current - 1; current >= 0; --current) {
+ if (strncmp((char *) name,
+ (char *) table->entries[current].partition_guid, 16) == 0) {
+ return &table->entries[current];
+ }
+ }
+
+ return NULL;
+}
+
+int strncmp_UTF16_char(const uint16_t *s1, const char *s2, size_t n)
+{
+ if (n == 0)
+ return (0);
+ do {
+ if (((*s1) & 127) != *s2++)
+ return (((unsigned char) ((*s1) & 127)) - *(unsigned char *)--s2);
+ if (*s1++ == 0)
+ break;
+ } while (--n != 0);
+ return (0);
+}
+
+int strncmp_UTF16(const uint16_t *s1, const uint16_t *s2, size_t n)
+{
+ if (n == 0)
+ return (0);
+ do {
+ if ((*s1) != *s2++)
+ return (*s1 - *--s2);
+ if (*s1++ == 0)
+ break;
+ } while (--n != 0);
+ return (0);
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_name(struct GPT_entry_table *table, const char *name)
+{
+ //TODO: reverse direction
+ int count = (int) table->header->entries_count;
+ int current;
+
+ for (current = 0; current < count; ++current) {
+ if (strncmp_UTF16_char(table->entries[current].name,
+ (char *) name, 16) == 0) {
+ return &table->entries[current];
+ }
+ }
+
+ return NULL;
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_UTFname(struct GPT_entry_table *table, const uint16_t *name)
+{
+ int count = (int) table->header->entries_count;
+ int current;
+
+ for (current = 0; current < count; ++current) {
+ if (strncmp_UTF16(table->entries[current].name,
+ name, GPT_NAMELEN) == 0) {
+ return &table->entries[current];
+ }
+ }
+
+ return NULL;
+}
+
+void GPT_sync(struct GPT_entry_table *table)
+{
+ uint32_t crc;
+
+ //calculate crc32
+ crc = crc32(0, Z_NULL, 0);
+ crc = crc32(crc, (void*) table->entries, table->header->entries_count * sizeof(*table->entries));
+ table->header->partition_array_checksum = crc;
+
+ table->header->header_checksum = 0;
+ crc = crc32(0, Z_NULL, 0);
+ crc = crc32(crc, (void*) table->header, sizeof(*table->header));
+ table->header->header_checksum = crc;
+
+ //sync secondary partion
+ if (table->second_valid) {
+ memcpy((void *)table->second_entries, (void *) table->entries, table->partition_table_size);
+ memcpy((void *)table->second_header, (void *)table->header, sizeof(*table->header));
+ }
+
+ if(!ioctl(table->fd, BLKRRPART, NULL)) {
+ D(WARN, "Unable to force kernel to refresh partition table");
+ }
+}
+
+void GPT_to_UTF16(uint16_t *to, const char *from, int n)
+{
+ int i;
+ for (i = 0; i < (n - 1) && (to[i] = from[i]) != '\0'; ++i);
+ to[i] = '\0';
+}
+
+void GPT_from_UTF16(char *to, const uint16_t *from, int n)
+{
+ int i;
+ for (i = 0; i < (n - 1) && (to[i] = from[i] & 127) != '\0'; ++i);
+ to[i] = '\0';
+}
+
+static int GPT_check_overlap_except(struct GPT_entry_table *table,
+ struct GPT_entry_raw *entry,
+ struct GPT_entry_raw *exclude) {
+ int current = (int) table->header->entries_count;
+ int dontcheck;
+ struct GPT_entry_raw *current_entry;
+ if (entry->last_lba < entry->first_lba) {
+ D(WARN, "Start address have to be less than end address");
+ return 1;
+ }
+
+ for (current = current - 1; current >= 0; --current) {
+ current_entry = &table->entries[current];
+ dontcheck = strncmp((char *) entry->partition_guid,
+ (char *) current_entry->partition_guid , 16) == 0;
+ dontcheck |= current_entry->type_guid[0] == 0;
+ dontcheck |= current_entry == exclude;
+
+ if (!dontcheck && ((entry->last_lba >= current_entry->first_lba &&
+ entry->first_lba < current_entry->last_lba ))) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int GPT_check_overlap(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+ return GPT_check_overlap_except(table, entry, NULL);
+}
+
+static char *get_key_value(char *ptr, char **key, char **value)
+{
+ *key = ptr;
+ ptr = strchr(ptr, '=');
+
+ if (ptr == NULL)
+ return NULL;
+
+ *ptr++ = '\0';
+ *value = ptr;
+ ptr = strchr(ptr, ';');
+
+ if (ptr == NULL)
+ ptr = *value + strlen(*value);
+ else
+ *ptr = '\0';
+
+ *key = strip(*key);
+ *value = strip(*value);
+
+ return ptr;
+}
+
+//TODO: little endian?
+static int add_key_value(const char *key, const char *value, struct GPT_entry_raw *entry)
+{
+ char *endptr;
+ if (!strcmp(key, "type")) {
+ strncpy((char *) entry->type_guid, value, 16);
+ entry->type_guid[15] = 0;
+ }
+ else if (!strcmp(key, "guid")) {
+ strncpy((char *) entry->partition_guid, value, 16);
+ entry->type_guid[15] = 0;
+ }
+ else if (!strcmp(key, "firstlba")) {
+ entry->first_lba = strtoul(value, &endptr, 10);
+ if (*endptr != '\0') goto error;
+ }
+ else if (!strcmp(key, "lastlba")) {
+ entry->last_lba = strtoul(value, &endptr, 10);
+ if (*endptr != '\0') goto error;
+ }
+ else if (!strcmp(key, "flags")) {
+ entry->flags = strtoul(value, &endptr, 10);
+ if (*endptr != '\0') goto error;
+ }
+ else if (!strcmp(key, "name")) {
+ GPT_to_UTF16(entry->name, value, GPT_NAMELEN);
+ }
+ else {
+ goto error;
+ }
+
+ return 0;
+
+error:
+ D(ERR, "Could not find key or parse value: %s,%s", key, value);
+ return 1;
+}
+
+int GPT_parse_entry(char *string, struct GPT_entry_raw *entry)
+{
+ char *ptr = string;
+ char *key, *value;
+
+ while ((ptr = get_key_value(ptr, &key, &value)) != NULL) {
+ if (add_key_value(key, value, entry)) {
+ D(WARN, "key or value not valid: %s %s", key, value);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void entry_set_guid(int n, uint8_t *guid)
+{
+ guid[0] = (uint8_t) (n + 1);
+ int fd;
+ fd = open("/dev/urandom", O_RDONLY);
+ read(fd, &guid[1], 15);
+ close(fd);
+}
+
+void GPT_default_content(struct GPT_content *content, struct GPT_entry_table *table)
+{
+ if (table != NULL) {
+ memcpy(&content->header, table->header, sizeof(content->header));
+ }
+ else {
+ D(WARN, "Could not locate old gpt table, using default values");
+ memset(&content->header, 0, sizeof(content->header) / sizeof(int));
+ content->header = (struct GPT_header) {
+ .revision = 0x0100,
+ .header_size = sizeof(content->header),
+ .header_checksum = 0,
+ .reserved_zeros = 0,
+ .current_lba = 1,
+ .backup_lba = 1,
+ .entry_size = sizeof(struct GPT_entry_raw),
+ .partition_array_checksum = 0
+ };
+ strncpy((char *)content->header.signature, "EFI PART", 8);
+ entry_set_guid(0, content->header.disk_guid);
+ }
+}
+
+static int get_config_uint64(cnode *node, uint64_t *ptr, const char *name)
+{
+ const char *tmp;
+ uint64_t val;
+ char *endptr;
+ if ((tmp = config_str(node, name, NULL))) {
+ val = strtoull(tmp, &endptr, 10);
+ if (*endptr != '\0') {
+ D(WARN, "Value for %s is not a number: %s", name, tmp);
+ return 1;
+ }
+ *ptr = val;
+ return 0;
+ }
+ return 1;
+}
+
+static void GPT_parse_header(cnode *node, struct GPT_content *content)
+{
+ get_config_uint64(node, &content->header.current_lba, "header_lba");
+ get_config_uint64(node, &content->header.backup_lba, "backup_lba");
+ get_config_uint64(node, &content->header.first_usable_lba, "first_lba");
+ get_config_uint64(node, &content->header.last_usable_lba, "last_lba");
+ get_config_uint64(node, &content->header.entries_lba, "entries_lba");
+}
+
+static int GPT_parse_partitions(cnode *node, struct GPT_content *content)
+{
+ cnode *current;
+ int i;
+ uint64_t partition_size;
+ for (i = 0, current = node->first_child; current; current = current->next, ++i) {
+ entry_set_guid(i, content->entries[i].partition_guid);
+ memcpy(&content->entries[i].type_guid, partition_type_uuid, 16);
+ if (get_config_uint64(current, &content->entries[i].first_lba, "first_lba")) {
+ D(ERR, "first_lba not specified");
+ return 1;
+ }
+ if (get_config_uint64(current, &partition_size, "partition_size")) {
+ D(ERR, "partition_size not specified");
+ return 1;
+ }
+ get_config_uint64(current, &content->entries[i].flags, "flags");
+ content->entries[i].last_lba = content->entries[i].first_lba + partition_size - 1;
+ GPT_to_UTF16(content->entries[i].name, current->name, 16);
+ }
+ return 0;
+}
+
+static inline int cnode_count(cnode *node)
+{
+ int i;
+ cnode *current;
+ for (i = 0, current = node->first_child; current; current = current->next, ++i)
+ ;
+ return i;
+}
+
+
+static int GPT_parse_cnode(cnode *root, struct GPT_content *content)
+{
+ cnode *partnode;
+
+ if (!(partnode = config_find(root, "partitions"))) {
+ D(ERR, "Could not find partition table");
+ return 0;
+ }
+
+ GPT_parse_header(root, content);
+
+ content->header.entries_count = cnode_count(partnode);
+ content->entries = malloc(content->header.entries_count * sizeof(struct GPT_entry_raw));
+
+ if (GPT_parse_partitions(partnode, content)) {
+ D(ERR, "Could not parse partitions");
+ return 0;
+ }
+
+ return 1;
+}
+
+int GPT_parse_file(int fd, struct GPT_content *content)
+{
+ char *data;
+ int size;
+ int ret;
+ cnode *root = config_node("", "");
+
+ size = get_file_size(fd);
+ data = (char *) mmap(NULL, size + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+
+ if (data == NULL) {
+ if (size == 0)
+ D(ERR, "config file empty");
+ else
+ D(ERR, "Out of memory");
+ return 0;
+ }
+
+ data[size - 1] = 0;
+ config_load(root, data);
+
+ if (root->first_child == NULL) {
+ D(ERR, "Could not read config file");
+ return 0;
+ }
+
+ ret = GPT_parse_cnode(root, content);
+ munmap(data, size);
+ return ret;
+}
+
+void GPT_release_content(struct GPT_content *content)
+{
+ free(content->entries);
+}
+
+int GPT_write_content(const char *device, struct GPT_content *content)
+{
+ struct GPT_entry_table *maptable;
+
+ maptable = GPT_get_from_content(device, content);
+ if (maptable == NULL) {
+ D(ERR, "could not map device");
+ return 0;
+ }
+
+ memcpy(maptable->header, &content->header, sizeof(*maptable->header));
+ memcpy(maptable->entries, content->entries,
+ content->header.entries_count * sizeof(*maptable->entries));
+
+ //GPT_sync(maptable);
+ GPT_release_device(maptable);
+
+ return 1;
+}
+
diff --git a/fastbootd/commands/partitions.h b/fastbootd/commands/partitions.h
new file mode 100644
index 0000000..f2d590f
--- /dev/null
+++ b/fastbootd/commands/partitions.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * 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.
+ */
+
+
+#ifndef __FASTBOOTD_PATITIONS_
+#define __FASTBOOTD_PATITIONS_
+
+#include <stdint.h>
+
+#define GPT_ENTRIES 128
+#define GPT_NAMELEN 36
+
+// it should be passed in little endian order
+struct GPT_entry_raw {
+ uint8_t type_guid[16];
+ uint8_t partition_guid[16];
+ uint64_t first_lba; // little endian
+ uint64_t last_lba;
+ uint64_t flags;
+ uint16_t name[GPT_NAMELEN]; // UTF-16 LE
+};
+
+struct GPT_mapping {
+ void *map_ptr;
+ void *ptr;
+ unsigned size;
+};
+
+struct GPT_entry_table {
+ int fd;
+
+ struct GPT_mapping header_map;
+ struct GPT_mapping entries_map;
+ struct GPT_mapping sec_header_map;
+ struct GPT_mapping sec_entries_map;
+
+ struct GPT_header *header;
+ struct GPT_entry_raw *entries;
+ struct GPT_header *second_header;
+ struct GPT_entry_raw *second_entries;
+
+ unsigned sector_size;
+ unsigned partition_table_size;
+ int second_valid;
+};
+
+struct GPT_header {
+ uint8_t signature[8];
+ uint32_t revision;
+ uint32_t header_size;
+ uint32_t header_checksum;
+ uint32_t reserved_zeros;
+ uint64_t current_lba;
+ uint64_t backup_lba;
+ uint64_t first_usable_lba;
+ uint64_t last_usable_lba;
+ uint8_t disk_guid[16];
+ uint64_t entries_lba;
+ uint32_t entries_count;
+ uint32_t entry_size;
+ uint32_t partition_array_checksum;
+ // the rest should be filled with zeros
+} __attribute__((packed));
+
+struct GPT_content {
+ struct GPT_header header;
+ struct GPT_entry_raw *entries;
+};
+
+
+struct GPT_entry_table* GPT_get_device(const char *, unsigned lba);
+
+void GPT_release_device(struct GPT_entry_table *);
+
+void GPT_edit_entry(struct GPT_entry_table *table,
+ struct GPT_entry_raw *old_entry,
+ struct GPT_entry_raw *new_entry);
+
+int GPT_delete_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+
+void GPT_add_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+
+struct GPT_entry_raw *GPT_get_pointer(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+struct GPT_entry_raw *GPT_get_pointer_by_guid(struct GPT_entry_table *, const char *);
+struct GPT_entry_raw *GPT_get_pointer_by_name(struct GPT_entry_table *, const char *);
+
+//Use after every edit operation
+void GPT_sync();
+
+void GPT_to_UTF16(uint16_t *, const char *, int );
+void GPT_from_UTF16(char *, const uint16_t *, int);
+
+int GPT_parse_entry(char *string, struct GPT_entry_raw *entry);
+
+void GPT_default_content(struct GPT_content *content, struct GPT_entry_table *table);
+
+void GPT_release_content(struct GPT_content *content);
+
+int GPT_parse_file(int fd, struct GPT_content *content);
+
+int GPT_write_content(const char *device, struct GPT_content *content);
+
+int gpt_mmap(struct GPT_mapping *mapping, uint64_t location, int size, int fd);
+
+void gpt_unmap(struct GPT_mapping *mapping);
+
+#endif
diff --git a/fastbootd/commands/virtual_partitions.c b/fastbootd/commands/virtual_partitions.c
new file mode 100644
index 0000000..813f485
--- /dev/null
+++ b/fastbootd/commands/virtual_partitions.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * 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 "commands/virtual_partitions.h"
+#include "debug.h"
+
+static struct virtual_partition *partitions = NULL;
+
+int try_handle_virtual_partition(struct protocol_handle *handle, const char *arg)
+{
+ struct virtual_partition *current;
+
+ for (current = partitions; current != NULL; current = current->next) {
+ if (!strcmp(current->name, arg)) {
+ current->handler(handle, arg);
+ }
+ }
+
+ return 0;
+}
+
+void virtual_partition_register(
+ const char * name,
+ void (*handler)(struct protocol_handle *phandle, const char *arg))
+{
+ struct virtual_partition *new;
+ new = malloc(sizeof(*new));
+ if (new) {
+ new->name = name;
+ new->handler = handler;
+ new->next = partitions;
+ partitions = new;
+ }
+ else {
+ D(ERR, "Out of memory");
+ }
+}
diff --git a/fastbootd/commands/virtual_partitions.h b/fastbootd/commands/virtual_partitions.h
new file mode 100644
index 0000000..88df71e
--- /dev/null
+++ b/fastbootd/commands/virtual_partitions.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * 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.
+ */
+
+#ifndef FASTBOOTD_VIRTUAL_PARTITIONS_H
+#define FASTBOOTD_VIRTUAL_PARTITIONS_H
+
+#include "protocol.h"
+
+struct virtual_partition {
+ struct virtual_partition *next;
+ const char *name;
+ void (*handler)(struct protocol_handle *phandle, const char *arg);
+};
+
+int try_handle_virtual_partition(struct protocol_handle *handle, const char *arg);
+
+void virtual_partition_register(
+ const char * name,
+ void (*handler)(struct protocol_handle *phandle, const char *arg));
+
+#endif
diff --git a/fastbootd/config.c b/fastbootd/config.c
index b8503fd..fe6da69 100644
--- a/fastbootd/config.c
+++ b/fastbootd/config.c
@@ -35,29 +35,13 @@
#include <sys/types.h>
#include "protocol.h"
+#include "utils.h"
#include "debug.h"
// TODO: change config path
#define CONFIG_PATH "/data/fastboot.cfg"
-static char *strip(char *str)
-{
- int n;
-
- n = strspn(str, " \t");
- str += n;
-
- for (n = strlen(str) - 1; n >= 0; n--) {
- if (str[n] == ' ' || str[n] == '\t')
- str[n] = '\0';
- else
- break;
- }
-
- return str;
-}
-
static int config_parse_line(char *line)
{
char *c;
diff --git a/fastbootd/fastbootd.c b/fastbootd/fastbootd.c
index 98df0db..90b9ef9 100644
--- a/fastbootd/fastbootd.c
+++ b/fastbootd/fastbootd.c
@@ -20,6 +20,7 @@
#include <cutils/klog.h>
#include "debug.h"
+#include "trigger.h"
unsigned int debug_level = DEBUG;
@@ -36,6 +37,7 @@
klog_set_level(6);
config_init();
+ load_trigger();
commands_init();
usb_init();
while (1) {
diff --git a/fastbootd/include/vendor_trigger.h b/fastbootd/include/vendor_trigger.h
new file mode 100644
index 0000000..66f65c4
--- /dev/null
+++ b/fastbootd/include/vendor_trigger.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * 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.
+ */
+
+#ifndef __VENDOR_TRIGGER_H_
+#define __VENDOR_TRIGGER_H_
+
+#define TRIGGER_MODULE_ID "vendortrigger"
+#include <hardware/hardware.h>
+
+__BEGIN_DECLS
+
+struct GPT_entry_raw;
+struct GPT_content;
+
+/*
+ * Structer with function pointers may become longer in the future
+ */
+
+struct vendor_trigger_t {
+ struct hw_device_t common;
+
+ /*
+ * This function runs at the beggining and shoud never be changed
+ *
+ * version is number parameter indicating version on the fastbootd side
+ * libversion is version indicateing version of the library version
+ *
+ * returns 0 if it can cooperate with the current version and 1 in opposite
+ */
+ int (*check_version)(const int version, int *libversion);
+
+
+ /*
+ * Return value 1 forbid the action from the vendor site and sets errno
+ */
+ int (* gpt_layout)(struct GPT_content *);
+ int (* oem_cmd)(const char *arg, const char **response);
+};
+
+__END_DECLS
+
+#endif
diff --git a/fastbootd/other/gptedit.c b/fastbootd/other/gptedit.c
new file mode 100644
index 0000000..07d01cb
--- /dev/null
+++ b/fastbootd/other/gptedit.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * 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 <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+
+#include <cutils/klog.h>
+
+#include "commands/partitions.h"
+#include "debug.h"
+
+unsigned int debug_level = DEBUG;
+//TODO: add tool to generate config file
+
+void usage() {
+ fprintf(stderr,
+ "usage: test_gpt [ <option> ] <file>\n"
+ "\n"
+ "options:\n"
+ " -p print partitions\n"
+ " -c print config file\n"
+ " -a adds new partition\n"
+ " -d deletes partition (-o needed)\n"
+ "\n"
+ " -n name@startlba,endlba new partition detail\n"
+ " -o old partition name\n"
+ " -t type guid\n"
+ " -g partition guid\n"
+ " -l gpt_location specyfies gpt secto\n"
+ );
+
+}
+
+void printGPT(struct GPT_entry_table *table);
+void addGPT(struct GPT_entry_table *table, const char *arg, const char *guid, const char *tguid);
+void deleteGPT(struct GPT_entry_table *table, const char *name);
+void configPrintGPT(struct GPT_entry_table *table);
+
+int main(int argc, char *argv[]) {
+ int print_cmd = 0;
+ int config_cmd = 0;
+ int add_cmd = 0;
+ int del_cmd = 0;
+ int sync_cmd = 0;
+ int c;
+ const char *new_partition = NULL;
+ const char *old_partition = NULL;
+ const char *type_guid = NULL;
+ const char *partition_guid = NULL;
+ unsigned gpt_location = 1;
+
+ klog_init();
+ klog_set_level(6);
+
+ const struct option longopts[] = {
+ {"print", no_argument, 0, 'p'},
+ {"config-print", no_argument, 0, 'c'},
+ {"add", no_argument, 0, 'a'},
+ {"del", no_argument, 0, 'd'},
+ {"new", required_argument, 0, 'n'},
+ {"old", required_argument, 0, 'o'},
+ {"type", required_argument, 0, 't'},
+ {"sync", required_argument, 0, 's'},
+ {"guid", required_argument, 0, 'g'},
+ {"location", required_argument, 0, 'l'},
+ {0, 0, 0, 0}
+ };
+
+ while (1) {
+ c = getopt_long(argc, argv, "pcadt:g:n:o:sl:", longopts, NULL);
+ /* Alphabetical cases */
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'p':
+ print_cmd = 1;
+ break;
+ case 'c':
+ config_cmd = 1;
+ break;
+ case 'a':
+ add_cmd = 1;
+ break;
+ case 'd':
+ del_cmd = 1;
+ break;
+ case 'n':
+ new_partition = optarg;
+ break;
+ case 'o':
+ old_partition = optarg;
+ break;
+ case 't':
+ type_guid = optarg;
+ case 'g':
+ partition_guid = optarg;
+ break;
+ case 's':
+ sync_cmd = 1;
+ break;
+ case 'l':
+ gpt_location = strtoul(optarg, NULL, 10);
+ fprintf(stderr, "Got offset as %d", gpt_location);
+ break;
+ case '?':
+ return 1;
+ default:
+ abort();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ usage();
+ return 1;
+ }
+
+ const char *path = argv[0];
+ struct GPT_entry_table *table = GPT_get_device(path, gpt_location);
+ if (table == NULL) {
+ fprintf(stderr, "unable to get GPT table from %s\n", path);
+ return 1;
+ }
+
+ fprintf(stderr, "entries %d, name %s\n", table->header->entries_count, (char *) table->header->signature);
+
+
+ if (add_cmd)
+ addGPT(table, new_partition, partition_guid, type_guid);
+ if (del_cmd)
+ deleteGPT(table, old_partition);
+ if (print_cmd)
+ printGPT(table);
+ if (config_cmd)
+ configPrintGPT(table);
+ if (sync_cmd)
+ GPT_sync(table);
+
+ GPT_release_device(table);
+
+ return 0;
+}
+
+void printGPT(struct GPT_entry_table *table) {
+ struct GPT_entry_raw *entry = table->entries;
+ unsigned n, m;
+ char name[GPT_NAMELEN + 1];
+
+ printf("ptn start block end block name\n");
+ printf("---- ------------- -------------\n");
+
+ for (n = 0; n < table->header->entries_count; n++, entry++) {
+ if (entry->type_guid[0] == 0)
+ continue;
+ for (m = 0; m < GPT_NAMELEN; m++) {
+ name[m] = entry->name[m] & 127;
+ }
+ name[m] = 0;
+ printf("#%03d %13lld %13lld %s\n",
+ n + 1, entry->first_lba, entry->last_lba, name);
+ }
+}
+
+void configPrintGPT(struct GPT_entry_table *table) {
+ struct GPT_entry_raw *entry = table->entries;
+ unsigned n, m;
+ char name[GPT_NAMELEN + 1];
+
+ printf("header_lba %lld\n", table->header->current_lba);
+ printf("backup_lba %lld\n", table->header->backup_lba);
+ printf("first_lba %lld\n", table->header->first_usable_lba);
+ printf("last_lba %lld\n", table->header->last_usable_lba);
+ printf("entries_lba %lld\n", table->header->entries_lba);
+
+ printf("\npartitions {\n");
+
+ for (n = 0; n < table->header->entries_count; n++, entry++) {
+ uint64_t size = entry->last_lba - entry->first_lba + 1;
+
+ if (entry->type_guid[0] == 0)
+ continue;
+ for (m = 0; m < GPT_NAMELEN; m++) {
+ name[m] = entry->name[m] & 127;
+ }
+ name[m] = 0;
+
+ printf(" %s {\n", name);
+ printf(" first_lba %lld\n", entry->first_lba);
+ printf(" partition_size %lld\n", size);
+ if (entry->flags != 0)
+ printf(" flags %lld\n", entry->flags);
+ printf(" }\n\n");
+ }
+ printf("}\n");
+}
+
+void addGPT(struct GPT_entry_table *table, const char *str , const char *guid, const char *tguid) {
+ char *c, *c2;
+ char *arg = malloc(strlen(str));
+ char *name = arg;
+ unsigned start, end;
+ strcpy(arg, str);
+ if (guid == NULL || tguid == NULL) {
+ fprintf(stderr, "Type guid and partion guid needed");
+ free(arg);
+ return;
+ }
+
+ c = strchr(arg, '@');
+
+ if (c == NULL) {
+ fprintf(stderr, "Wrong entry format");
+ free(arg);
+ return;
+ }
+
+ *c++ = '\0';
+
+ c2 = strchr(c, ',');
+
+ if (c2 == NULL) {
+ fprintf(stderr, "Wrong entry format");
+ free(arg);
+ return;
+ }
+
+ start = strtoul(c, NULL, 10);
+ *c2++ = '\0';
+ end = strtoul(c2, NULL, 10);
+
+ struct GPT_entry_raw data;
+ strncpy((char *)data.partition_guid, guid, 15);
+ data.partition_guid[15] = '\0';
+ strncpy((char *)data.type_guid, tguid, 15);
+ data.type_guid[15] = '\0';
+ GPT_to_UTF16(data.name, name, GPT_NAMELEN);
+ data.first_lba = start;
+ data.last_lba = end;
+
+ fprintf(stderr, "Adding (%d,%d) %s as, [%s, %s]", start, end, name, (char *) data.type_guid, (char *) data.partition_guid);
+ GPT_add_entry(table, &data);
+ free(arg);
+}
+
+void deleteGPT(struct GPT_entry_table *table, const char *name) {
+ struct GPT_entry_raw *entry;
+
+ if (name == NULL) {
+ fprintf(stderr, "Need partition name");
+ return;
+ }
+
+ entry = GPT_get_pointer_by_name(table, name);
+
+ if (!entry) {
+ fprintf(stderr, "Unable to find partition: %s", name);
+ return;
+ }
+ GPT_delete_entry(table, entry);
+}
+
diff --git a/fastbootd/other/partitions.sample.cfg b/fastbootd/other/partitions.sample.cfg
new file mode 100644
index 0000000..49562cf
--- /dev/null
+++ b/fastbootd/other/partitions.sample.cfg
@@ -0,0 +1,60 @@
+
+header_lba 1
+backup_lba 101
+first_lba 43
+last_lba 100
+entries_lba 2
+
+partitions {
+ #You can generate this as output from gptedit -c
+ SOS {
+ first_lba 28672
+ partition_size 16384
+ }
+
+ DTB {
+ first_lba 45056
+ partition_size 8192
+ }
+
+ LNX {
+ first_lba 53248
+ partition_size 16384
+ }
+
+ APP {
+ first_lba 69632
+ partition_size 1048576
+ }
+
+ CAC {
+ first_lba 1118208
+ partition_size 1572864
+ }
+
+ MSC {
+ first_lba 2691072
+ partition_size 4096
+ }
+
+ USP {
+ first_lba 2695168
+ partition_size 65536
+ }
+
+ MDA {
+ first_lba 2760704
+ partition_size 4096
+ }
+
+ FCT {
+ first_lba 2764800
+ partition_size 32768
+ }
+
+ UDA {
+ first_lba 2797568
+ partition_size 27975680
+ }
+}
+
diff --git a/fastbootd/other/vendor_trigger.c b/fastbootd/other/vendor_trigger.c
new file mode 100644
index 0000000..9b7562c
--- /dev/null
+++ b/fastbootd/other/vendor_trigger.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * 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 <stdlib.h>
+
+#include "vendor_trigger.h"
+#include "debug.h"
+
+unsigned int debug_level = DEBUG;
+
+static const int version = 1;
+
+int delete_partition(struct GPT_entry_raw *);
+int add_partition(struct GPT_entry_raw *);
+int modify_partition(struct GPT_entry_raw *, struct GPT_entry_raw *);
+
+int check_version(const int fastboot_version, int *libversion) {
+ *libversion = version;
+ return !(fastboot_version == version);
+}
+
+int delete_partition(struct GPT_entry_raw *entry) {
+ D(DEBUG, "message from libvendor");
+ return 0;
+}
+
+int add_partition(struct GPT_entry_raw *entry) {
+ D(DEBUG, "message from libvendor");
+ return 0;
+}
+
+int modify_partition(struct GPT_entry_raw *oldentry, struct GPT_entry_raw *newentry) {
+ D(DEBUG, "message from libvendor");
+ return 0;
+}
+
+int gpt_layout(struct GPT_content *table) {
+ D(DEBUG, "message from libvendor");
+ return 0;
+}
+
+int oem_cmd(const char *arg, const char **response) {
+ D(DEBUG, "message from libvendor, oem catched request %s", arg);
+ return 0;
+}
+
+static int close_triggers(struct vendor_trigger_t *dev)
+{
+ if (dev)
+ free(dev);
+
+ return 0;
+}
+
+static int open_triggers(const struct hw_module_t *module, char const *name,
+ struct hw_device_t **device) {
+ struct vendor_trigger_t *dev = malloc(sizeof(struct vendor_trigger_t));
+ klog_init();
+ klog_set_level(6);
+
+ memset(dev, 0, sizeof(*dev));
+ dev->common.module = (struct hw_module_t *) module;
+ dev->common.close = (int (*)(struct hw_device_t *)) close_triggers;
+
+ dev->gpt_layout = gpt_layout;
+ dev->oem_cmd = oem_cmd;
+
+ *device = (struct hw_device_t *) dev;
+
+ return 0;
+}
+
+
+static struct hw_module_methods_t trigger_module_methods = {
+ .open = open_triggers,
+};
+
+struct hw_module_t HAL_MODULE_INFO_SYM = {
+ .tag = HARDWARE_MODULE_TAG,
+ .version_major = 1,
+ .version_minor = 0,
+ .id = TRIGGER_MODULE_ID,
+ .name = "vendor trigger library for fastbootd",
+ .author = "Google, Inc.",
+ .methods = &trigger_module_methods,
+};
+
diff --git a/fastbootd/trigger.c b/fastbootd/trigger.c
new file mode 100644
index 0000000..e63e64d
--- /dev/null
+++ b/fastbootd/trigger.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * 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 <dlfcn.h>
+
+#include <hardware/hardware.h>
+#include "debug.h"
+#include "trigger.h"
+#include "protocol.h"
+#include "vendor_trigger.h"
+
+static const int version = 1;
+
+static struct vendor_trigger_t *triggers = NULL;
+
+int load_trigger() {
+ int err;
+ hw_module_t* module;
+ hw_device_t* device;
+ int libversion;
+
+ err = hw_get_module(TRIGGER_MODULE_ID, (hw_module_t const**)&module);
+
+ if (err == 0) {
+ err = module->methods->open(module, NULL, &device);
+
+ if (err == 0) {
+ triggers = (struct vendor_trigger_t *) device;
+ } else {
+ D(WARN, "Libvendor load error");
+ return 1;
+ }
+ }
+ else {
+ D(WARN, "Libvendor not load: %s", strerror(-err));
+ return 0;
+ }
+
+ if (triggers->check_version != NULL &&
+ triggers->check_version(version, &libversion)) {
+
+ triggers = NULL;
+ D(ERR, "Library report incompability");
+ return 1;
+ }
+ D(INFO, "libvendortrigger loaded");
+
+ return 0;
+}
+
+int trigger_oem_cmd(const char *arg, const char **response) {
+ if (triggers != NULL && triggers->oem_cmd != NULL)
+ return triggers->oem_cmd(arg, response);
+ return 0;
+}
+
+int trigger_gpt_layout(struct GPT_content *table) {
+ if (triggers != NULL && triggers->gpt_layout != NULL)
+ return triggers->gpt_layout(table);
+ return 0;
+}
+
diff --git a/fastbootd/trigger.h b/fastbootd/trigger.h
new file mode 100644
index 0000000..404acb4
--- /dev/null
+++ b/fastbootd/trigger.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * 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.
+ */
+
+#ifndef __FASTBOOTD_TRIGGER_H_
+#define __FASTBOOTD_TRIGGER_H_
+
+#include "commands/partitions.h"
+#include "vendor_trigger.h"
+
+int load_trigger();
+
+/* same as in struct triggers */
+
+int trigger_gpt_layout(struct GPT_content *table);
+int trigger_oem_cmd(const char *arg, const char **response);
+
+#endif
diff --git a/fastbootd/utils.c b/fastbootd/utils.c
new file mode 100644
index 0000000..16e1c09
--- /dev/null
+++ b/fastbootd/utils.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+
+#include "utils.h"
+#include "debug.h"
+
+#ifndef BLKDISCARD
+#define BLKDISCARD _IO(0x12,119)
+#endif
+
+#ifndef BLKSECDISCARD
+#define BLKSECDISCARD _IO(0x12,125)
+#endif
+
+
+int get_stream_size(FILE *stream) {
+ int size;
+ fseek(stream, 0, SEEK_END);
+ size = ftell(stream);
+ fseek(stream, 0, SEEK_SET);
+ return size;
+}
+
+uint64_t get_block_device_size(int fd)
+{
+ uint64_t size = 0;
+ int ret;
+
+ ret = ioctl(fd, BLKGETSIZE64, &size);
+
+ if (ret)
+ return 0;
+
+ return size;
+}
+
+uint64_t get_file_size(int fd)
+{
+ struct stat buf;
+ int ret;
+ int64_t computed_size;
+
+ ret = fstat(fd, &buf);
+ if (ret)
+ return 0;
+
+ if (S_ISREG(buf.st_mode))
+ computed_size = buf.st_size;
+ else if (S_ISBLK(buf.st_mode))
+ computed_size = get_block_device_size(fd);
+ else
+ computed_size = 0;
+
+ return computed_size;
+}
+
+uint64_t get_file_size64(int fd)
+{
+ struct stat64 buf;
+ int ret;
+ uint64_t computed_size;
+
+ ret = fstat64(fd, &buf);
+ if (ret)
+ return 0;
+
+ if (S_ISREG(buf.st_mode))
+ computed_size = buf.st_size;
+ else if (S_ISBLK(buf.st_mode))
+ computed_size = get_block_device_size(fd);
+ else
+ computed_size = 0;
+
+ return computed_size;
+}
+
+
+char *strip(char *str)
+{
+ int n;
+
+ n = strspn(str, " \t");
+ str += n;
+ n = strcspn(str, " \t");
+ str[n] = '\0';
+
+ return str;
+}
+
+int wipe_block_device(int fd, int64_t len)
+{
+ uint64_t range[2];
+ int ret;
+
+ range[0] = 0;
+ range[1] = len;
+ ret = ioctl(fd, BLKSECDISCARD, &range);
+ if (ret < 0) {
+ range[0] = 0;
+ range[1] = len;
+ ret = ioctl(fd, BLKDISCARD, &range);
+ if (ret < 0) {
+ D(WARN, "Discard failed\n");
+ return 1;
+ } else {
+ D(WARN, "Wipe via secure discard failed, used discard instead\n");
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/fastbootd/utils.h b/fastbootd/utils.h
new file mode 100644
index 0000000..a553a25
--- /dev/null
+++ b/fastbootd/utils.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * 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.
+ */
+
+#ifndef _FASTBOOT_UTLIS_H
+#define _FASTBOOT_UTILS_H
+
+#include <stdio.h>
+
+int get_stream_size(FILE *);
+
+char *strip(char *str);
+
+uint64_t get_file_size64(int fd);
+uint64_t get_file_size(int fd);
+uint64_t get_block_device_size(int fd);
+int wipe_block_device(int fd, int64_t len);
+
+#define ROUND_TO_PAGE(address,pagesize) ((address + pagesize - 1) & (~(pagesize - 1)))
+
+#define ROUND_UP(number,size) (((number + size - 1) / size) * size)
+
+#endif
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 688c7ff..424d180 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -28,6 +28,7 @@
#include <unistd.h>
#include <batteryservice/BatteryService.h>
#include <cutils/klog.h>
+#include <utils/Errors.h>
#include <utils/String8.h>
#include <utils/Vector.h>
@@ -272,6 +273,50 @@
props.chargerWirelessOnline;
}
+status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
+ status_t ret = BAD_VALUE;
+
+ switch(id) {
+ case BATTERY_PROP_CHARGE_COUNTER:
+ if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+ val->valueInt =
+ getIntField(mHealthdConfig->batteryChargeCounterPath);
+ ret = NO_ERROR;
+ } else {
+ ret = NAME_NOT_FOUND;
+ }
+ break;
+
+ case BATTERY_PROP_CURRENT_NOW:
+ if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+ val->valueInt =
+ getIntField(mHealthdConfig->batteryCurrentNowPath);
+ ret = NO_ERROR;
+ } else {
+ ret = NAME_NOT_FOUND;
+ }
+ break;
+
+ case BATTERY_PROP_CURRENT_AVG:
+ if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ val->valueInt =
+ getIntField(mHealthdConfig->batteryCurrentAvgPath);
+ ret = NO_ERROR;
+ } else {
+ ret = NAME_NOT_FOUND;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (ret != NO_ERROR)
+ val->valueInt = INT_MIN;
+
+ return ret;
+}
+
void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) {
String8 path;
@@ -358,6 +403,14 @@
mHealthdConfig->batteryCurrentNowPath = path;
}
+ if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+ path.clear();
+ path.appendFormat("%s/%s/current_avg",
+ POWER_SUPPLY_SYSFS_PATH, name);
+ if (access(path, R_OK) == 0)
+ mHealthdConfig->batteryCurrentAvgPath = path;
+ }
+
if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/charge_counter",
@@ -416,7 +469,7 @@
KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
if (nosvcmgr == false) {
- mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar(this);
+ mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
mBatteryPropertiesRegistrar->publish();
}
}
diff --git a/healthd/BatteryMonitor.h b/healthd/BatteryMonitor.h
index ba291af..34b423d 100644
--- a/healthd/BatteryMonitor.h
+++ b/healthd/BatteryMonitor.h
@@ -17,6 +17,7 @@
#ifndef HEALTHD_BATTERYMONITOR_H
#define HEALTHD_BATTERYMONITOR_H
+#include <batteryservice/BatteryService.h>
#include <binder/IInterface.h>
#include <utils/String8.h>
#include <utils/Vector.h>
@@ -41,6 +42,7 @@
void init(struct healthd_config *hc, bool nosvcmgr);
bool update(void);
+ status_t getProperty(int id, struct BatteryProperty *val);
private:
struct healthd_config *mHealthdConfig;
diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp
index 6a33ad8..f2edee4 100644
--- a/healthd/BatteryPropertiesRegistrar.cpp
+++ b/healthd/BatteryPropertiesRegistrar.cpp
@@ -23,11 +23,9 @@
#include <utils/Mutex.h>
#include <utils/String16.h>
-namespace android {
+#include "healthd.h"
-BatteryPropertiesRegistrar::BatteryPropertiesRegistrar(BatteryMonitor* monitor) {
- mBatteryMonitor = monitor;
-}
+namespace android {
void BatteryPropertiesRegistrar::publish() {
defaultServiceManager()->addService(String16("batterypropreg"), this);
@@ -53,7 +51,7 @@
mListeners.add(listener);
listener->asBinder()->linkToDeath(this);
}
- mBatteryMonitor->update();
+ healthd_battery_update();
}
void BatteryPropertiesRegistrar::unregisterListener(const sp<IBatteryPropertiesListener>& listener) {
@@ -67,6 +65,10 @@
}
}
+status_t BatteryPropertiesRegistrar::getProperty(int id, struct BatteryProperty *val) {
+ return healthd_get_property(id, val);
+}
+
void BatteryPropertiesRegistrar::binderDied(const wp<IBinder>& who) {
Mutex::Autolock _l(mRegistrationLock);
diff --git a/healthd/BatteryPropertiesRegistrar.h b/healthd/BatteryPropertiesRegistrar.h
index 793ddad..3e86fdf 100644
--- a/healthd/BatteryPropertiesRegistrar.h
+++ b/healthd/BatteryPropertiesRegistrar.h
@@ -17,8 +17,6 @@
#ifndef HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
#define HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
-#include "BatteryMonitor.h"
-
#include <binder/IBinder.h>
#include <utils/Mutex.h>
#include <utils/Vector.h>
@@ -28,22 +26,19 @@
namespace android {
-class BatteryMonitor;
-
class BatteryPropertiesRegistrar : public BnBatteryPropertiesRegistrar,
public IBinder::DeathRecipient {
public:
- BatteryPropertiesRegistrar(BatteryMonitor* monitor);
void publish();
void notifyListeners(struct BatteryProperties props);
private:
- BatteryMonitor* mBatteryMonitor;
Mutex mRegistrationLock;
Vector<sp<IBatteryPropertiesListener> > mListeners;
void registerListener(const sp<IBatteryPropertiesListener>& listener);
void unregisterListener(const sp<IBatteryPropertiesListener>& listener);
+ status_t getProperty(int id, struct BatteryProperty *val);
void binderDied(const wp<IBinder>& who);
};
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
index 9b84c3e..62f4d11 100644
--- a/healthd/healthd.cpp
+++ b/healthd/healthd.cpp
@@ -31,6 +31,7 @@
#include <cutils/uevent.h>
#include <sys/epoll.h>
#include <sys/timerfd.h>
+#include <utils/Errors.h>
using namespace android;
@@ -49,6 +50,7 @@
.batteryTemperaturePath = String8(String8::kEmptyString),
.batteryTechnologyPath = String8(String8::kEmptyString),
.batteryCurrentNowPath = String8(String8::kEmptyString),
+ .batteryCurrentAvgPath = String8(String8::kEmptyString),
.batteryChargeCounterPath = String8(String8::kEmptyString),
};
@@ -89,7 +91,11 @@
KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
}
-static void battery_update(void) {
+status_t healthd_get_property(int id, struct BatteryProperty *val) {
+ return gBatteryMonitor->getProperty(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).
@@ -114,7 +120,7 @@
}
static void periodic_chores() {
- battery_update();
+ healthd_battery_update();
}
static void uevent_init(void) {
@@ -144,7 +150,7 @@
while (*cp) {
if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
- battery_update();
+ healthd_battery_update();
break;
}
diff --git a/healthd/healthd.h b/healthd/healthd.h
index 5374fb1..dc3f67e 100644
--- a/healthd/healthd.h
+++ b/healthd/healthd.h
@@ -18,6 +18,7 @@
#define _HEALTHD_H_
#include <batteryservice/BatteryService.h>
+#include <utils/Errors.h>
#include <utils/String8.h>
// periodic_chores_interval_fast, periodic_chores_interval_slow: intervals at
@@ -61,9 +62,16 @@
android::String8 batteryTemperaturePath;
android::String8 batteryTechnologyPath;
android::String8 batteryCurrentNowPath;
+ android::String8 batteryCurrentAvgPath;
android::String8 batteryChargeCounterPath;
};
+// Global helper functions
+
+void healthd_battery_update();
+android::status_t healthd_get_property(int id,
+ struct android::BatteryProperty *val);
+
// The following are implemented in libhealthd_board to handle board-specific
// behavior.
//
diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h
index 053bfaf..fa8f03f 100644
--- a/include/utils/LruCache.h
+++ b/include/utils/LruCache.h
@@ -17,8 +17,8 @@
#ifndef ANDROID_UTILS_LRU_CACHE_H
#define ANDROID_UTILS_LRU_CACHE_H
+#include <UniquePtr.h>
#include <utils/BasicHashtable.h>
-#include <utils/UniquePtr.h>
namespace android {
diff --git a/init/property_service.c b/init/property_service.c
index c370769..1b9327c 100644
--- a/init/property_service.c
+++ b/init/property_service.c
@@ -281,7 +281,6 @@
static bool is_legal_property_name(const char* name, size_t namelen)
{
size_t i;
- bool previous_was_dot = false;
if (namelen >= PROP_NAME_MAX) return false;
if (namelen < 1) return false;
if (name[0] == '.') return false;
@@ -291,11 +290,10 @@
/* Don't allow ".." to appear in a property name */
for (i = 0; i < namelen; i++) {
if (name[i] == '.') {
- if (previous_was_dot == true) return false;
- previous_was_dot = true;
+ // i=0 is guaranteed to never have a dot. See above.
+ if (name[i-1] == '.') return false;
continue;
}
- previous_was_dot = false;
if (name[i] == '_' || name[i] == '-') continue;
if (name[i] >= 'a' && name[i] <= 'z') continue;
if (name[i] >= 'A' && name[i] <= 'Z') continue;
diff --git a/lmkd/Android.mk b/lmkd/Android.mk
new file mode 100644
index 0000000..5d6d1d2
--- /dev/null
+++ b/lmkd/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := lmkd.c
+LOCAL_STATIC_LIBRARIES := libcutils liblog libm libc
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+
+LOCAL_MODULE := lmkd
+
+include $(BUILD_EXECUTABLE)
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
new file mode 100644
index 0000000..376410b
--- /dev/null
+++ b/lmkd/lmkd.c
@@ -0,0 +1,735 @@
+/*
+ * Copyright (C) 2013 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 "lowmemorykiller"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <cutils/log.h>
+#include <cutils/sockets.h>
+
+#define MEMCG_SYSFS_PATH "/dev/memcg/"
+#define MEMPRESSURE_WATCH_LEVEL "medium"
+#define ZONEINFO_PATH "/proc/zoneinfo"
+#define LINE_MAX 128
+
+#define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
+#define INKERNEL_ADJ_PATH "/sys/module/lowmemorykiller/parameters/adj"
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
+
+enum lmk_cmd {
+ LMK_TARGET,
+ LMK_PROCPRIO,
+ LMK_PROCREMOVE,
+};
+
+#define MAX_TARGETS 6
+/*
+ * longest is LMK_TARGET followed by MAX_TARGETS each minfree and minkillprio
+ * values
+ */
+#define CTRL_PACKET_MAX (sizeof(int) * (MAX_TARGETS * 2 + 1))
+
+/* default to old in-kernel interface if no memory pressure events */
+static int use_inkernel_interface = 1;
+
+/* memory pressure level medium event */
+static int mpevfd;
+
+/* control socket listen and data */
+static int ctrl_lfd;
+static int ctrl_dfd = -1;
+static int ctrl_dfd_reopened; /* did we reopen ctrl conn on this loop? */
+
+/* 1 memory pressure level, 1 ctrl listen socket, 1 ctrl data socket */
+#define MAX_EPOLL_EVENTS 3
+static int epollfd;
+static int maxevents;
+
+#define OOM_DISABLE (-17)
+/* inclusive */
+#define OOM_ADJUST_MIN (-16)
+#define OOM_ADJUST_MAX 15
+
+static int lowmem_adj[MAX_TARGETS];
+static int lowmem_minfree[MAX_TARGETS];
+static int lowmem_targets_size;
+
+struct sysmeminfo {
+ int nr_free_pages;
+ int nr_file_pages;
+ int nr_shmem;
+ int totalreserve_pages;
+};
+
+struct adjslot_list {
+ struct adjslot_list *next;
+ struct adjslot_list *prev;
+};
+
+struct proc {
+ struct adjslot_list asl;
+ int pid;
+ int oomadj;
+ struct proc *pidhash_next;
+};
+
+#define PIDHASH_SZ 1024
+static struct proc *pidhash[PIDHASH_SZ];
+#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
+
+#define ADJTOSLOT(adj) (adj + -OOM_ADJUST_MIN)
+static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_ADJUST_MAX) + 1];
+
+/*
+ * Wait 1-2 seconds for the death report of a killed process prior to
+ * considering killing more processes.
+ */
+#define KILL_TIMEOUT 2
+/* Time of last process kill we initiated, stop me before I kill again */
+static time_t kill_lasttime;
+
+/* PAGE_SIZE / 1024 */
+static long page_k;
+
+static struct proc *pid_lookup(int pid) {
+ struct proc *procp;
+
+ for (procp = pidhash[pid_hashfn(pid)]; procp && procp->pid != pid;
+ procp = procp->pidhash_next)
+ ;
+
+ return procp;
+}
+
+static void adjslot_insert(struct adjslot_list *head, struct adjslot_list *new)
+{
+ struct adjslot_list *next = head->next;
+ new->prev = head;
+ new->next = next;
+ next->prev = new;
+ head->next = new;
+}
+
+static void adjslot_remove(struct adjslot_list *old)
+{
+ struct adjslot_list *prev = old->prev;
+ struct adjslot_list *next = old->next;
+ next->prev = prev;
+ prev->next = next;
+}
+
+static struct adjslot_list *adjslot_tail(struct adjslot_list *head) {
+ struct adjslot_list *asl = head->prev;
+
+ return asl == head ? NULL : asl;
+}
+
+static void proc_slot(struct proc *procp) {
+ int adjslot = ADJTOSLOT(procp->oomadj);
+
+ adjslot_insert(&procadjslot_list[adjslot], &procp->asl);
+}
+
+static void proc_unslot(struct proc *procp) {
+ adjslot_remove(&procp->asl);
+}
+
+static void proc_insert(struct proc *procp) {
+ int hval = pid_hashfn(procp->pid);
+
+ procp->pidhash_next = pidhash[hval];
+ pidhash[hval] = procp;
+ proc_slot(procp);
+}
+
+static int pid_remove(int pid) {
+ int hval = pid_hashfn(pid);
+ struct proc *procp;
+ struct proc *prevp;
+
+ for (procp = pidhash[hval], prevp = NULL; procp && procp->pid != pid;
+ procp = procp->pidhash_next)
+ prevp = procp;
+
+ if (!procp)
+ return -1;
+
+ if (!prevp)
+ pidhash[hval] = procp->pidhash_next;
+ else
+ prevp->pidhash_next = procp->pidhash_next;
+
+ proc_unslot(procp);
+ free(procp);
+ return 0;
+}
+
+static void writefilestring(char *path, char *s) {
+ int fd = open(path, O_WRONLY);
+ int len = strlen(s);
+ int ret;
+
+ if (fd < 0) {
+ ALOGE("Error opening %s; errno=%d", path, errno);
+ return;
+ }
+
+ ret = write(fd, s, len);
+ if (ret < 0) {
+ ALOGE("Error writing %s; errno=%d", path, errno);
+ } else if (ret < len) {
+ ALOGE("Short write on %s; length=%d", path, ret);
+ }
+
+ close(fd);
+}
+
+static void cmd_procprio(int pid, int oomadj) {
+ struct proc *procp;
+ char path[80];
+ char val[20];
+
+ if (oomadj < OOM_DISABLE || oomadj > OOM_ADJUST_MAX) {
+ ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj);
+ return;
+ }
+
+ snprintf(path, sizeof(path), "/proc/%d/oom_adj", pid);
+ snprintf(val, sizeof(val), "%d", oomadj);
+ writefilestring(path, val);
+
+ if (use_inkernel_interface)
+ return;
+
+ procp = pid_lookup(pid);
+ if (!procp) {
+ procp = malloc(sizeof(struct proc));
+ if (!procp) {
+ // Oh, the irony. May need to rebuild our state.
+ return;
+ }
+
+ procp->pid = pid;
+ procp->oomadj = oomadj;
+ proc_insert(procp);
+ } else {
+ proc_unslot(procp);
+ procp->oomadj = oomadj;
+ proc_slot(procp);
+ }
+}
+
+static void cmd_procremove(int pid) {
+ struct proc *procp;
+
+ if (use_inkernel_interface)
+ return;
+
+ pid_remove(pid);
+ kill_lasttime = 0;
+}
+
+static void cmd_target(int ntargets, int *params) {
+ int i;
+
+ if (ntargets > (int)ARRAY_SIZE(lowmem_adj))
+ return;
+
+ for (i = 0; i < ntargets; i++) {
+ lowmem_minfree[i] = ntohl(*params++);
+ lowmem_adj[i] = ntohl(*params++);
+ }
+
+ lowmem_targets_size = ntargets;
+
+ if (use_inkernel_interface) {
+ char minfreestr[128];
+ char killpriostr[128];
+
+ minfreestr[0] = '\0';
+ killpriostr[0] = '\0';
+
+ for (i = 0; i < lowmem_targets_size; i++) {
+ char val[40];
+
+ if (i) {
+ strlcat(minfreestr, ",", sizeof(minfreestr));
+ strlcat(killpriostr, ",", sizeof(killpriostr));
+ }
+
+ snprintf(val, sizeof(val), "%d", lowmem_minfree[i]);
+ strlcat(minfreestr, val, sizeof(minfreestr));
+ snprintf(val, sizeof(val), "%d", lowmem_adj[i]);
+ strlcat(killpriostr, val, sizeof(killpriostr));
+ }
+
+ writefilestring(INKERNEL_MINFREE_PATH, minfreestr);
+ writefilestring(INKERNEL_ADJ_PATH, killpriostr);
+ }
+}
+
+static void ctrl_data_close(void) {
+ ALOGI("Closing Activity Manager data connection");
+ close(ctrl_dfd);
+ ctrl_dfd = -1;
+ maxevents--;
+}
+
+static int ctrl_data_read(char *buf, size_t bufsz) {
+ int ret = 0;
+
+ ret = read(ctrl_dfd, buf, bufsz);
+
+ if (ret == -1) {
+ ALOGE("control data socket read failed; errno=%d", errno);
+ } else if (ret == 0) {
+ ALOGE("Got EOF on control data socket");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static void ctrl_command_handler(void) {
+ int ibuf[CTRL_PACKET_MAX / sizeof(int)];
+ int len;
+ int cmd = -1;
+ int nargs;
+ int targets;
+
+ len = ctrl_data_read((char *)ibuf, CTRL_PACKET_MAX);
+ if (len <= 0)
+ return;
+
+ nargs = len / sizeof(int) - 1;
+ if (nargs < 0)
+ goto wronglen;
+
+ cmd = ntohl(ibuf[0]);
+
+ switch(cmd) {
+ case LMK_TARGET:
+ targets = nargs / 2;
+ if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj))
+ goto wronglen;
+ cmd_target(targets, &ibuf[1]);
+ break;
+ case LMK_PROCPRIO:
+ if (nargs != 2)
+ goto wronglen;
+ cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]));
+ break;
+ case LMK_PROCREMOVE:
+ if (nargs != 1)
+ goto wronglen;
+ cmd_procremove(ntohl(ibuf[1]));
+ break;
+ default:
+ ALOGE("Received unknown command code %d", cmd);
+ return;
+ }
+
+ return;
+
+wronglen:
+ ALOGE("Wrong control socket read length cmd=%d len=%d", cmd, len);
+}
+
+static void ctrl_data_handler(uint32_t events) {
+ if (events & EPOLLHUP) {
+ ALOGI("ActivityManager disconnected");
+ if (!ctrl_dfd_reopened)
+ ctrl_data_close();
+ } else if (events & EPOLLIN) {
+ ctrl_command_handler();
+ }
+}
+
+static void ctrl_connect_handler(uint32_t events) {
+ struct sockaddr addr;
+ socklen_t alen;
+ struct epoll_event epev;
+
+ if (ctrl_dfd >= 0) {
+ ctrl_data_close();
+ ctrl_dfd_reopened = 1;
+ }
+
+ alen = sizeof(addr);
+ ctrl_dfd = accept(ctrl_lfd, &addr, &alen);
+
+ if (ctrl_dfd < 0) {
+ ALOGE("lmkd control socket accept failed; errno=%d", errno);
+ return;
+ }
+
+ ALOGI("ActivityManager connected");
+ maxevents++;
+ epev.events = EPOLLIN;
+ epev.data.ptr = (void *)ctrl_data_handler;
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_dfd, &epev) == -1) {
+ ALOGE("epoll_ctl for data connection socket failed; errno=%d", errno);
+ ctrl_data_close();
+ return;
+ }
+}
+
+static int zoneinfo_parse_protection(char *cp) {
+ int max = 0;
+ int zoneval;
+
+ if (*cp++ != '(')
+ return 0;
+
+ do {
+ zoneval = strtol(cp, &cp, 0);
+ if ((*cp != ',') && (*cp != ')'))
+ return 0;
+ if (zoneval > max)
+ max = zoneval;
+ } while (cp = strtok(NULL, " "));
+
+ return max;
+}
+
+static void zoneinfo_parse_line(char *line, struct sysmeminfo *mip) {
+ char *cp = line;
+ char *ap;
+
+ cp = strtok(line, " ");
+ if (!cp)
+ return;
+
+ ap = strtok(NULL, " ");
+ if (!ap)
+ return;
+
+ if (!strcmp(cp, "nr_free_pages"))
+ mip->nr_free_pages += strtol(ap, NULL, 0);
+ else if (!strcmp(cp, "nr_file_pages"))
+ mip->nr_file_pages += strtol(ap, NULL, 0);
+ else if (!strcmp(cp, "nr_shmem"))
+ mip->nr_shmem += strtol(ap, NULL, 0);
+ else if (!strcmp(cp, "high"))
+ mip->totalreserve_pages += strtol(ap, NULL, 0);
+ else if (!strcmp(cp, "protection:"))
+ mip->totalreserve_pages += zoneinfo_parse_protection(ap);
+}
+
+static int zoneinfo_parse(struct sysmeminfo *mip) {
+ FILE *f;
+ char *cp;
+ char line[LINE_MAX];
+
+ memset(mip, 0, sizeof(struct sysmeminfo));
+ f = fopen(ZONEINFO_PATH, "r");
+ if (!f) {
+ ALOGE("%s open: errno=%d", ZONEINFO_PATH, errno);
+ return -1;
+ }
+
+ while (fgets(line, LINE_MAX, f))
+ zoneinfo_parse_line(line, mip);
+
+ fclose(f);
+ return 0;
+}
+
+static int proc_get_size(int pid) {
+ char path[PATH_MAX];
+ char line[LINE_MAX];
+ FILE *f;
+ int rss = 0;
+ int total;
+
+ snprintf(path, PATH_MAX, "/proc/%d/statm", pid);
+ f = fopen(path, "r");
+ if (!f)
+ return -1;
+ if (!fgets(line, LINE_MAX, f)) {
+ fclose(f);
+ return -1;
+ }
+
+ sscanf(line, "%d %d ", &total, &rss);
+ fclose(f);
+ return rss;
+}
+
+static char *proc_get_name(int pid) {
+ char path[PATH_MAX];
+ static char line[LINE_MAX];
+ FILE *f;
+ char *cp;
+
+ snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid);
+ f = fopen(path, "r");
+ if (!f)
+ return NULL;
+ if (!fgets(line, LINE_MAX, f)) {
+ fclose(f);
+ return NULL;
+ }
+
+ cp = strchr(line, ' ');
+ if (cp)
+ *cp = '\0';
+
+ return line;
+}
+
+static struct proc *proc_adj_lru(int oomadj) {
+ return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
+}
+
+static void mp_event(uint32_t events) {
+ int i;
+ int ret;
+ unsigned long long evcount;
+ struct sysmeminfo mi;
+ int other_free;
+ int other_file;
+ int minfree = 0;
+ int min_score_adj = OOM_ADJUST_MAX + 1;
+
+ ret = read(mpevfd, &evcount, sizeof(evcount));
+ if (ret < 0)
+ ALOGE("Error reading memory pressure event fd; errno=%d",
+ errno);
+
+ if (time(NULL) - kill_lasttime < KILL_TIMEOUT)
+ return;
+
+ if (zoneinfo_parse(&mi) < 0)
+ return;
+
+ other_free = mi.nr_free_pages - mi.totalreserve_pages;
+ other_file = mi.nr_file_pages - mi.nr_shmem;
+
+ for (i = 0; i < lowmem_targets_size; i++) {
+ minfree = lowmem_minfree[i];
+ if (other_free < minfree && other_file < minfree) {
+ min_score_adj = lowmem_adj[i];
+ break;
+ }
+ }
+
+ if (min_score_adj == OOM_ADJUST_MAX + 1)
+ return;
+
+ for (i = OOM_ADJUST_MAX; i >= min_score_adj; i--) {
+ struct proc *procp;
+
+ retry:
+ procp = proc_adj_lru(i);
+
+ if (procp) {
+ int pid = procp->pid;
+ char *taskname;
+ int tasksize;
+ int r;
+
+ taskname = proc_get_name(pid);
+ if (!taskname) {
+ pid_remove(pid);
+ goto retry;
+ }
+
+ tasksize = proc_get_size(pid);
+ if (tasksize < 0) {
+ pid_remove(pid);
+ goto retry;
+ }
+
+ ALOGI("Killing '%s' (%d), adj %d\n"
+ " to free %ldkB because cache %ldkB is below limit %ldkB for oom_adj %d\n"
+ " Free memory is %ldkB %s reserved",
+ taskname, pid, procp->oomadj, tasksize * page_k,
+ other_file * page_k, minfree * page_k, min_score_adj,
+ other_free * page_k, other_free >= 0 ? "above" : "below");
+ r = kill(pid, SIGKILL);
+ pid_remove(pid);
+
+ if (r) {
+ ALOGE("kill(%d): errno=%d", procp->pid, errno);
+ goto retry;
+ } else {
+ time(&kill_lasttime);
+ break;
+ }
+ }
+ }
+}
+
+static int init_mp(char *levelstr, void *event_handler)
+{
+ int mpfd;
+ int evfd;
+ int evctlfd;
+ char buf[256];
+ struct epoll_event epev;
+ int ret;
+
+ mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY);
+ if (mpfd < 0) {
+ ALOGI("No kernel memory.pressure_level support (errno=%d)", errno);
+ goto err_open_mpfd;
+ }
+
+ evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY);
+ if (evctlfd < 0) {
+ ALOGI("No kernel memory cgroup event control (errno=%d)", errno);
+ goto err_open_evctlfd;
+ }
+
+ evfd = eventfd(0, EFD_NONBLOCK);
+ if (evfd < 0) {
+ ALOGE("eventfd failed for level %s; errno=%d", levelstr, errno);
+ goto err_eventfd;
+ }
+
+ ret = snprintf(buf, sizeof(buf), "%d %d %s", evfd, mpfd, levelstr);
+ if (ret >= (ssize_t)sizeof(buf)) {
+ ALOGE("cgroup.event_control line overflow for level %s", levelstr);
+ goto err;
+ }
+
+ ret = write(evctlfd, buf, strlen(buf) + 1);
+ if (ret == -1) {
+ ALOGE("cgroup.event_control write failed for level %s; errno=%d",
+ levelstr, errno);
+ goto err;
+ }
+
+ epev.events = EPOLLIN;
+ epev.data.ptr = event_handler;
+ ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, evfd, &epev);
+ if (ret == -1) {
+ ALOGE("epoll_ctl for level %s failed; errno=%d", levelstr, errno);
+ goto err;
+ }
+ maxevents++;
+ mpevfd = evfd;
+ return 0;
+
+err:
+ close(evfd);
+err_eventfd:
+ close(evctlfd);
+err_open_evctlfd:
+ close(mpfd);
+err_open_mpfd:
+ return -1;
+}
+
+static int init(void) {
+ struct epoll_event epev;
+ int i;
+ int ret;
+
+ page_k = sysconf(_SC_PAGESIZE);
+ if (page_k == -1)
+ page_k = PAGE_SIZE;
+ page_k /= 1024;
+
+ epollfd = epoll_create(MAX_EPOLL_EVENTS);
+ if (epollfd == -1) {
+ ALOGE("epoll_create failed (errno=%d)", errno);
+ return -1;
+ }
+
+ ctrl_lfd = android_get_control_socket("lmkd");
+ if (ctrl_lfd < 0) {
+ ALOGE("get lmkd control socket failed");
+ return -1;
+ }
+
+ ret = listen(ctrl_lfd, 1);
+ if (ret < 0) {
+ ALOGE("lmkd control socket listen failed (errno=%d)", errno);
+ return -1;
+ }
+
+ epev.events = EPOLLIN;
+ epev.data.ptr = (void *)ctrl_connect_handler;
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_lfd, &epev) == -1) {
+ ALOGE("epoll_ctl for lmkd control socket failed (errno=%d)", errno);
+ return -1;
+ }
+ maxevents++;
+
+ use_inkernel_interface = !access(INKERNEL_MINFREE_PATH, W_OK);
+
+ if (use_inkernel_interface) {
+ ALOGI("Using in-kernel low memory killer interface");
+ } else {
+ ret = init_mp(MEMPRESSURE_WATCH_LEVEL, (void *)&mp_event);
+ if (ret)
+ ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
+ }
+
+ for (i = 0; i <= ADJTOSLOT(OOM_ADJUST_MAX); i++) {
+ procadjslot_list[i].next = &procadjslot_list[i];
+ procadjslot_list[i].prev = &procadjslot_list[i];
+ }
+
+ return 0;
+}
+
+static void mainloop(void) {
+ while (1) {
+ struct epoll_event events[maxevents];
+ int nevents;
+ int i;
+
+ ctrl_dfd_reopened = 0;
+ nevents = epoll_wait(epollfd, events, maxevents, -1);
+
+ if (nevents == -1) {
+ if (errno == EINTR)
+ continue;
+ ALOGE("epoll_wait failed (errno=%d)", errno);
+ continue;
+ }
+
+ for (i = 0; i < nevents; ++i) {
+ if (events[i].events & EPOLLERR)
+ ALOGD("EPOLLERR on event #%d", i);
+ if (events[i].data.ptr)
+ (*(void (*)(uint32_t))events[i].data.ptr)(events[i].events);
+ }
+ }
+}
+
+int main(int argc, char **argv) {
+ if (!init())
+ mainloop();
+
+ ALOGI("exiting");
+ return 0;
+}
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 3a1b281..a325692 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -90,10 +90,6 @@
# Logged when the supplicant switches to a new state
50023 wifi_supplicant_state_changed (supplicant_state|1|5)
-# Do not change these names without updating tag in:
-#//device/dalvik/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.c
-51000 socket_stats (send|1|2),(recv|1|2),(ip|1|5),(port|1|5),(close|1|5)
-
# Database operation samples.
# db: the filename of the database
# sql: the executed query (without query args)
@@ -136,7 +132,7 @@
80310 bionic_event_resolver_wrong_query (uid|1)
# libcore failure logging
-90100 cert_pin_failure (certs|4)
+90100 exp_det_cert_pin_failure (certs|4)
# NOTE - the range 1000000-2000000 is reserved for partners and others who
# want to define their own log tags without conflicting with the core platform.
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 3f7c89f..aa4c975 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -83,6 +83,10 @@
mkdir /mnt/obb 0700 root system
mount tmpfs tmpfs /mnt/obb mode=0755,gid=1000
+ # memory control cgroup
+ mkdir /dev/memcg 0700 root system
+ mount cgroup none /dev/memcg memory
+
write /proc/sys/kernel/panic_on_oops 1
write /proc/sys/kernel/hung_task_timeout_secs 0
write /proc/cpu/alignment 4
@@ -433,6 +437,11 @@
on property:ro.kernel.qemu=1
start adbd
+service lmkd /system/bin/lmkd
+ class core
+ critical
+ socket lmkd seqpacket 0660 system system
+
service servicemanager /system/bin/servicemanager
class core
user system
@@ -442,6 +451,7 @@
onrestart restart zygote
onrestart restart media
onrestart restart surfaceflinger
+ onrestart restart inputflinger
onrestart restart drm
service vold /system/bin/vold
@@ -471,6 +481,12 @@
group graphics drmrpc
onrestart restart zygote
+service inputflinger /system/bin/inputflinger
+ class main
+ user system
+ group input
+ onrestart restart zygote
+
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system