The Android Open Source Project | c24a8e6 | 2009-03-03 19:28:42 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2008 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include "bootloader.h" |
| 18 | #include "common.h" |
| 19 | #include "firmware.h" |
| 20 | #include "roots.h" |
| 21 | |
| 22 | #include <errno.h> |
| 23 | #include <string.h> |
| 24 | #include <sys/reboot.h> |
| 25 | |
| 26 | static const char *update_type = NULL; |
| 27 | static const char *update_data = NULL; |
| 28 | static int update_length = 0; |
| 29 | |
| 30 | int remember_firmware_update(const char *type, const char *data, int length) { |
| 31 | if (update_type != NULL || update_data != NULL) { |
| 32 | LOGE("Multiple firmware images\n"); |
| 33 | return -1; |
| 34 | } |
| 35 | |
| 36 | update_type = type; |
| 37 | update_data = data; |
| 38 | update_length = length; |
| 39 | return 0; |
| 40 | } |
| 41 | |
| 42 | |
| 43 | /* Bootloader / Recovery Flow |
| 44 | * |
| 45 | * On every boot, the bootloader will read the bootloader_message |
| 46 | * from flash and check the command field. The bootloader should |
| 47 | * deal with the command field not having a 0 terminator correctly |
| 48 | * (so as to not crash if the block is invalid or corrupt). |
| 49 | * |
| 50 | * The bootloader will have to publish the partition that contains |
| 51 | * the bootloader_message to the linux kernel so it can update it. |
| 52 | * |
| 53 | * if command == "boot-recovery" -> boot recovery.img |
| 54 | * else if command == "update-radio" -> update radio image (below) |
| 55 | * else if command == "update-hboot" -> update hboot image (below) |
| 56 | * else -> boot boot.img (normal boot) |
| 57 | * |
| 58 | * Radio/Hboot Update Flow |
| 59 | * 1. the bootloader will attempt to load and validate the header |
| 60 | * 2. if the header is invalid, status="invalid-update", goto #8 |
| 61 | * 3. display the busy image on-screen |
| 62 | * 4. if the update image is invalid, status="invalid-radio-image", goto #8 |
| 63 | * 5. attempt to update the firmware (depending on the command) |
| 64 | * 6. if successful, status="okay", goto #8 |
| 65 | * 7. if failed, and the old image can still boot, status="failed-update" |
| 66 | * 8. write the bootloader_message, leaving the recovery field |
| 67 | * unchanged, updating status, and setting command to |
| 68 | * "boot-recovery" |
| 69 | * 9. reboot |
| 70 | * |
| 71 | * The bootloader will not modify or erase the cache partition. |
| 72 | * It is recovery's responsibility to clean up the mess afterwards. |
| 73 | */ |
| 74 | |
| 75 | int maybe_install_firmware_update(const char *send_intent) { |
| 76 | if (update_data == NULL || update_length == 0) return 0; |
| 77 | |
| 78 | /* We destroy the cache partition to pass the update image to the |
| 79 | * bootloader, so all we can really do afterwards is wipe cache and reboot. |
| 80 | * Set up this instruction now, in case we're interrupted while writing. |
| 81 | */ |
| 82 | |
| 83 | struct bootloader_message boot; |
| 84 | memset(&boot, 0, sizeof(boot)); |
| 85 | strlcpy(boot.command, "boot-recovery", sizeof(boot.command)); |
| 86 | strlcpy(boot.recovery, "recovery\n--wipe_cache\n", sizeof(boot.command)); |
| 87 | if (send_intent != NULL) { |
| 88 | strlcat(boot.recovery, "--send_intent=", sizeof(boot.recovery)); |
| 89 | strlcat(boot.recovery, send_intent, sizeof(boot.recovery)); |
| 90 | strlcat(boot.recovery, "\n", sizeof(boot.recovery)); |
| 91 | } |
| 92 | if (set_bootloader_message(&boot)) return -1; |
| 93 | |
| 94 | int width = 0, height = 0, bpp = 0; |
| 95 | char *busy_image = ui_copy_image( |
| 96 | BACKGROUND_ICON_FIRMWARE_INSTALLING, &width, &height, &bpp); |
| 97 | char *fail_image = ui_copy_image( |
| 98 | BACKGROUND_ICON_FIRMWARE_ERROR, &width, &height, &bpp); |
| 99 | |
| 100 | ui_print("Writing %s image...\n", update_type); |
| 101 | if (write_update_for_bootloader( |
| 102 | update_data, update_length, |
| 103 | width, height, bpp, busy_image, fail_image)) { |
| 104 | LOGE("Can't write %s image\n(%s)\n", update_type, strerror(errno)); |
| 105 | format_root_device("CACHE:"); // Attempt to clean cache up, at least. |
| 106 | return -1; |
| 107 | } |
| 108 | |
| 109 | free(busy_image); |
| 110 | free(fail_image); |
| 111 | |
| 112 | /* The update image is fully written, so now we can instruct the bootloader |
| 113 | * to install it. (After doing so, it will come back here, and we will |
| 114 | * wipe the cache and reboot into the system.) |
| 115 | */ |
| 116 | snprintf(boot.command, sizeof(boot.command), "update-%s", update_type); |
| 117 | if (set_bootloader_message(&boot)) { |
| 118 | format_root_device("CACHE:"); |
| 119 | return -1; |
| 120 | } |
| 121 | |
| 122 | reboot(RB_AUTOBOOT); |
| 123 | |
| 124 | // Can't reboot? WTF? |
| 125 | LOGE("Can't reboot\n"); |
| 126 | return -1; |
| 127 | } |