diff --git a/Android.mk b/Android.mk
index 1ae53b25b6e63e6b57495c7b409f05670d9ab279..ba3cab01977b0f508fac1de3066d217cbb10de10 100644
--- a/Android.mk
+++ b/Android.mk
@@ -47,3 +47,5 @@ $(DX):
 $(AAPT):
 	mkdir -p $(@D)
 	ln -sf $(FAKETOOLS_AAPT) $@
+
+include $(LOCAL_PATH)/updater/Android.mk
diff --git a/b2g_product.mk b/b2g_product.mk
index dd76bca59336721cb33aa393a2b6c7820ce94d6a..bce99c260bc2c8753410bbe74cacde9b967b5e8d 100644
--- a/b2g_product.mk
+++ b/b2g_product.mk
@@ -28,6 +28,7 @@
 include gonk-misc/b2g.mk
 TARGET_PROVIDES_INIT_RC :=
 PRODUCT_PACKAGES := $(filter-out fakeperm rilproxy,$(PRODUCT_PACKAGES))
+PRODUCT_PACKAGES += librecovery_updater_qcom
 
 PRODUCT_PROPERTY_OVERRIDES += \
   ro.moz.ril.emergency_by_default=true
diff --git a/patch/ics_strawberry/bootable/bootloader/lk/ota_fotacookie_withECC.patch b/patch/ics_strawberry/bootable/bootloader/lk/ota_fotacookie_withECC.patch
new file mode 100644
index 0000000000000000000000000000000000000000..1977c473a43be11d31b2cae2a78febc5a3feaa26
--- /dev/null
+++ b/patch/ics_strawberry/bootable/bootloader/lk/ota_fotacookie_withECC.patch
@@ -0,0 +1,34 @@
+From ddd6a1ad574c18e33804be4122ea08ab6717eae6 Mon Sep 17 00:00:00 2001
+From: Vasanthakumar Pandurangan <vasanth@codeaurora.org>
+Date: Tue, 23 Oct 2012 12:16:53 +0530
+Subject: [PATCH] For Modem ota update in Nand devices, bootloader should
+ write a magic cookie in FOTA partition which it already does
+ but without ECC. This change is to do the same with ECC
+
+Change-Id: Iadaf6fd2c5197abc618aa249b88b2c4529257eee
+Signed-off-by: Vasanthakumar Pandurangan <vasanth@codeaurora.org>
+---
+ platform/msm_shared/nand.c |    5 -----
+ 1 files changed, 0 insertions(+), 5 deletions(-)
+
+diff --git a/platform/msm_shared/nand.c b/platform/msm_shared/nand.c
+index 08b68dd..b59df28 100644
+--- a/platform/msm_shared/nand.c
++++ b/platform/msm_shared/nand.c
+@@ -159,13 +159,8 @@ static struct flash_identification supported_flash[] = {
+ 
+ static void set_nand_configuration(char type)
+ {
+-	if (type == TYPE_MODEM_PARTITION) {
+-		CFG0 = CFG0_M;
+-		CFG1 = CFG1_M;
+-	} else {
+ 		CFG0 = CFG0_A;
+ 		CFG1 = CFG1_A;
+-	}
+ }
+ 
+ static void flash_nand_read_id(dmov_s * cmdlist, unsigned *ptrlist)
+-- 
+1.7.8.3
+
diff --git a/patch/ics_strawberry/bootable/recovery/handle_radio_update.patch b/patch/ics_strawberry/bootable/recovery/handle_radio_update.patch
new file mode 100644
index 0000000000000000000000000000000000000000..a93ebf34d4777bb17c527ccb155a4d3d8495cc3c
--- /dev/null
+++ b/patch/ics_strawberry/bootable/recovery/handle_radio_update.patch
@@ -0,0 +1,109 @@
+diff --git a/recovery.c b/recovery.c
+index cddcbd6..e261af7 100644
+--- a/recovery.c
++++ b/recovery.c
+@@ -1,6 +1,6 @@
+ /*
+  * Copyright (C) 2007 The Android Open Source Project
+- *
++ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+  * 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
+@@ -58,6 +58,7 @@ static const struct option OPTIONS[] = {
+   { "wipe_data", no_argument, NULL, 'w' },
+   { "wipe_cache", no_argument, NULL, 'c' },
+   { "show_text", no_argument, NULL, 't' },
++  { "radio_status", no_argument, NULL, 'r' },
+   { NULL, 0, NULL, 0 },
+ };
+ 
+@@ -67,6 +68,7 @@ static const char *LOG_FILE = "/cache/recovery/log";
+ static const char *LAST_LOG_FILE = "/cache/recovery/last_log";
+ static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install";
+ static const char *CACHE_ROOT = "/cache";
++static const char *RADIO_DIR = "/sdcard/radio";
+ static const char *SDCARD_ROOT = "/sdcard";
+ static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
+ static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";
+@@ -592,9 +594,12 @@ update_directory(const char* path, const char* unmount_when_done,
+             ui_print("\n-- Install %s ...\n", path);
+             set_sdcard_update_bootloader_message();
+             char* copy = copy_sideloaded_package(new_path);
+-            if (unmount_when_done != NULL) {
+-                ensure_path_unmounted(unmount_when_done);
++            // create radio folder for QCOM radio image update
++            if((mkdir(RADIO_DIR,777) != 0) && (errno != EEXIST)) {  // In this case EEXIST is not a valid error
++                result = INSTALL_ERROR;
++                break;
+             }
++
+             if (copy) {
+                 result = install_package(copy, wipe_cache, TEMPORARY_INSTALL_FILE);
+                 free(copy);
+@@ -1326,7 +1331,7 @@ main(int argc, char **argv) {
+     const char *send_intent = NULL;
+     const char *update_package = NULL;
+     int wipe_data = 0, wipe_cache = 0;
+-
++    int radio_status_present = 0;
+     //check delta update first
+     handle_deltaupdate_status();
+ 
+@@ -1339,6 +1344,7 @@ main(int argc, char **argv) {
+         case 'w': wipe_data = wipe_cache = 1; break;
+         case 'c': wipe_cache = 1; break;
+         case 't': ui_show_text(1); break;
++        case 'r': radio_status_present = 1; break;
+         case '?':
+             LOGE("Invalid command argument\n");
+             continue;
+@@ -1375,13 +1381,25 @@ main(int argc, char **argv) {
+     int status = INSTALL_SUCCESS;
+ 
+     if (update_package != NULL) {
+-        status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE);
+-        if (status == INSTALL_SUCCESS && wipe_cache) {
+-            if (erase_volume("/cache")) {
+-                LOGE("Cache wipe (requested by package) failed.");
++        if(ensure_path_mounted(SDCARD_ROOT) != 0) {
++            ui_print("ensure_path_mounted failed.\n");
++            status = INSTALL_ERROR;
++        } else if((mkdir(RADIO_DIR,777) != 0) && (errno != EEXIST)) {
++            //In this case EEXIST is not a valid error
++            ui_print("creating radio directory failed. errno = %d\n", errno);
++            status = INSTALL_ERROR;
++        } else {
++            finish_recovery(NULL);
++            status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE);
++            if (status == INSTALL_SUCCESS && wipe_cache) {
++                if (erase_volume("/cache")) {
++                    LOGE("Cache wipe (requested by package) failed.");
++                    status = INSTALL_ERROR;
++                }
+             }
++            if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");
++            ensure_path_unmounted(SDCARD_ROOT);
+         }
+-        if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");
+     } else if (wipe_data) {
+         if (device_wipe_data()) status = INSTALL_ERROR;
+         if (erase_volume("/data")) status = INSTALL_ERROR;
+@@ -1390,6 +1408,9 @@ main(int argc, char **argv) {
+     } else if (wipe_cache) {
+         if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
+         if (status != INSTALL_SUCCESS) ui_print("Cache wipe failed.\n");
++    } else if (radio_status_present) {
++        //irrespective of radio_status value, request for a reboot
++        goto recovery_end;
+     } else {
+         status = INSTALL_ERROR;  // No command specified
+     }
+@@ -1399,6 +1420,7 @@ main(int argc, char **argv) {
+         prompt_and_wait();
+     }
+ 
++recovery_end:
+     // Otherwise, get ready to boot the main system...
+     finish_recovery(send_intent);
+     ui_print("Rebooting...\n");
diff --git a/patch/ics_strawberry/build/include_radio_image.patch b/patch/ics_strawberry/build/include_radio_image.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b5ab986e8599442d7b6e09755ac7b99cf7188959
--- /dev/null
+++ b/patch/ics_strawberry/build/include_radio_image.patch
@@ -0,0 +1,47 @@
+diff --git a/core/Makefile b/core/Makefile
+index 48275cf..eb20e75 100644
+--- a/core/Makefile
++++ b/core/Makefile
+@@ -1273,6 +1273,7 @@ name := $(name)-target_files-$(FILE_NAME_TAG)
+ 
+ intermediates := $(call intermediates-dir-for,PACKAGING,target_files)
+ BUILT_TARGET_FILES_PACKAGE := $(intermediates)/$(name).zip
++BUILT_TARGET_RADIO_PACKAGE := $(intermediates)/$(name)/RADIO
+ $(BUILT_TARGET_FILES_PACKAGE): intermediates := $(intermediates)
+ $(BUILT_TARGET_FILES_PACKAGE): \
+ 		zip_root := $(intermediates)/$(name)
+@@ -1440,6 +1441,7 @@ ifdef BOARD_KERNEL_2KPAGESIZE
+ endif
+ endif
+ 	$(hide) $(foreach t,$(INSTALLED_RADIOIMAGE_TARGET),\
++				echo "INSTALLED_RADIOIMAGE_TARGET $(t)"; \
+ 	            mkdir -p $(zip_root)/RADIO; \
+ 	            $(ACP) $(t) $(zip_root)/RADIO/$(notdir $(t));)
+ 	@# Contents of the system image
+@@ -1523,7 +1525,7 @@ INTERNAL_OTA_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
+ 
+ $(INTERNAL_OTA_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)
+ 
+-$(INTERNAL_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(OTATOOLS)
++$(INTERNAL_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(OTATOOLS) $(INSTALLED_RADIOIMAGE_TARGET)
+ 	@echo "Package OTA: $@"
+ 	$(hide) ./build/tools/releasetools/ota_from_target_files -v \
+ 	   -p $(HOST_OUT) \
+@@ -1535,7 +1537,7 @@ INTERNAL_2KNAND_OTA_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name)_2knand.zip
+ 
+ $(INTERNAL_2KNAND_OTA_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)
+ 
+-$(INTERNAL_2KNAND_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(OTATOOLS)
++$(INTERNAL_2KNAND_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(OTATOOLS) $(INSTALLED_RADIOIMAGE_TARGET)
+ 	@echo "Package OTA: $@"
+ 	$(hide) ./build/tools/releasetools/ota_from_target_files -v \
+ 	   -p $(HOST_OUT) \
+@@ -1552,7 +1554,7 @@ endif
+ 
+ $(INTERNAL_MMC_OTA_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)
+ 
+-$(INTERNAL_MMC_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(OTATOOLS)
++$(INTERNAL_MMC_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(OTATOOLS) $(INSTALLED_RADIOIMAGE_TARGET)
+ 	@echo "Package OTA: $@"
+ 	$(hide) ./build/tools/releasetools/ota_from_target_files -v \
+ 	   -p $(HOST_OUT) \
diff --git a/patch/ics_strawberry/device/qcom/common/device_specific_fw_update.patch b/patch/ics_strawberry/device/qcom/common/device_specific_fw_update.patch
new file mode 100644
index 0000000000000000000000000000000000000000..90dcab2fbb5cb67696ee0df4d402daee4491333b
--- /dev/null
+++ b/patch/ics_strawberry/device/qcom/common/device_specific_fw_update.patch
@@ -0,0 +1,163 @@
+diff --git a/common.mk b/common.mk
+index 2f3622f..8080d70 100755
+--- a/common.mk
++++ b/common.mk
+@@ -360,6 +360,8 @@ TSLIB_EXTERNAL += ts
+ QRGND := qrngd
+ QRGND += qrngtest
+ 
++#UPDATER
++UPDATER := librecovery_ui_qcom
+ #WPA
+ WPA := wpa_supplicant.conf
+ 
+diff --git a/releasetools.py b/releasetools.py
+index 45d0953..f6210b1 100755
+--- a/releasetools.py
++++ b/releasetools.py
+@@ -19,22 +18,6 @@
+ import common
+ import re
+ 
+-def LoadFilesMap(zip, type):
+-  try:
+-    data = zip.read("RADIO/filesmap")
+-  except KeyError:
+-    print "Warning: could not find RADIO/filesmap in %s." % zip
+-    data = ""
+-  d = {}
+-  for line in data.split("\n"):
+-    line = line.strip()
+-    if not line or line.startswith("#"): continue
+-    pieces = line.split()
+-    if not (len(pieces) == 2):
+-      raise ValueError("malformed filesmap line: \"%s\"" % (line,))
+-    d[pieces[0]] = pieces[1]
+-  return d
+-
+ def GetRadioFiles(z):
+   out = {}
+   for info in z.infolist():
+@@ -44,51 +27,93 @@ def GetRadioFiles(z):
+   return out
+ 
+ def FullOTA_Assertions(info):
+-  #TODO: Implement device specific asserstions.
+-  return
++  AddBootloaderAssertion(info, info.input_zip)
++
+ 
+ def IncrementalOTA_Assertions(info):
+-  #TODO: Implement device specific asserstions.
+-  return
++  AddBootloaderAssertion(info, info.target_zip)
++
++
++def AddBootloaderAssertion(info, input_zip):
++  android_info = input_zip.read("OTA/android-info.txt")
++  m = re.search(r"require\s+version-bootloader\s*=\s*(\S+)", android_info)
++  if m:
++    bootloaders = m.group(1).split("|")
++    if "*" not in bootloaders:
++      info.script.AssertSomeBootloader(*bootloaders)
++    info.metadata["pre-bootloader"] = m.group(1)
++
++def CheckRadiotarget(info, mount_point):
++    fstab = info.script.info.get("fstab", None)
++    if fstab:
++      p = fstab[mount_point]
++      info.script.AppendExtra('assert(qcom.set_radio("%s"));' %
++                         (p.fs_type))
++
++# map qcom partitions with filenames
++QCOM_MOUNT_MAP = { "NON-HLOS.bin": "modem",
++                   "rpm.mbn": "rpm",
++                   "sbl1.mbn": "sbl1",
++                   "sbl2.mbn": "sbl2",
++                   "sbl3.mbn": "sbl3",
++                   "tz.mbn": "tz",
++                   "emmc_appsboot.mbn": "aboot",
++                   "radio.img": "radio"}
++
++
++def InstallRadio(radio_img, api_version, input_zip, fn, info):
++  fn2 = fn[6:]
++  fn3 = "/sdcard/radio/" + fn2
++  common.ZipWriteStr(info.output_zip, fn2, radio_img)
+ 
+-def InstallRawImage(image_data, api_version, input_zip, fn, info, filesmap):
+-  #fn is in RADIO/* format. Extracting just file name.
+-  filename = fn[6:]
+   if api_version >= 3:
+-    if filename not in filesmap:
++    if (fn2.endswith("ENC") or fn2.endswith("enc")):
++        info.script.AppendExtra(('''
++assert(package_extract_file("%s", "%s"));
++''' %(fn2,fn3) % locals()).lstrip())
++    else:
++        fstab = info.script.info.get("fstab", None)
++        if fn2 not in QCOM_MOUNT_MAP:
++            return
++        if QCOM_MOUNT_MAP[fn2] not in fstab:
++            return
++        info.script.WriteRawImage(QCOM_MOUNT_MAP[fn2], fn2);
+         return
+-    info.script.AppendExtra('package_extract_file("%s", "%s");' % (filename,filesmap[filename]))
+-    common.ZipWriteStr(info.output_zip, filename, image_data)
+-    return
++  elif info.input_version >= 2:
++    info.script.AppendExtra(
++        'write_firmware_image("PACKAGE:radio.img", "radio");')
+   else:
+-    print "warning raido-update: no support for api_version less than 3."
++    info.script.AppendExtra(
++        ('assert(package_extract_file("radio.img", "/tmp/radio.img"),\n'
++         '       write_firmware_image("/tmp/radio.img", "radio"));\n'))
++
+ 
+-def FULLOTA_InstallEnd_MMC(info):
++def FullOTA_InstallEnd(info):
+   files = GetRadioFiles(info.input_zip)
+   if files == {}:
+-    print "warning radio-update: no radio image in input target_files; not flashing radio"
++    print "warning sha: no radio image in input target_files; not flashing radio"
+     return
++
++  enc_file = "false";
++
+   info.script.UnmountAll()
+   info.script.Print("Writing radio image...")
+-  #Load filesmap file
+-  filesmap = LoadFilesMap(info.input_zip, info.type)
+-  if filesmap == {}:
+-      print "warning radio-update: no or invalid filesmap file found. not flashing radio"
+-      return
+   for f in files:
+-    image_data = info.input_zip.read(f)
+-    InstallRawImage(image_data, info.input_version, info.input_zip, f, info, filesmap)
+-  return
++    if (f.endswith("ENC") or f.endswith("enc")):
++        continue
++    radio_img = info.input_zip.read(f)
++    InstallRadio(radio_img, info.input_version, info.input_zip, f, info)
+ 
+-def FULLOTA_InstallEnd_MTD(info):
+-  print "warning radio-update: no implementation for radio upgrade for NAND devices"
++  for f in files:
++    if (f.endswith("ENC") or f.endswith("enc")):
++        radio_img = info.input_zip.read(f)
++        InstallRadio(radio_img, info.input_version, info.input_zip, f, info)
++        enc_file = "true"
++
++  if (enc_file == "true"):
++    CheckRadiotarget(info, "/recovery")
+   return
+ 
+-def FullOTA_InstallEnd(info):
+-  if info.type == 'MTD':
+-    FULLOTA_InstallEnd_MTD(info)
+-  if info.type == 'MMC':
+-    FULLOTA_InstallEnd_MMC(info)
+ 
+ def IncrementalOTA_InstallEnd(info):
+   #TODO: Implement device specific asserstions.
diff --git a/patch/ics_strawberry/device/qcom/msm7627a/enable_recovery_updater.patch b/patch/ics_strawberry/device/qcom/msm7627a/enable_recovery_updater.patch
new file mode 100644
index 0000000000000000000000000000000000000000..38aa1e0419cd38a4e7a65d47272053a0b722ab69
--- /dev/null
+++ b/patch/ics_strawberry/device/qcom/msm7627a/enable_recovery_updater.patch
@@ -0,0 +1,21 @@
+diff --git a/BoardConfig.mk b/BoardConfig.mk
+index e8b6695..2d0243d 100644
+--- a/BoardConfig.mk
++++ b/BoardConfig.mk
+@@ -61,6 +61,7 @@ BOARD_HAVE_BLUETOOTH := true
+ BOARD_HAVE_QCOM_FM := true
+ TARGET_HAVE_TSLIB := true
+ 
++ADD_RADIO_FILES := true
+ TARGET_NO_BOOTLOADER := false
+ TARGET_NO_KERNEL := false
+ TARGET_NO_RADIOIMAGE := true
+@@ -109,7 +110,7 @@ BOARD_USERDATAIMAGE_PARTITION_SIZE := 314556416
+ BOARD_PERSISTIMAGE_PARTITION_SIZE := 10485760
+ BOARD_CACHEIMAGE_PARTITION_SIZE := 41943040
+ BOARD_FLASH_BLOCK_SIZE := 131072 # (BOARD_KERNEL_PAGESIZE * 64)
+-#TARGET_RECOVERY_UPDATER_LIBS += librecovery_updater_qcom
++TARGET_RECOVERY_UPDATER_LIBS += librecovery_updater_qcom
+ 
+ BOARD_HAVE_MXT224_CFG := true
+ 
diff --git a/updater/Android.mk b/updater/Android.mk
new file mode 100644
index 0000000000000000000000000000000000000000..824b0b287be2edd0c1c672297b3be67eca8db10d
--- /dev/null
+++ b/updater/Android.mk
@@ -0,0 +1,19 @@
+ifneq ($(TARGET_SIMULATOR),true)
+ifeq ($(TARGET_ARCH),arm)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# librecovery_update_qcom is a set of edify extension functions for
+# doing radio update on QCOM devices.
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := recovery_updater.c firmware.c bootloader.c
+LOCAL_STATIC_LIBRARIES += libmtdutils
+LOCAL_C_INCLUDES += bootable/recovery
+LOCAL_MODULE := librecovery_updater_qcom
+include $(BUILD_STATIC_LIBRARY)
+
+endif   # TARGET_ARCH == arm
+endif   # !TARGET_SIMULATOR
diff --git a/updater/MODULE_LICENSE_APACHE2 b/updater/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/updater/NOTICE b/updater/NOTICE
new file mode 100644
index 0000000000000000000000000000000000000000..64aaa8dbd68e6917b35c02655ba2f8d763165368
--- /dev/null
+++ b/updater/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2009, 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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/updater/bootloader.c b/updater/bootloader.c
new file mode 100644
index 0000000000000000000000000000000000000000..2c2aee9d8d7526b1dca3f8efea87610bf2abad5e
--- /dev/null
+++ b/updater/bootloader.c
@@ -0,0 +1,311 @@
+/* Copyright (C) 2008 The Android Open Source Project
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "bootloader.h"
+#include "common.h"
+#include "mtdutils/mtdutils.h"
+#include "roots.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+static const int MISC_PAGES = 3;         // number of pages to save
+static const int MISC_COMMAND_PAGE = 1;  // bootloader command is this page
+
+#undef LOGE
+#define LOGE(...) fprintf(stderr, "E:" __VA_ARGS__)
+
+#ifdef LOG_VERBOSE
+static void dump_data(const char *data, int len) {
+    int pos;
+    for (pos = 0; pos < len; ) {
+        printf("%05x: %02x", pos, data[pos]);
+        for (++pos; pos < len && (pos % 24) != 0; ++pos) {
+            printf(" %02x", data[pos]);
+        }
+        printf("\n");
+    }
+}
+#endif
+
+int get_bootloader_message_emmc(struct bootloader_message *out, Volume *v) {
+    FILE* f = fopen(v->device, "rb");
+    if (f == NULL) {
+        LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
+        return -1;
+    }
+    struct bootloader_message temp;
+    int count = fread(&temp, sizeof(temp), 1, f);
+    if (count != 1) {
+        LOGE("Failed reading %s\n(%s)\n", v->device, strerror(errno));
+        return -1;
+    }
+    if (fclose(f) != 0) {
+        LOGE("Failed closing %s\n(%s)\n", v->device, strerror(errno));
+        return -1;
+    }
+    memcpy(out, &temp, sizeof(temp));
+    return 0;
+}
+
+int set_bootloader_message_emmc(const struct bootloader_message *in, Volume *v) {
+    FILE* f = fopen(v->device, "wb");
+    if (f == NULL) {
+        LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
+        return -1;
+    }
+    int count = fwrite(in, sizeof(*in), 1, f);
+    if (count != 1) {
+        LOGE("Failed writing %s\n(%s)\n", v->device, strerror(errno));
+        return -1;
+    }
+    if (fclose(f) != 0) {
+        LOGE("Failed closing %s\n(%s)\n", v->device, strerror(errno));
+        return -1;
+    }
+    return 0;
+}
+
+int get_bootloader_message(struct bootloader_message *out) {
+    size_t write_size;
+    const MtdPartition *part = mtd_find_partition_by_name(MISC_NAME);
+    if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) {
+        LOGE("Can't find %s\n", MISC_NAME);
+        return -1;
+    }
+
+    MtdReadContext *read = mtd_read_partition(part);
+    if (read == NULL) {
+        LOGE("Can't open %s\n(%s)\n", MISC_NAME, strerror(errno));
+        return -1;
+    }
+
+    const ssize_t size = write_size * MISC_PAGES;
+    char data[size];
+    ssize_t r = mtd_read_data(read, data, size);
+    if (r != size) LOGE("Can't read %s\n(%s)\n", MISC_NAME, strerror(errno));
+    mtd_read_close(read);
+    if (r != size) return -1;
+
+#ifdef LOG_VERBOSE
+    printf("\n--- get_bootloader_message ---\n");
+    dump_data(data, size);
+    printf("\n");
+#endif
+
+    memcpy(out, &data[write_size * MISC_COMMAND_PAGE], sizeof(*out));
+    return 0;
+}
+
+int set_bootloader_message(const struct bootloader_message *in) {
+    size_t write_size;
+    const MtdPartition *part = mtd_find_partition_by_name(MISC_NAME);
+    if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) {
+        LOGE("Can't find %s\n", MISC_NAME);
+        return -1;
+    }
+
+    MtdReadContext *read = mtd_read_partition(part);
+    if (read == NULL) {
+        LOGE("Can't open %s\n(%s)\n", MISC_NAME, strerror(errno));
+        return -1;
+    }
+
+    ssize_t size = write_size * MISC_PAGES;
+    char data[size];
+    ssize_t r = mtd_read_data(read, data, size);
+    if (r != size) LOGE("Can't read %s\n(%s)\n", MISC_NAME, strerror(errno));
+    mtd_read_close(read);
+    if (r != size) return -1;
+
+    memcpy(&data[write_size * MISC_COMMAND_PAGE], in, sizeof(*in));
+
+#ifdef LOG_VERBOSE
+    printf("\n--- set_bootloader_message ---\n");
+    dump_data(data, size);
+    printf("\n");
+#endif
+
+    MtdWriteContext *write = mtd_write_partition(part);
+    if (write == NULL) {
+        LOGE("Can't open %s\n(%s)\n", MISC_NAME, strerror(errno));
+        return -1;
+    }
+    if (mtd_write_data(write, data, size) != size) {
+        LOGE("Can't write %s\n(%s)\n", MISC_NAME, strerror(errno));
+        mtd_write_close(write);
+        return -1;
+    }
+    if (mtd_write_close(write)) {
+        LOGE("Can't finish %s\n(%s)\n", MISC_NAME, strerror(errno));
+        return -1;
+    }
+
+    LOGI("Set boot command \"%s\"\n", in->command[0] != 255 ? in->command : "");
+    return 0;
+}
+
+/* Update Image
+ *
+ * - will be stored in the "cache" partition
+ * - bad blocks will be ignored, like boot.img and recovery.img
+ * - the first block will be the image header (described below)
+ * - the size is in BYTES, inclusive of the header
+ * - offsets are in BYTES from the start of the update header
+ */
+
+struct update_header {
+    unsigned char MAGIC[UPDATE_MAGIC_SIZE];
+
+    unsigned version;
+    unsigned size;
+
+    unsigned image_offset;
+    unsigned image_length;
+};
+
+int write_update_for_bootloader(
+        const char *update, int update_length,
+        const char *log_filename) {
+    const MtdPartition *part = mtd_find_partition_by_name(CACHE_NAME);
+    if (part == NULL) {
+        LOGE("Can't find %s\n", CACHE_NAME);
+        return -1;
+    }
+
+    MtdWriteContext *write = mtd_write_partition(part);
+    if (write == NULL) {
+        LOGE("Can't open %s\n(%s)\n", CACHE_NAME, strerror(errno));
+        return -1;
+    }
+
+    /* Write an invalid (zero) header first, to disable any previous
+     * update and any other structured contents (like a filesystem),
+     * and as a placeholder for the amount of space required.
+     */
+
+    struct update_header header;
+    memset(&header, 0, sizeof(header));
+    const ssize_t header_size = sizeof(header);
+    if (mtd_write_data(write, (char*) &header, header_size) != header_size) {
+        LOGE("Can't write header to %s\n(%s)\n", CACHE_NAME, strerror(errno));
+        mtd_write_close(write);
+        return -1;
+    }
+
+    /* Write each section individually block-aligned, so we can write
+     * each block independently without complicated buffering.
+     */
+
+    memcpy(&header.MAGIC, UPDATE_MAGIC, UPDATE_MAGIC_SIZE);
+    header.version = UPDATE_VERSION;
+    header.size = header_size;
+
+    if (log_filename != NULL) {
+        // Write 1 byte into the following block, then fill to the end
+        // in order to reserve that block.  We'll use the block to
+        // send a copy of the log through to the next invocation of
+        // recovery.  We write the log as late as possible in order to
+        // capture any messages emitted by this function.
+        mtd_erase_blocks(write, 0);
+        if (mtd_write_data(write, (char*) &header, 1) != 1) {
+            LOGE("Can't write log block to %s\n(%s)\n",
+                 CACHE_NAME, strerror(errno));
+            mtd_write_close(write);
+            return -1;
+        }
+    }
+
+    off_t image_start_pos = mtd_erase_blocks(write, 0);
+    header.image_length = update_length;
+    if ((int) header.image_offset == -1 ||
+        mtd_write_data(write, update, update_length) != update_length) {
+        LOGE("Can't write update to %s\n(%s)\n", CACHE_NAME, strerror(errno));
+        mtd_write_close(write);
+        return -1;
+    }
+    mtd_erase_blocks(write, 0);
+    /* Sending image offset as it is.Apps bootloader will take care of bad blocks */
+    header.image_offset = 0x80000;
+
+    /* Write the header last, after all the blocks it refers to, so that
+     * when the magic number is installed everything is valid.
+     */
+
+    if (mtd_write_close(write)) {
+        LOGE("Can't finish writing %s\n(%s)\n", CACHE_NAME, strerror(errno));
+        return -1;
+    }
+
+    write = mtd_write_partition(part);
+    if (write == NULL) {
+        LOGE("Can't reopen %s\n(%s)\n", CACHE_NAME, strerror(errno));
+        return -1;
+    }
+
+    if (mtd_write_data(write, (char*) &header, header_size) != header_size) {
+        LOGE("Can't rewrite header to %s\n(%s)\n", CACHE_NAME, strerror(errno));
+        mtd_write_close(write);
+        return -1;
+    }
+
+    if (log_filename != NULL) {
+        LOGE("writing log\n");
+        size_t erase_size;
+        if (mtd_partition_info(part, NULL, &erase_size, NULL) != 0) {
+            LOGE("Error reading block size\n(%s)\n", strerror(errno));
+            mtd_write_close(write);
+            return -1;
+        }
+        mtd_erase_blocks(write, 0);
+
+        if (erase_size > 0) {
+            char* log = malloc(erase_size);
+            FILE* f = fopen(log_filename, "rb");
+            // The fseek() may fail if it tries to go before the
+            // beginning of the log, but that's okay because we want
+            // to be positioned at the start anyway.
+            fseek(f, -(erase_size-sizeof(size_t)-LOG_MAGIC_SIZE), SEEK_END);
+            memcpy(log, LOG_MAGIC, LOG_MAGIC_SIZE);
+            size_t read = fread(log+sizeof(size_t)+LOG_MAGIC_SIZE,
+                                1, erase_size-sizeof(size_t)-LOG_MAGIC_SIZE, f);
+            LOGI("read %d bytes from log\n", (int)read);
+            *(size_t *)(log + LOG_MAGIC_SIZE) = read;
+            fclose(f);
+            if (mtd_write_data(write, log, erase_size) != erase_size) {
+                LOGE("failed to store log in cache partition\n(%s)\n",
+                     strerror(errno));
+                mtd_write_close(write);
+            }
+            free(log);
+        }
+    }
+
+    if (mtd_erase_blocks(write, 0) != image_start_pos) {
+        LOGE("Misalignment rewriting %s\n(%s)\n", CACHE_NAME, strerror(errno));
+        mtd_write_close(write);
+        return -1;
+    }
+
+    LOGE("closing partition\n");
+    if (mtd_write_close(write)) {
+        LOGE("Can't finish header of %s\n(%s)\n", CACHE_NAME, strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/updater/bootloader.h b/updater/bootloader.h
new file mode 100644
index 0000000000000000000000000000000000000000..bc80a258eb08343e068d8b933423b225971b7104
--- /dev/null
+++ b/updater/bootloader.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _RECOVERY_BOOTLOADER_H
+#define _RECOVERY_BOOTLOADER_H
+
+/* Bootloader Message
+ *
+ * This structure describes the content of a block in flash
+ * that is used for recovery and the bootloader to talk to
+ * each other.
+ *
+ * The command field is updated by linux when it wants to
+ * reboot into recovery or to update radio or bootloader firmware.
+ * It is also updated by the bootloader when firmware update
+ * is complete (to boot into recovery for any final cleanup)
+ *
+ * The status field is written by the bootloader after the
+ * completion of an "update-radio" command.
+ *
+ * The recovery field is only written by linux and used
+ * for the system to send a message to recovery or the
+ * other way around.
+ */
+struct bootloader_message {
+    char command[32];
+    char status[32];
+    char recovery[1024];
+};
+
+/* Read and write the bootloader command from the "misc" partition.
+ * These return zero on success.
+ */
+int get_bootloader_message(struct bootloader_message *out);
+int set_bootloader_message(const struct bootloader_message *in);
+
+/* Write an update to the cache partition for update-radio.
+ * Note, this destroys any filesystem on the cache partition!
+ */
+int write_update_for_bootloader(
+        const char *update, int update_len,
+        const char *log_filename);
+
+/* Look for a log stored in the cache partition in the block after the
+ * firmware update header.  If we can read such a log, copy it to
+ * stdout (ie, the current log).
+ */
+void recover_firmware_update_log();
+
+#define CACHE_NAME  "cache"
+#define MISC_NAME   "misc"
+
+#define UPDATE_MAGIC       "MSM-RADIO-UPDATE"
+#define UPDATE_MAGIC_SIZE  16
+#define UPDATE_VERSION     0x00010000
+
+#define LOG_MAGIC        "LOGmagic"
+#define LOG_MAGIC_SIZE   8
+
+
+
+#endif
diff --git a/updater/firmware.c b/updater/firmware.c
new file mode 100644
index 0000000000000000000000000000000000000000..9fc673ab0f27caf051055a8d952c6bc3f087ce2d
--- /dev/null
+++ b/updater/firmware.c
@@ -0,0 +1,251 @@
+/* Copyright (C) 2008 The Android Open Source Project
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "bootloader.h"
+#include "common.h"
+#include "firmware.h"
+#include "mtdutils/mtdutils.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/reboot.h>
+#include <sys/mount.h>
+
+/* Bootloader / Recovery Flow
+ *
+ * On every boot, the bootloader will read the bootloader_message
+ * from flash and check the command field.  The bootloader should
+ * deal with the command field not having a 0 terminator correctly
+ * (so as to not crash if the block is invalid or corrupt).
+ *
+ * The bootloader will have to publish the partition that contains
+ * the bootloader_message to the linux kernel so it can update it.
+ *
+ * if command == "boot-recovery" -> boot recovery.img
+ * else if command == "update-radio" -> update radio image (below)
+ * else -> boot boot.img (normal boot)
+ *
+ * Radio Update Flow
+ * 1. the bootloader will attempt to load and validate the header
+ * 2. if the header is invalid, status="invalid-update", goto #8
+ * 3. display the busy image on-screen
+ * 4. if the update image is invalid, status="invalid-radio-image", goto #8
+ * 5. attempt to update the firmware (depending on the command)
+ * 6. if successful, status="okay", goto #8
+ * 7. if failed, and the old image can still boot, status="failed-update"
+ * 8. write the bootloader_message, leaving the recovery field
+ *    unchanged, updating status, and setting command to
+ *    "boot-recovery"
+ * 9. reboot
+ *
+ * The bootloader will not modify or erase the cache partition.
+ * It is recovery's responsibility to clean up the mess afterwards.
+ */
+
+#undef LOGE
+#define LOGE(...) fprintf(stderr, "E:" __VA_ARGS__)
+
+static int num_volumes = 0;
+static Volume* device_volumes = NULL;
+
+int install_firmware_update(const char *update_type,
+                            const char *update_data,
+                            size_t update_length,
+                            const char *log_filename) {
+    if (update_data == NULL || update_length == 0) return 0;
+
+    mtd_scan_partitions();
+
+    /* We destroy the cache partition to pass the update image to the
+     * bootloader, so all we can really do afterwards is wipe cache and reboot.
+     * Set up this instruction now, in case we're interrupted while writing.
+     */
+
+    struct bootloader_message boot;
+    memset(&boot, 0, sizeof(boot));
+    strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
+    strlcpy(boot.recovery, "recovery\n--wipe_cache\n", sizeof(boot.command));
+    if (set_bootloader_message(&boot)) return -1;
+
+    if (write_update_for_bootloader(
+            update_data, update_length,
+            log_filename)) {
+        LOGE("Can't write %s image\n(%s)\n", update_type, strerror(errno));
+        return -1;
+    }
+
+    /* The update image is fully written, so now we can instruct the bootloader
+     * to install it.  (After doing so, it will come back here, and we will
+     * wipe the cache and reboot into the system.)
+     */
+    snprintf(boot.command, sizeof(boot.command), "update-%s", update_type);
+    if (set_bootloader_message(&boot)) {
+        return -1;
+    }
+
+    reboot(RB_AUTOBOOT);
+
+    LOGE("Can't reboot\n");
+    return -1;
+}
+
+static void load_volume_table() {
+    int alloc = 2;
+    device_volumes = malloc(alloc * sizeof(Volume));
+
+    // Insert an entry for /tmp, which is the ramdisk and is always mounted.
+    device_volumes[0].mount_point = "/tmp";
+    device_volumes[0].fs_type = "ramdisk";
+    device_volumes[0].device = NULL;
+    device_volumes[0].device2 = NULL;
+    num_volumes = 1;
+
+    FILE* fstab;
+    fstab = fopen("/etc/recovery_mmc.fstab", "r");
+
+    if (fstab == NULL) {
+        LOGE("failed to open /etc/recovery.fstab (%s)\n", strerror(errno));
+        return;
+    }
+
+    char buffer[1024];
+    int i;
+    while (fgets(buffer, sizeof(buffer)-1, fstab)) {
+        for (i = 0; buffer[i] && isspace(buffer[i]); ++i);
+        if (buffer[i] == '\0' || buffer[i] == '#') continue;
+
+        char* original = strdup(buffer);
+
+        char* mount_point = strtok(buffer+i, " \t\n");
+        char* fs_type = strtok(NULL, " \t\n");
+        char* device = strtok(NULL, " \t\n");
+        // lines may optionally have a second device, to use if
+        // mounting the first one fails.
+        char* device2 = strtok(NULL, " \t\n");
+
+        if (mount_point && fs_type && device) {
+            while (num_volumes >= alloc) {
+                alloc *= 2;
+                device_volumes = realloc(device_volumes, alloc*sizeof(Volume));
+            }
+            device_volumes[num_volumes].mount_point = strdup(mount_point);
+            device_volumes[num_volumes].fs_type = strdup(fs_type);
+            device_volumes[num_volumes].device = strdup(device);
+            device_volumes[num_volumes].device2 =
+                device2 ? strdup(device2) : NULL;
+            ++num_volumes;
+        } else {
+            LOGE("skipping malformed recovery.fstab line: %s\n", original);
+        }
+        free(original);
+    }
+
+    fclose(fstab);
+}
+
+static Volume* volume_for_path(const char* path) {
+    int i;
+    for (i = 0; i < num_volumes; ++i) {
+        Volume* v = device_volumes+i;
+        int len = strlen(v->mount_point);
+        if (strncmp(path, v->mount_point, len) == 0 &&
+            (path[len] == '\0' || path[len] == '/')) {
+            return v;
+        }
+    }
+    return NULL;
+}
+
+int start_firmware_update(char *update_type, char *part_type)
+{
+    int result;
+    struct bootloader_message boot;
+
+    memset(&boot, 0, sizeof(boot));
+
+    if(!strcmp(part_type, "mtd"))
+    {
+        mtd_scan_partitions();
+
+        strlcpy(boot.recovery, "recovery\n--radio_status\n", sizeof(boot.command));
+        snprintf(boot.command, sizeof(boot.command), "update-%s", update_type);
+        if (set_bootloader_message(&boot)) {
+            return -1;
+        }
+    }
+    else if(!strcmp(part_type, "emmc"))
+    {
+        Volume *v = NULL;
+
+        load_volume_table();
+
+        v = volume_for_path("/sys_boot");
+        if (strcmp(v->fs_type, "vfat"))
+        {
+            LOGE("Error in fs_type for sys_boot partition\n");
+            return -1;
+        }
+
+        mkdir("/sys_boot", 777);
+
+        /* Try mounting device first */
+        result = mount(v->device, v->mount_point, v->fs_type,
+                       MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
+        if(result)
+        {
+            /* Try mounting device2 next */
+            result = mount(v->device2, v->mount_point, v->fs_type,
+                           MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
+        }
+        if(result == 0)
+        {
+             /* Creating cookie file for radio update */
+             FILE *fp = fopen("/sys_boot/upcookie.txt", "w");
+             fclose(fp);
+
+             /* Unmount the sdcard now */
+             if(umount(v->mount_point))
+             {
+                 LOGE("Error in unmounting  %s\n",v->mount_point);
+                 return -1;
+             }
+             else
+                 LOGI("Created cookie file for eMMC radio update\n");
+        }
+        else
+        {
+             LOGE("Error in mounting  %s\n",v->mount_point);
+             return -1;
+        }
+		memset(&boot, 0, sizeof(boot));
+        v = volume_for_path("/misc");
+        if (set_bootloader_message_emmc(&boot, v)) {
+            return -1;
+        }
+    }
+    else
+    {
+        LOGE("Error in part_type %s\n",part_type);
+        return -1;
+    }
+
+    sync();
+    reboot(RB_AUTOBOOT);
+
+    // Can't reboot?  WTF?
+    LOGE("Can't reboot\n");
+    return -1;
+}
diff --git a/updater/firmware.h b/updater/firmware.h
new file mode 100644
index 0000000000000000000000000000000000000000..bb319bf6075eb1d7266e522b4ab99c6673ffd99c
--- /dev/null
+++ b/updater/firmware.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _RECOVERY_FIRMWARE_H
+#define _RECOVERY_FIRMWARE_H
+
+/* Reboot into the bootloader to install the given update.
+ * Returns 0 if no radio image was defined, nonzero on error,
+ * doesn't return at all on success...
+ */
+int install_firmware_update(const char *update_type,
+                            const char *update_data,
+                            size_t update_length,
+                            const char *log_filename);
+
+#endif
diff --git a/updater/recovery_updater.c b/updater/recovery_updater.c
new file mode 100644
index 0000000000000000000000000000000000000000..f5804908acce40ec141c73580288cb672b287849
--- /dev/null
+++ b/updater/recovery_updater.c
@@ -0,0 +1,87 @@
+/* Copyright (C) 2009 The Android Open Source Project
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "edify/expr.h"
+#include "firmware.h"
+
+Value* UpdateFn(const char* name, State* state, int argc, Expr* argv[]) {
+    if (argc != 1) {
+        return ErrorAbort(state, "%s() expects 6 args, got %d", name, argc);
+    }
+
+    char* type = strrchr(name, '_');
+    if (type == NULL || *(type+1) == '\0') {
+        return ErrorAbort(state, "%s() couldn't get type from function name",
+                          name);
+    }
+    ++type;
+
+    Value* image;
+
+    if (ReadValueArgs(state, argv, 1, &image) <0) {
+        return NULL;
+    }
+
+    if (image->type != VAL_BLOB) {
+        printf("image argument is not blob (is type %d)\n", image->type);
+        goto done;
+    }
+
+    install_firmware_update(type, image->data, image->size, "/tmp/recovery.log");
+    printf("%s: install_firmware_update returned!\n", name);
+
+  done:
+    FreeValue(image);
+    // install_firmware_update should reboot.  If it returns, it failed.
+    return StringValue(strdup(""));
+}
+
+Value* SetRadioFn(const char* name, State* state, int argc, Expr* argv[]) {
+    char *part_type;
+
+    if (argc != 1) {
+        return ErrorAbort(state, "%s() expects arg, got %d", name, argc);
+    }
+
+    char* type = strrchr(name, '_');
+    if (type == NULL || *(type+1) == '\0') {
+        return ErrorAbort(state, "%s() couldn't get type from function name",
+                          name);
+    }
+    ++type;
+
+    if (ReadArgs(state, argv, 1, &part_type) <0) {
+        return NULL;
+    }
+
+    start_firmware_update(type,part_type);
+
+    return StringValue(strdup(""));
+}
+
+void Register_librecovery_updater_qcom() {
+    fprintf(stderr, "installing QCOM updater extensions\n");
+
+    RegisterFunction("qcom.install_radio", UpdateFn);
+    RegisterFunction("qcom.set_radio", SetRadioFn);
+}