write primary and backup copies of the manta bootloader

Bug: 6641429
Change-Id: Ibfa46e7369cb3ed641474e94736f9570ae298963
diff --git a/BoardConfig.mk b/BoardConfig.mk
index 785896c..ab2cef1 100755
--- a/BoardConfig.mk
+++ b/BoardConfig.mk
@@ -70,4 +70,5 @@
 
 TARGET_RECOVERY_PIXEL_FORMAT := RGBX_8888
 TARGET_RECOVERY_UI_LIB := librecovery_ui_manta
+TARGET_RECOVERY_UPDATER_LIBS += librecovery_updater_manta
 TARGET_RELEASETOOLS_EXTENSIONS := device/samsung/manta
diff --git a/recovery/Android.mk b/recovery/Android.mk
index 8c901cc..1a2b865 100644
--- a/recovery/Android.mk
+++ b/recovery/Android.mk
@@ -11,3 +11,14 @@
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
+
+# Edify extension functions for doing bootloader updates on manta devices.
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_C_INCLUDES += bootable/recovery
+LOCAL_SRC_FILES := recovery_updater.c
+
+# should match TARGET_RECOVERY_UPDATER_LIBS set in BoardConfig.mk
+LOCAL_MODULE := librecovery_updater_manta
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/recovery/recovery_updater.c b/recovery/recovery_updater.c
new file mode 100644
index 0000000..8da6165
--- /dev/null
+++ b/recovery/recovery_updater.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "edify/expr.h"
+
+// The bootloader block /dev/block/mmcblk0boot0 contains a low-level
+// bootloader (BL1/BL2) followed by a "boot flag", followed by a
+// primary copy of the full bootloader, then a backup copy of the full
+// bootloader.  The boot flag tells the low-level code which copy of
+// the big bootloader to boot.
+//
+// The strategy here is to read the boot flag, write the *other* copy
+// of the big bootloader, flip the boot flag to that freshly-written
+// copy, write remaining copy of the big bootloader, then flip the
+// flag back (so most of the time it will be booting the primary
+// copy).  At the end we update the BL1/BL2, which is slightly
+// dangerous (there's no backup; if we fail during writing this part
+// then the device is a brick).  We could remove that before launch,
+// or restrict it to only being written on test-keys/dev-keys devices.
+
+#define BOOTFLAG_OFFSET (31*1024)
+
+#define BL1BL2_LENGTH (31*1024)
+
+#define INPUT_OFFSET (35*1024)
+#define PRIMARY_OUTPUT_OFFSET (35*1024)
+#define SECONDARY_OUTPUT_OFFSET (1280*1024)
+#define BIG_LENGTH (1245*1024)
+
+static void copy_block(FILE *f, unsigned char* data, size_t in_offset,
+                       size_t length, size_t out_offset) {
+    if (fseek(f, out_offset, SEEK_SET) < 0) {
+        fprintf(stderr, "failed to seek to %d: %s\n",
+                out_offset, strerror(errno));
+        return;
+    }
+    if (fwrite(data+in_offset, 1, length, f) != length) {
+        fprintf(stderr, "failed to write bootloader: %s\n", strerror(errno));
+        return;
+    }
+    fflush(f);
+    fsync(fileno(f));
+}
+
+static int get_bootflag(FILE *f) {
+    fseek(f, BOOTFLAG_OFFSET, SEEK_SET);
+    char buffer[8];
+    if (fread(buffer, 1, 8, f) != 8) {
+        fprintf(stderr, "failed to read boot flag: %s\n", strerror(errno));
+        return 0;
+    }
+
+    fprintf(stderr, "bootflag is [%c%c%c%c%c%c%c%c]\n",
+            buffer[0], buffer[1], buffer[2], buffer[3],
+            buffer[4], buffer[5], buffer[6], buffer[7]);
+
+    if (strncmp(buffer, "MANTABL", 7) != 0) return 0;
+    if (buffer[7] == '1') return 1;
+    if (buffer[7] == '2') return 2;
+    return 0;
+}
+
+static void set_bootflag(FILE* f, int value) {
+    unsigned char buffer[9] = "MANTABLx";
+    buffer[7] = '0' + value;
+    copy_block(f, buffer, 0, 8, BOOTFLAG_OFFSET);
+}
+
+static int update_bootloader(unsigned char* img_data,
+                             size_t img_size,
+                             char* block_fn,
+                             char* force_ro_fn) {
+    if (img_size != INPUT_OFFSET + BIG_LENGTH) {
+        fprintf(stderr, "expected bootloader.img of length %d; got %d\n",
+                INPUT_OFFSET + BIG_LENGTH, img_size);
+        return -1;
+    }
+
+
+    FILE* f = fopen(force_ro_fn, "w");
+    if (!f) {
+        fprintf(stderr, "failed to open %s: %s\n", force_ro_fn, strerror(errno));
+        return -1;
+    }
+    if (fwrite("0", 1, 1, f) != 1) {
+        fprintf(stderr, "failed to write %s: %s\n", force_ro_fn, strerror(errno));
+        return -1;
+    }
+    fflush(f);
+    fsync(fileno(f));
+    if (fclose(f) != 0) {
+        fprintf(stderr, "failed to close %s: %s\n", force_ro_fn, strerror(errno));
+        return -1;
+    }
+
+    f = fopen(block_fn, "r+b");
+    if (!f) {
+        fprintf(stderr, "failed to open %s: %s\n", block_fn, strerror(errno));
+        return -1;
+    }
+
+    int i;
+    int bootflag = 0;
+    for (i = 0; i < 2; ++i) {
+        bootflag = get_bootflag(f);
+
+        switch (bootflag) {
+            case 1:
+                fprintf(stderr, "updating secondary copy of bootloader\n");
+                copy_block(f, img_data, INPUT_OFFSET, BIG_LENGTH, SECONDARY_OUTPUT_OFFSET);
+                set_bootflag(f, 2);
+                break;
+            case 2:
+                fprintf(stderr, "updating primary copy of bootloader\n");
+                copy_block(f, img_data, INPUT_OFFSET, BIG_LENGTH, PRIMARY_OUTPUT_OFFSET);
+                set_bootflag(f, 1);
+                break;
+            case 0:
+                fprintf(stderr, "no bootflag; updating entire bootloader block\n");
+                copy_block(f, img_data, 0, img_size, 0);
+                i = 2;
+                break;
+        }
+    }
+
+    if (bootflag != 0) {
+        fprintf(stderr, "updating BL1/BL2\n");
+        copy_block(f, img_data, 0, BL1BL2_LENGTH, 0);
+    }
+
+    fclose(f);
+    return 0;
+}
+
+Value* WriteBootloaderFn(const char* name, State* state, int argc, Expr* argv[])
+{
+    int result = -1;
+    Value* img;
+    Value* block_loc;
+    Value* force_ro_loc;
+
+    if (argc != 3) {
+        return ErrorAbort(state, "%s() expects 3 args, got %d", name, argc);
+    }
+
+    if (ReadValueArgs(state, argv, 3, &img, &block_loc, &force_ro_loc) < 0) {
+        return NULL;
+    }
+
+    if(img->type != VAL_BLOB ||
+       block_loc->type != VAL_STRING ||
+       force_ro_loc->type != VAL_STRING) {
+      FreeValue(img);
+      FreeValue(block_loc);
+      FreeValue(force_ro_loc);
+      return ErrorAbort(state, "%s(): argument types are incorrect", name);
+    }
+
+    result = update_bootloader(img->data, img->size,
+                               block_loc->data, force_ro_loc->data);
+    FreeValue(img);
+    FreeValue(block_loc);
+    FreeValue(force_ro_loc);
+    return StringValue(strdup(result == 0 ? "t" : ""));
+}
+
+void Register_librecovery_updater_manta() {
+    fprintf(stderr, "installing samsung.manta updater extensions\n");
+
+    RegisterFunction("samsung.manta.write_bootloader", WriteBootloaderFn);
+}
diff --git a/releasetools.py b/releasetools.py
index 833c013..ae5692e 100644
--- a/releasetools.py
+++ b/releasetools.py
@@ -59,13 +59,13 @@
 
 def WriteBootloader(info, bootloader_img):
   common.ZipWriteStr(info.output_zip, "bootloader.img", bootloader_img)
-  common.ZipWriteStr(info.output_zip, "zero", "0")
   bl_type, bl_device = common.GetTypeAndDevice("/bootloader", info.info_dict)
 
   fstab = info.info_dict["fstab"]
 
   info.script.Print("Writing bootloader...")
   force_ro = "/sys/block/" + bl_device.split("/")[-1] + "/force_ro"
-  info.script.AppendExtra('package_extract_file("zero", "%s");' % (force_ro,))
-  info.script.AppendExtra('package_extract_file("bootloader.img", "%s");' %
-                          bl_device)
+  info.script.AppendExtra(
+      ('samsung.manta.write_bootloader(package_extract_file('
+       '"bootloader.img"), "%s", "%s");') %
+      (bl_device, force_ro))