Add fastboot --os-version and --os-patch-level.

Also move -c to --cmdline to match the python mkbootimg tool. Remove
the short options for these rarely-used options.

Remove #if 0 cruft from <bootimg/bootimg.h>.

Bug: http://b/77340848
Test: manual testing (need to fix http://b/77809061 for unit tests)
Change-Id: Ieaa795566611bd89ba3fee39a1d9f6ad0713a587
This commit is contained in:
Elliott Hughes 2018-04-05 16:12:47 -07:00
parent 0289f35b83
commit 577e8b44b6
4 changed files with 199 additions and 211 deletions

View File

@ -34,16 +34,15 @@
#include <stdlib.h>
#include <string.h>
void bootimg_set_cmdline(boot_img_hdr_v1* h, const char* cmdline) {
if (strlen(cmdline) >= sizeof(h->cmdline)) die("command line too large: %zu", strlen(cmdline));
strcpy(reinterpret_cast<char*>(h->cmdline), cmdline);
void bootimg_set_cmdline(boot_img_hdr_v1* h, const std::string& cmdline) {
if (cmdline.size() >= sizeof(h->cmdline)) die("command line too large: %zu", cmdline.size());
strcpy(reinterpret_cast<char*>(h->cmdline), cmdline.c_str());
}
boot_img_hdr_v1* mkbootimg(void* kernel, int64_t kernel_size, off_t kernel_offset, void* ramdisk,
int64_t ramdisk_size, off_t ramdisk_offset, void* second,
int64_t second_size, off_t second_offset, size_t page_size, size_t base,
off_t tags_offset, uint32_t header_version, int64_t* bootimg_size) {
size_t page_mask = page_size - 1;
boot_img_hdr_v1* mkbootimg(void* kernel, int64_t kernel_size, void* ramdisk, int64_t ramdisk_size,
void* second, int64_t second_size, size_t base,
const boot_img_hdr_v1& src, int64_t* bootimg_size) {
const size_t page_mask = src.page_size - 1;
int64_t header_actual = sizeof(boot_img_hdr_v1) & (~page_mask);
int64_t kernel_actual = (kernel_size + page_mask) & (~page_mask);
@ -53,30 +52,26 @@ boot_img_hdr_v1* mkbootimg(void* kernel, int64_t kernel_size, off_t kernel_offse
*bootimg_size = header_actual + kernel_actual + ramdisk_actual + second_actual;
boot_img_hdr_v1* hdr = reinterpret_cast<boot_img_hdr_v1*>(calloc(*bootimg_size, 1));
if (hdr == nullptr) {
return hdr;
}
if (hdr == nullptr) die("couldn't allocate boot image: %" PRId64 " bytes", *bootimg_size);
*hdr = src;
memcpy(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
hdr->kernel_size = kernel_size;
hdr->ramdisk_size = ramdisk_size;
hdr->second_size = second_size;
hdr->kernel_addr = base + kernel_offset;
hdr->ramdisk_addr = base + ramdisk_offset;
hdr->second_addr = base + second_offset;
hdr->tags_addr = base + tags_offset;
hdr->kernel_addr += base;
hdr->ramdisk_addr += base;
hdr->second_addr += base;
hdr->tags_addr += base;
hdr->page_size = page_size;
if (header_version) {
hdr->header_version = header_version;
if (hdr->header_version != 0) {
hdr->header_size = sizeof(boot_img_hdr_v1);
}
memcpy(hdr->magic + page_size, kernel, kernel_size);
memcpy(hdr->magic + page_size + kernel_actual, ramdisk, ramdisk_size);
memcpy(hdr->magic + page_size + kernel_actual + ramdisk_actual, second, second_size);
memcpy(hdr->magic + hdr->page_size, kernel, kernel_size);
memcpy(hdr->magic + hdr->page_size + kernel_actual, ramdisk, ramdisk_size);
memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual, second, second_size);
return hdr;
}

View File

@ -26,17 +26,15 @@
* SUCH DAMAGE.
*/
#ifndef _FASTBOOT_BOOTIMG_UTILS_H_
#define _FASTBOOT_BOOTIMG_UTILS_H_
#pragma once
#include <bootimg.h>
#include <inttypes.h>
#include <sys/types.h>
void bootimg_set_cmdline(boot_img_hdr_v1* h, const char* cmdline);
boot_img_hdr_v1* mkbootimg(void* kernel, int64_t kernel_size, off_t kernel_offset, void* ramdisk,
int64_t ramdisk_size, off_t ramdisk_offset, void* second,
int64_t second_size, off_t second_offset, size_t page_size, size_t base,
off_t tags_offset, uint32_t header_version, int64_t* bootimg_size);
#include <string>
#endif
boot_img_hdr_v1* mkbootimg(void* kernel, int64_t kernel_size, void* ramdisk, int64_t ramdisk_size,
void* second, int64_t second_size, size_t base,
const boot_img_hdr_v1& src, int64_t* bootimg_size);
void bootimg_set_cmdline(boot_img_hdr_v1* h, const std::string& cmdline);

View File

@ -76,9 +76,9 @@ using android::base::unique_fd;
char cur_product[FB_RESPONSE_SZ + 1];
static const char* serial = nullptr;
static const char* cmdline = nullptr;
static unsigned short vendor_id = 0;
static int long_listing = 0;
static bool g_long_listing = false;
// Don't resparse files in too-big chunks.
// libsparse will support INT_MAX, but this results in large allocations, so
// let's keep it at 1GB to avoid memory pressure on the host.
@ -86,12 +86,9 @@ static constexpr int64_t RESPARSE_LIMIT = 1 * 1024 * 1024 * 1024;
static int64_t sparse_limit = -1;
static int64_t target_sparse_limit = -1;
static unsigned page_size = 2048;
static unsigned base_addr = 0x10000000;
static unsigned kernel_offset = 0x00008000;
static unsigned ramdisk_offset = 0x01000000;
static unsigned second_offset = 0x00f00000;
static unsigned tags_offset = 0x00000100;
static unsigned g_base_addr = 0x10000000;
static boot_img_hdr_v1 g_boot_img_hdr = {};
static std::string g_cmdline;
static bool g_disable_verity = false;
static bool g_disable_verification = false;
@ -223,7 +220,7 @@ static int list_devices_callback(usb_ifc_info* info) {
serial = "????????????";
}
// output compatible with "adb devices"
if (!long_listing) {
if (!g_long_listing) {
printf("%s\tfastboot", serial.c_str());
} else {
printf("%-22s fastboot", serial.c_str());
@ -360,14 +357,18 @@ static int show_help() {
" Download and boot kernel from RAM.\n"
" flash:raw PARTITION KERNEL [RAMDISK [SECOND]]\n"
" Create boot image and flash it.\n"
// TODO: give -c a long option, and remove the short options for this group?
" -c CMDLINE Override kernel command line.\n"
" --cmdline CMDLINE Override kernel command line.\n"
" --base ADDRESS Set kernel base address (default: 0x10000000).\n"
" --kernel-offset Set kernel offset (default: 0x00008000).\n"
" --ramdisk-offset Set ramdisk offset (default: 0x01000000).\n"
" --tags-offset Set tags offset (default: 0x00000100).\n"
" --page-size BYTES Set flash page size (default: 2048).\n"
" --header-version VERSION Set boot image header version.\n"
" --os-version MAJOR[.MINOR[.PATCH]]\n"
" Set boot image OS version (default: 0.0.0).\n"
" --os-patch-level YYYY-MM-DD\n"
" Set boot image OS security patch level.\n"
// TODO: still missing: `second_addr`, `name`, `id`, `recovery_dtbo_*`.
"\n"
// TODO: what device(s) used this? is there any documentation?
//" continue Continue with autoboot.\n"
@ -404,8 +405,7 @@ static int show_help() {
}
static void* load_bootable_image(const std::string& kernel, const std::string& ramdisk,
const std::string& second_stage, int64_t* sz,
const char* cmdline, uint32_t header_version) {
const std::string& second_stage, int64_t* sz) {
int64_t ksize;
void* kdata = load_file(kernel.c_str(), &ksize);
if (kdata == nullptr) die("cannot load '%s': %s", kernel.c_str(), strerror(errno));
@ -415,12 +415,14 @@ static void* load_bootable_image(const std::string& kernel, const std::string& r
die("cannot load '%s': too short", kernel.c_str());
}
if (!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
if (cmdline) bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v1*>(kdata), cmdline);
if (!g_cmdline.empty()) {
bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v1*>(kdata), g_cmdline);
}
uint32_t header_version_existing =
reinterpret_cast<boot_img_hdr_v1*>(kdata)->header_version;
if (header_version != header_version_existing) {
if (g_boot_img_hdr.header_version != header_version_existing) {
die("header version mismatch, expected: %" PRIu32 " found %" PRIu32 "",
header_version, header_version_existing);
g_boot_img_hdr.header_version, header_version_existing);
}
if (!ramdisk.empty()) die("cannot boot a boot.img *and* ramdisk");
@ -444,16 +446,12 @@ static void* load_bootable_image(const std::string& kernel, const std::string& r
}
fprintf(stderr,"creating boot image...\n");
int64_t bsize = 0;
boot_img_hdr_v1* bdata = mkbootimg(kdata, ksize, kernel_offset,
rdata, rsize, ramdisk_offset,
sdata, ssize, second_offset,
page_size, base_addr, tags_offset, header_version, &bsize);
boot_img_hdr_v1* bdata = mkbootimg(kdata, ksize, rdata, rsize, sdata, ssize,
g_base_addr, g_boot_img_hdr, sz);
if (bdata == nullptr) die("failed to create boot.img");
if (cmdline) bootimg_set_cmdline(bdata, cmdline);
fprintf(stderr, "creating boot image - %" PRId64 " bytes\n", bsize);
*sz = bsize;
if (!g_cmdline.empty()) bootimg_set_cmdline(bdata, g_cmdline);
fprintf(stderr, "creating boot image - %" PRId64 " bytes\n", *sz);
return bdata;
}
@ -1422,34 +1420,37 @@ int main(int argc, char **argv)
bool skip_secondary = false;
bool set_fbe_marker = false;
void *data;
uint32_t header_version = 0;
int64_t sz;
int longindex;
std::string slot_override;
std::string next_active;
g_boot_img_hdr.kernel_addr = 0x00008000;
g_boot_img_hdr.ramdisk_addr = 0x01000000;
g_boot_img_hdr.second_addr = 0x00f00000;
g_boot_img_hdr.tags_addr = 0x00000100;
g_boot_img_hdr.page_size = 2048;
const struct option longopts[] = {
{"base", required_argument, 0, 'b'},
{"kernel_offset", required_argument, 0, 'k'},
{"kernel-offset", required_argument, 0, 'k'},
{"page_size", required_argument, 0, 'n'},
{"page-size", required_argument, 0, 'n'},
{"ramdisk_offset", required_argument, 0, 'r'},
{"ramdisk-offset", required_argument, 0, 'r'},
{"tags_offset", required_argument, 0, 't'},
{"tags-offset", required_argument, 0, 't'},
{"base", required_argument, 0, 0},
{"cmdline", required_argument, 0, 0},
{"disable-verification", no_argument, 0, 0},
{"disable-verity", no_argument, 0, 0},
{"header-version", required_argument, 0, 0},
{"help", no_argument, 0, 'h'},
{"unbuffered", no_argument, 0, 0},
{"slot", required_argument, 0, 0},
{"set_active", optional_argument, 0, 'a'},
{"kernel-offset", required_argument, 0, 0},
{"os-patch-level", required_argument, 0, 0},
{"os-version", required_argument, 0, 0},
{"page-size", required_argument, 0, 0},
{"ramdisk-offset", required_argument, 0, 0},
{"set-active", optional_argument, 0, 'a'},
{"skip-secondary", no_argument, 0, 0},
{"skip-reboot", no_argument, 0, 0},
{"skip-secondary", no_argument, 0, 0},
{"slot", required_argument, 0, 0},
{"tags-offset", required_argument, 0, 0},
{"unbuffered", no_argument, 0, 0},
{"verbose", no_argument, 0, 'v'},
{"version", no_argument, 0, 0},
{"disable-verity", no_argument, 0, 0},
{"disable-verification", no_argument, 0, 0},
{"header-version", required_argument, 0, 0},
#if !defined(_WIN32)
{"wipe-and-use-fbe", no_argument, 0, 0},
#endif
@ -1458,98 +1459,108 @@ int main(int argc, char **argv)
serial = getenv("ANDROID_SERIAL");
while (1) {
int c = getopt_long(argc, argv, "vwb:k:n:r:t:s:S:lc:i:m:ha::", longopts, &longindex);
if (c < 0) {
break;
}
/* Alphabetical cases */
switch (c) {
case 'a':
wants_set_active = true;
if (optarg)
next_active = optarg;
break;
case 'b':
base_addr = strtoul(optarg, 0, 16);
break;
case 'c':
cmdline = optarg;
break;
case 'h':
return show_help();
case 'i': {
char *endptr = nullptr;
unsigned long val;
val = strtoul(optarg, &endptr, 0);
if (!endptr || *endptr != '\0' || (val & ~0xffff))
die("invalid vendor id '%s'", optarg);
vendor_id = (unsigned short)val;
break;
}
case 'k':
kernel_offset = strtoul(optarg, 0, 16);
break;
case 'l':
long_listing = 1;
break;
case 'n':
page_size = (unsigned)strtoul(optarg, nullptr, 0);
if (!page_size) die("invalid page size");
break;
case 'r':
ramdisk_offset = strtoul(optarg, 0, 16);
break;
case 't':
tags_offset = strtoul(optarg, 0, 16);
break;
case 's':
serial = optarg;
break;
case 'S':
sparse_limit = parse_num(optarg);
if (sparse_limit < 0) die("invalid sparse limit");
break;
case 'v':
set_verbose();
break;
case 'w':
wants_wipe = true;
break;
case '?':
return 1;
case 0:
if (strcmp("unbuffered", longopts[longindex].name) == 0) {
int c;
while ((c = getopt_long(argc, argv, "a::hi:ls:S:vw", longopts, &longindex)) != -1) {
if (c == 0) {
std::string name{longopts[longindex].name};
if (name == "base") {
g_base_addr = strtoul(optarg, 0, 16);
} else if (name == "cmdline") {
g_cmdline = optarg;
} else if (name == "disable-verification") {
g_disable_verification = true;
} else if (name == "disable-verity") {
g_disable_verity = true;
} else if (name == "header-version") {
g_boot_img_hdr.header_version = strtoul(optarg, nullptr, 0);
} else if (name == "kernel-offset") {
g_boot_img_hdr.kernel_addr = strtoul(optarg, 0, 16);
} else if (name == "os-patch-level") {
unsigned year, month, day;
if (sscanf(optarg, "%u-%u-%u", &year, &month, &day) != 3) {
syntax_error("OS patch level should be YYYY-MM-DD: %s", optarg);
}
if (year < 2000 || year >= 2128) syntax_error("year out of range: %d", year);
if (month < 1 || month > 12) syntax_error("month out of range: %d", month);
g_boot_img_hdr.SetOsPatchLevel(year, month);
} else if (name == "os-version") {
unsigned major = 0, minor = 0, patch = 0;
std::vector<std::string> versions = android::base::Split(optarg, ".");
if (versions.size() < 1 || versions.size() > 3 ||
(versions.size() >= 1 && !android::base::ParseUint(versions[0], &major)) ||
(versions.size() >= 2 && !android::base::ParseUint(versions[1], &minor)) ||
(versions.size() == 3 && !android::base::ParseUint(versions[2], &patch)) ||
(major > 0x7f || minor > 0x7f || patch > 0x7f)) {
syntax_error("bad OS version: %s", optarg);
}
g_boot_img_hdr.SetOsVersion(major, minor, patch);
} else if (name == "page-size") {
g_boot_img_hdr.page_size = strtoul(optarg, nullptr, 0);
if (g_boot_img_hdr.page_size == 0) die("invalid page size");
} else if (name == "ramdisk-offset") {
g_boot_img_hdr.ramdisk_addr = strtoul(optarg, 0, 16);
} else if (name == "skip-reboot") {
skip_reboot = true;
} else if (name == "skip-secondary") {
skip_secondary = true;
} else if (name == "slot") {
slot_override = optarg;
} else if (name == "tags-offset") {
g_boot_img_hdr.tags_addr = strtoul(optarg, 0, 16);
} else if (name == "unbuffered") {
setvbuf(stdout, nullptr, _IONBF, 0);
setvbuf(stderr, nullptr, _IONBF, 0);
} else if (strcmp("version", longopts[longindex].name) == 0) {
} else if (name == "version") {
fprintf(stdout, "fastboot version %s\n", FASTBOOT_VERSION);
fprintf(stdout, "Installed as %s\n", android::base::GetExecutablePath().c_str());
return 0;
} else if (strcmp("slot", longopts[longindex].name) == 0) {
slot_override = std::string(optarg);
} else if (strcmp("skip-secondary", longopts[longindex].name) == 0 ) {
skip_secondary = true;
} else if (strcmp("skip-reboot", longopts[longindex].name) == 0 ) {
skip_reboot = true;
} else if (strcmp("disable-verity", longopts[longindex].name) == 0 ) {
g_disable_verity = true;
} else if (strcmp("disable-verification", longopts[longindex].name) == 0 ) {
g_disable_verification = true;
#if !defined(_WIN32)
} else if (strcmp("wipe-and-use-fbe", longopts[longindex].name) == 0) {
} else if (name == "wipe-and-use-fbe") {
wants_wipe = true;
set_fbe_marker = true;
#endif
} else if (strcmp("header-version", longopts[longindex].name) == 0) {
header_version = strtoul(optarg, nullptr, 0);
} else {
die("unknown option %s", longopts[longindex].name);
}
break;
default:
abort();
} else {
switch (c) {
case 'a':
wants_set_active = true;
if (optarg) next_active = optarg;
break;
case 'h':
return show_help();
case 'i':
{
char *endptr = nullptr;
unsigned long val = strtoul(optarg, &endptr, 0);
if (!endptr || *endptr != '\0' || (val & ~0xffff)) {
die("invalid vendor id '%s'", optarg);
}
vendor_id = (unsigned short)val;
break;
}
case 'l':
g_long_listing = true;
break;
case 's':
serial = optarg;
break;
case 'S':
sparse_limit = parse_num(optarg);
if (sparse_limit < 0) die("invalid sparse limit");
break;
case 'v':
set_verbose();
break;
case 'w':
wants_wipe = true;
break;
case '?':
return 1;
default:
abort();
}
}
}
@ -1668,7 +1679,7 @@ int main(int argc, char **argv)
std::string second_stage;
if (!args.empty()) second_stage = next_arg(&args);
data = load_bootable_image(kernel, ramdisk, second_stage, &sz, cmdline, header_version);
data = load_bootable_image(kernel, ramdisk, second_stage, &sz);
fb_queue_download("boot.img", data, sz);
fb_queue_command("boot", "booting");
} else if (command == "flash") {
@ -1694,7 +1705,7 @@ int main(int argc, char **argv)
std::string second_stage;
if (!args.empty()) second_stage = next_arg(&args);
data = load_bootable_image(kernel, ramdisk, second_stage, &sz, cmdline, header_version);
data = load_bootable_image(kernel, ramdisk, second_stage, &sz);
auto flashraw = [&](const std::string& partition) {
fb_queue_flash(partition, data, sz);
};

View File

@ -1,37 +1,33 @@
/* tools/mkbootimg/bootimg.h
**
** Copyright 2007, 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.
*/
/*
* Copyright (C) 2007 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.
*/
#pragma once
#include <stdint.h>
#ifndef _BOOT_IMAGE_H_
#define _BOOT_IMAGE_H_
#define BOOT_MAGIC "ANDROID!"
#define BOOT_MAGIC_SIZE 8
#define BOOT_NAME_SIZE 16
#define BOOT_ARGS_SIZE 512
#define BOOT_EXTRA_ARGS_SIZE 1024
#define BOOT_HEADER_VERSION_ZERO 0
/*
* Bootloader expects the structure of boot_img_hdr with header version
* BOOT_HEADER_VERSION_ZERO to be as follows:
*/
// The bootloader expects the structure of boot_img_hdr with header
// version 0 to be as follows:
struct boot_img_hdr_v0 {
// Must be BOOT_MAGIC.
uint8_t magic[BOOT_MAGIC_SIZE];
uint32_t kernel_size; /* size in bytes */
@ -45,26 +41,36 @@ struct boot_img_hdr_v0 {
uint32_t tags_addr; /* physical addr for kernel tags */
uint32_t page_size; /* flash page size we assume */
/*
* version for the boot image header.
*/
// Version of the boot image header.
uint32_t header_version;
/* operating system version and security patch level; for
* version "A.B.C" and patch level "Y-M-D":
* ver = A << 14 | B << 7 | C (7 bits for each of A, B, C)
* lvl = ((Y - 2000) & 127) << 4 | M (7 bits for Y, 4 bits for M)
* os_version = ver << 11 | lvl */
// Operating system version and security patch level.
// For version "A.B.C" and patch level "Y-M-D":
// (7 bits for each of A, B, C; 7 bits for (Y-2000), 4 bits for M)
// os_version = A[31:25] B[24:18] C[17:11] (Y-2000)[10:4] M[3:0]
uint32_t os_version;
#if __cplusplus
void SetOsVersion(unsigned major, unsigned minor, unsigned patch) {
os_version &= ((1 << 11) - 1);
os_version |= (((major & 0x7f) << 25) | ((minor & 0x7f) << 18) | ((patch & 0x7f) << 11));
}
void SetOsPatchLevel(unsigned year, unsigned month) {
os_version &= ~((1 << 11) - 1);
os_version |= (((year - 2000) & 0x7f) << 4) | ((month & 0xf) << 0);
}
#endif
uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
uint8_t cmdline[BOOT_ARGS_SIZE];
uint32_t id[8]; /* timestamp / checksum / sha1 / etc */
/* Supplemental command line data; kept here to maintain
* binary compatibility with older versions of mkbootimg */
// Supplemental command line data; kept here to maintain
// binary compatibility with older versions of mkbootimg.
uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
} __attribute__((packed));
@ -74,7 +80,7 @@ struct boot_img_hdr_v0 {
*/
typedef struct boot_img_hdr_v0 boot_img_hdr;
/* When a boot header is of version BOOT_HEADER_VERSION_ZERO, the structure of boot image is as
/* When a boot header is of version 0, the structure of boot image is as
* follows:
*
* +-----------------+
@ -103,15 +109,13 @@ typedef struct boot_img_hdr_v0 boot_img_hdr;
* else: jump to kernel_addr
*/
#define BOOT_HEADER_VERSION_ONE 1
struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
uint32_t recovery_dtbo_size; /* size in bytes for recovery DTBO image */
uint64_t recovery_dtbo_offset; /* physical load addr */
uint32_t header_size;
} __attribute__((packed));
/* When the boot image header has a version of BOOT_HEADER_VERSION_ONE, the structure of the boot
/* When the boot image header has a version of 1, the structure of the boot
* image is as follows:
*
* +-----------------+
@ -142,23 +146,3 @@ struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
* 7. if second_size != 0: jump to second_addr
* else: jump to kernel_addr
*/
#if 0
typedef struct ptentry ptentry;
struct ptentry {
char name[16]; /* asciiz partition name */
unsigned start; /* starting block number */
unsigned length; /* length in blocks */
unsigned flags; /* set to zero */
};
/* MSM Partition Table ATAG
**
** length: 2 + 7 * n
** atag: 0x4d534d70
** <ptentry> x n
*/
#endif
#endif