Skip to content

Commit

Permalink
forward port excluded bootloader stuff from eclair
Browse files Browse the repository at this point in the history
Change-Id: I03fb0d4dc982a3718a616c6204e70a3e11cff8f8
  • Loading branch information
koush committed Jul 1, 2010
1 parent 0eb14b3 commit 107629b
Show file tree
Hide file tree
Showing 6 changed files with 349 additions and 0 deletions.
150 changes: 150 additions & 0 deletions bootloader.c
Expand Up @@ -119,3 +119,153 @@ int set_bootloader_message(const struct bootloader_message *in) {
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
* - two raw bitmaps will be included, the "busy" and "fail" bitmaps
* - for dream, the bitmaps will be 320x480x16bpp RGB565
*/

#define UPDATE_MAGIC "MSM-RADIO-UPDATE"
#define UPDATE_MAGIC_SIZE 16
#define UPDATE_VERSION 0x00010000

struct update_header {
unsigned char MAGIC[UPDATE_MAGIC_SIZE];

unsigned version;
unsigned size;

unsigned image_offset;
unsigned image_length;

unsigned bitmap_width;
unsigned bitmap_height;
unsigned bitmap_bpp;

unsigned busy_bitmap_offset;
unsigned busy_bitmap_length;

unsigned fail_bitmap_offset;
unsigned fail_bitmap_length;
};

int write_update_for_bootloader(
const char *update, int update_length,
int bitmap_width, int bitmap_height, int bitmap_bpp,
const char *busy_bitmap, const char *fail_bitmap) {
if (ensure_root_path_unmounted(CACHE_NAME)) {
LOGE("Can't unmount %s\n", CACHE_NAME);
return -1;
}

const MtdPartition *part = get_root_mtd_partition(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;

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;
}
off_t busy_start_pos = mtd_erase_blocks(write, 0);
header.image_offset = mtd_find_write_start(write, image_start_pos);

header.bitmap_width = bitmap_width;
header.bitmap_height = bitmap_height;
header.bitmap_bpp = bitmap_bpp;

int bitmap_length = (bitmap_bpp + 7) / 8 * bitmap_width * bitmap_height;

header.busy_bitmap_length = busy_bitmap != NULL ? bitmap_length : 0;
if ((int) header.busy_bitmap_offset == -1 ||
mtd_write_data(write, busy_bitmap, bitmap_length) != bitmap_length) {
LOGE("Can't write bitmap to %s\n(%s)\n", CACHE_NAME, strerror(errno));
mtd_write_close(write);
return -1;
}
off_t fail_start_pos = mtd_erase_blocks(write, 0);
header.busy_bitmap_offset = mtd_find_write_start(write, busy_start_pos);

header.fail_bitmap_length = fail_bitmap != NULL ? bitmap_length : 0;
if ((int) header.fail_bitmap_offset == -1 ||
mtd_write_data(write, fail_bitmap, bitmap_length) != bitmap_length) {
LOGE("Can't write bitmap to %s\n(%s)\n", CACHE_NAME, strerror(errno));
mtd_write_close(write);
return -1;
}
mtd_erase_blocks(write, 0);
header.fail_bitmap_offset = mtd_find_write_start(write, fail_start_pos);

/* 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 (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;
}

if (mtd_write_close(write)) {
LOGE("Can't finish header of %s\n(%s)\n", CACHE_NAME, strerror(errno));
return -1;
}

return 0;
}
9 changes: 9 additions & 0 deletions bootloader.h
Expand Up @@ -47,4 +47,13 @@ struct bootloader_message {
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 or update-hboot.
* Note, this destroys any filesystem on the cache partition!
* The expected bitmap format is 240x320, 16bpp (2Bpp), RGB 5:6:5.
*/
int write_update_for_bootloader(
const char *update, int update_len,
int bitmap_width, int bitmap_height, int bitmap_bpp,
const char *busy_bitmap, const char *error_bitmap);

#endif
7 changes: 7 additions & 0 deletions common.h
Expand Up @@ -52,10 +52,17 @@ enum {
BACKGROUND_ICON_NONE,
BACKGROUND_ICON_INSTALLING,
BACKGROUND_ICON_ERROR,
BACKGROUND_ICON_FIRMWARE_INSTALLING,
BACKGROUND_ICON_FIRMWARE_ERROR,
NUM_BACKGROUND_ICONS
};
void ui_set_background(int icon);

// Get a malloc'd copy of the screen image showing (only) the specified icon.
// Also returns the width, height, and bits per pixel of the returned image.
// TODO: Use some sort of "struct Bitmap" here instead of all these variables?
char *ui_copy_image(int icon, int *width, int *height, int *bpp);

// Show a progress bar and define the scope of the next operation:
// portion - fraction of the progress bar the next operation will use
// seconds - expected time interval (progress bar moves at this minimum rate)
Expand Down
131 changes: 131 additions & 0 deletions firmware.c
@@ -0,0 +1,131 @@
/*
* 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.
*/

#include "bootloader.h"
#include "common.h"
#include "firmware.h"
#include "roots.h"

#include <errno.h>
#include <string.h>
#include <sys/reboot.h>

static const char *update_type = NULL;
static const char *update_data = NULL;
static int update_length = 0;

int remember_firmware_update(const char *type, const char *data, int length) {
if (update_type != NULL || update_data != NULL) {
LOGE("Multiple firmware images\n");
return -1;
}

update_type = type;
update_data = data;
update_length = length;
return 0;
}

// Return true if there is a firmware update pending.
int firmware_update_pending() {
return update_data != NULL && update_length > 0;
}

/* 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 if command == "update-hboot" -> update hboot image (below)
* else -> boot boot.img (normal boot)
*
* Radio/Hboot 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.
*/

int maybe_install_firmware_update(const char *send_intent) {
if (update_data == NULL || update_length == 0) return 0;

/* 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 (send_intent != NULL) {
strlcat(boot.recovery, "--send_intent=", sizeof(boot.recovery));
strlcat(boot.recovery, send_intent, sizeof(boot.recovery));
strlcat(boot.recovery, "\n", sizeof(boot.recovery));
}
if (set_bootloader_message(&boot)) return -1;

int width = 0, height = 0, bpp = 0;
char *busy_image = ui_copy_image(
BACKGROUND_ICON_FIRMWARE_INSTALLING, &width, &height, &bpp);
char *fail_image = ui_copy_image(
BACKGROUND_ICON_FIRMWARE_ERROR, &width, &height, &bpp);

ui_print("Writing %s image...\n", update_type);
if (write_update_for_bootloader(
update_data, update_length,
width, height, bpp, busy_image, fail_image)) {
LOGE("Can't write %s image\n(%s)\n", update_type, strerror(errno));
format_root_device("CACHE:"); // Attempt to clean cache up, at least.
return -1;
}

free(busy_image);
free(fail_image);

/* 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)) {
format_root_device("CACHE:");
return -1;
}

reboot(RB_AUTOBOOT);

// Can't reboot? WTF?
LOGE("Can't reboot\n");
return -1;
}
35 changes: 35 additions & 0 deletions firmware.h
@@ -0,0 +1,35 @@
/*
* 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

/* Save a radio or bootloader update image for later installation.
* The type should be one of "hboot" or "radio".
* Takes ownership of type and data. Returns nonzero on error.
*/
int remember_firmware_update(const char *type, const char *data, int length);

/* Returns true if a firmware update has been saved. */
int firmware_update_pending();

/* If an update was saved, reboot into the bootloader now to install it.
* Returns 0 if no radio image was defined, nonzero on error,
* doesn't return at all on success...
*/
int maybe_install_firmware_update(const char *send_intent);

#endif
17 changes: 17 additions & 0 deletions ui.c
Expand Up @@ -359,6 +359,23 @@ void ui_init(void)
pthread_create(&t, NULL, input_thread, NULL);
}

char *ui_copy_image(int icon, int *width, int *height, int *bpp) {
pthread_mutex_lock(&gUpdateMutex);
draw_background_locked(gBackgroundIcon[icon]);
*width = gr_fb_width();
*height = gr_fb_height();
*bpp = sizeof(gr_pixel) * 8;
int size = *width * *height * sizeof(gr_pixel);
char *ret = malloc(size);
if (ret == NULL) {
LOGE("Can't allocate %d bytes for image\n", size);
} else {
memcpy(ret, gr_fb_data(), size);
}
pthread_mutex_unlock(&gUpdateMutex);
return ret;
}

void ui_set_background(int icon)
{
pthread_mutex_lock(&gUpdateMutex);
Expand Down

2 comments on commit 107629b

@yyjdelete
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The
draw_background_locked(gBackgroundIcon[icon]);
should be
draw_background_locked(icon);
after
commit 6809c51

@koush
Copy link
Author

@koush koush commented on 107629b Nov 28, 2012

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yyjdelete thanks!

Please sign in to comment.