From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from list by lists.gnu.org with archive (Exim 4.71) id 1aQFSH-0002Ml-8k for mharc-grub-devel@gnu.org; Mon, 01 Feb 2016 09:27:49 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:60364) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aQFSD-0002MD-M6 for grub-devel@gnu.org; Mon, 01 Feb 2016 09:27:47 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1aQFS8-0002LB-QP for grub-devel@gnu.org; Mon, 01 Feb 2016 09:27:45 -0500 Received: from sub5.mail.dreamhost.com ([208.113.200.129]:53899 helo=homiemail-a82.g.dreamhost.com) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aQFS8-0002L5-Eo for grub-devel@gnu.org; Mon, 01 Feb 2016 09:27:40 -0500 Received: from homiemail-a82.g.dreamhost.com (localhost [127.0.0.1]) by homiemail-a82.g.dreamhost.com (Postfix) with ESMTP id 65AAC282074 for ; Mon, 1 Feb 2016 06:27:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=shealevy.com; h= mime-version:content-type:date:from:to:subject:in-reply-to :references:message-id:content-transfer-encoding; s=shealevy.com ; bh=LF9Lcynprhb0OssEnH9m4EMJvww=; b=DC8rEKC1fi51DwFryOO456U29eP LXb1vErZjgtq5pRrh0jW/xyrIyyCIFYUP60f8KveiA1nGrtc6WLQ7PyW7nktpQjh CzhspBV7bobcuZkUUDrfFOX83UdkIPXpAoneBbkDOYyA487C6meG4QiFZg5Z5S7M WSlkZpaUthuR1Nbg= Received: from webmail.shealevy.com (caiajhbihbdd.dreamhost.com [208.97.187.133]) (Authenticated sender: shea@shealevy.com) by homiemail-a82.g.dreamhost.com (Postfix) with ESMTPA id 61672282061 for ; Mon, 1 Feb 2016 06:27:39 -0800 (PST) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8; format=flowed Date: Mon, 01 Feb 2016 09:27:38 -0500 From: Shea Levy To: Subject: Re: [PATCH] Add module for interacting with the android bootloader control block In-Reply-To: <56AEDC80.7020301@gmail.com> References: <1454281263-19141-1-git-send-email-shea@shealevy.com> <56AEDC80.7020301@gmail.com> Message-ID: <024e397cbfee76eed796ac75b29c5fa5@shealevy.com> X-Sender: shea@shealevy.com User-Agent: Roundcube Webmail/0.8.2 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: Windows NT kernel [generic] [fuzzy] X-Received-From: 208.113.200.129 X-BeenThere: grub-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list Reply-To: The development of GNU GRUB List-Id: The development of GNU GRUB List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 01 Feb 2016 14:27:47 -0000 Hi Andrei, There's no formal specification for any of this, all I have to go off=20 of is the bits of the android codebase I've read, but I'll try to answer=20 as best I can. On 2016-01-31 23:18, Andrei Borzenkov wrote: > 01.02.2016 02:55, Shea Levy =D0=BF=D0=B8=D1=88=D0=B5=D1=82: >> >> >> Android systems use a so-called "bootloader control block" to >> coordinate between the bootloader and the recovery system (which=20 >> handles >> factory resets, system updates, firmware updates, etc.). The BCB is >> traditionally either an MTD device or an eMMC partition, and it=20 >> contains >> a struct bootloader_message directly on the device/partition. The >> recovery system will write a message into the control block, which=20 >> the >> bootloader is then meant to check to see if it should boot into=20 >> recovery >> mode or normal mode (the idea being that the control block is only >> cleared once recovery is finished, ensuring fault-tolerance; if the >> device is powered off mid-update, it will boot back into the same >> recovery mode). >> >> This module exports the bcb fields as env variables. >> First the user sets android_bcb_disk to the partition containing the=20 >> BCB >> (I don't know how to support MTDs yet but I only deal with x86).=20 >> Then >> variables such as android_bcb_command and android_bcb_recovery can=20 >> be >> checked and used to determine which mode to boot into. They can=20 >> also, if >> necessary, be modified by setting the variables in question. The=20 >> typical >> use case will probably be to check if there is a recovery command >> present and if so boot into recovery mode. >> >> Does that explain enough? >> > > How these variables are intended to be used? Could you provide=20 > example > of actual values in this block and example of grub code using them? > The recovery service that's started when booted into recovery mode=20 reads values out of the BCB to determine its operation. For example, it=20 checks that the first (newline-separated) string in the 'recovery' is=20 field is "recovery", and then uses the rest to set its argv (e.g. a=20 recovery field consisting of "recovery\n--wipe_data\0" would specify a=20 factory reset). The app itself writes in "boot-recovery" into the=20 "command" field and copies over its arguments, so that upon reboot the=20 command will be started over again (until the command actually finishes,=20 when it zeroes out the BCB). > > How BCB area is identified? Your code allows writing to arbitrary=20 > device > without any sanity check. > On the android userspace side there's only a recovery.fstab file, which=20 just names a block device. The code simply reads a struct out of the=20 block with no magic number or other kind of verification. Sometimes=20 there's not even a real partition table, just a config file with an=20 offset into a block. > > Is bootloader even *allowed* to make any choice here to remain > compliant? I.e. can bootloader ignore request stored in BCB? > I have been unable to find any formal spec on the matter, but at least=20 respecting the "boot-recovery" command would probably be a good idea for=20 stock android. But this seems more like policy than mechanism, and IMO=20 makes more sense to be determined at the grub.cfg level rather than the=20 module level. ~Shea > >> >> Thanks, >> Shea >> >> On 2016-01-31 18:41, Vladimir 'phcoder' Serbinenko >> wrote: >> >>> Sorry but I don't quite understand what this module is for. >> Can you please give an example of how it's used both in original=20 >> context >> and in GRUB. I have couple of style comments as well but clarifying=20 >> the >> usecase first is important. >>> >>> Le lun. 1 f=C3=A9vr. 2016 00:01, Shea Levy >> a =C3=A9crit : >>> >>>> --- >>>> grub-core/Makefile.core.def >> | 5 ++ >>>> grub-core/android/android_bcb.c | 163 >> ++++++++++++++++++++++++++++++++++++++++ >>>> 2 files changed, 168 >> insertions(+) >>>> create mode 100644 grub-core/android/android_bcb.c >>>> >> >>>> diff --git a/grub-core/Makefile.core.def >> b/grub-core/Makefile.core.def >>>> index 0cc40bb..b045ea4 100644 >>>> --- >> a/grub-core/Makefile.core.def >>>> +++ b/grub-core/Makefile.core.def >>>> @@ >> -2327,3 +2327,8 @@ module =3D { >>>> common =3D loader/i386/xen_file64.c; >>>> >> extra_dist =3D loader/i386/xen_fileXX.c; >>>> }; >>>> + >>>> +module =3D { >>>> + >> name =3D android_bcb; >>>> + common =3D android/android_bcb.c; >>>> +}; >>>> diff >> --git a/grub-core/android/android_bcb.c >> b/grub-core/android/android_bcb.c >>>> new file mode 100644 >>>> index >> 0000000..db49446 >>>> --- /dev/null >>>> +++ >> b/grub-core/android/android_bcb.c >>>> @@ -0,0 +1,163 @@ >>>> +/* >> android_bcb.c - module for interacting with the android bootloader >> control block */ >>>> +/* >>>> + * GRUB -- GRand Unified Bootloader >>>> + * >> Copyright (C) 2016 Free Software Foundation, Inc. >>>> + * >>>> + * GRUB is >> free software: you can redistribute it and/or modify >>>> + * it under the >> terms of the GNU General Public License as published by >>>> + * the Free >> Software Foundation, either version 3 of the License, or >>>> + * (at your >> option) any later version. >>>> + * >>>> + * GRUB is distributed in the hope >> that it will be useful, >>>> + * but WITHOUT ANY WARRANTY; without even >> the implied warranty of >>>> + * MERCHANTABILITY or FITNESS FOR A >> PARTICULAR PURPOSE. See the >>>> + * GNU General Public License for more >> details. >>>> + * >>>> + * You should have received a copy of the GNU >> General Public License >>>> + * along with GRUB. If not, see >> . >>>> + */ >>>> + >>>> +#include >> >>>> + >>>> +#include >>>> +#include >>>> >> +#include >>>> + >>>> +GRUB_MOD_LICENSE ("GPLv3+"); >>>> + >>>> >> +/* Definition of struct bootloader message from >>=20 >> https://android.googlesource.com/platform/bootable/recovery/+/9d72d417= 5b06a70c64c8867ff65b3c4c2181c9a9/bootloader.h#20 >> [2] >>>> + * Available under the following copyright and terms: >>>> + * >>>> >> + * 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 [3] >>>> + * >>>> + * 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. >>>> + */ >>>> +struct >> bootloader_message >>>> +{ >>>> + char command[32]; >>>> + char status[32]; >>>> >> + char recovery[768]; >>>> + // The 'recovery' field used to be 1024 >> bytes. It has only ever >>>> + // been used to store the recovery command >> line, so 768 bytes >>>> + // should be plenty. We carve off the last 256 >> bytes to store the >>>> + // stage string (for multistage packages) and >> possible future >>>> + // expansion. >>>> + char stage[32]; >>>> + char >> slot_suffix[32]; >>>> + char reserved[192]; >>>> +} GRUB_PACKED; >>>> + >>>> >> +struct bcb_state >>>> +{ >>>> + grub_disk_t disk; >>>> + char >> internal_write; >>>> + struct bootloader_message msg; >>>> +}; >>>> + >>>> >> +static struct bcb_state state =3D { 0 }; >>>> + >>>> +#define >> SET_FIELD(field) >>>> + state.msg. field[sizeof state.msg. field - 1] =3D >> ''; >>>> + grub_env_set ("android_bcb_" #field, state.msg. field) >>>> + >>>> >> +static void open_disk (const char *name) { >>>> + if (state.disk) { >>>> + >> grub_disk_close (state.disk); >>>> + } >>>> + >>>> + state.disk =3D >> grub_disk_open (name); >>>> + if (state.disk) { >>>> + grub_err_t err =3D >> grub_disk_read (state.disk, 0, 0, sizeof state.msg, >>>> + &state.msg); >>>> >> + if (err) { >>>> + grub_disk_close (state.disk); >>>> + state.disk =3D 0; >>>> >> + } else { >>>> + state.internal_write =3D 1; >>>> + SET_FIELD (command); >>>> + >> SET_FIELD (status); >>>> + SET_FIELD (recovery); >>>> + SET_FIELD >> (stage); >>>> + SET_FIELD (slot_suffix); >>>> + state.internal_write =3D 0; >>>> >> + } >>>> + } >>>> +} >>>> + >>>> +#define MAYBE_UPDATE_FIELD(field) >>>> + do { >> if (!grub_strcmp (var->name, "android_bcb_" #field)) >>>> + { >>>> + >> grub_memcpy (state.msg. field, val, sizeof state.msg. field - 1); >>>> + >> state.msg. field[sizeof state.msg. field - 1] =3D ''; >>>> + >> grub_disk_write (state.disk, 0, >>>> + offsetof (struct >> bootloader_message, field), >>>> + sizeof state.msg. field, state.msg. >> field); >>>> + } } while (0) >>>> + >>>> +static char *handle_write (struct >> grub_env_var *var, >>>> + const char *val) >>>> +{ >>>> + if (!grub_strcmp >> (var->name, "android_bcb_disk")) >>>> + { >>>> + open_disk (val); >>>> + if >> (!state.disk) >>>> + grub_print_error (); >>>> + } >>>> + else if (state.disk >> && !state.internal_write) >>>> + { >>>> + MAYBE_UPDATE_FIELD (command); >>>> + >> MAYBE_UPDATE_FIELD (status); >>>> + MAYBE_UPDATE_FIELD (recovery); >>>> + >> MAYBE_UPDATE_FIELD (stage); >>>> + MAYBE_UPDATE_FIELD (slot_suffix); >>>> + >> } >>>> + >>>> + return grub_strdup (val); >>>> +} >>>> + >>>> >> +GRUB_MOD_INIT(android_bcb) >>>> +{ >>>> + const char *disk =3D grub_env_get >> ("android_bcb_disk"); >>>> + if (disk) >>>> + { >>>> + open_disk (disk); >>>> + >> if (!state.disk) >>>> + grub_print_error (); >>>> + } >>>> + >>>> + if >> (!grub_register_variable_hook ("android_bcb_disk", 0, handle_write)) >>>> >> + grub_print_error (); >>>> + if (grub_register_variable_hook >> ("android_bcb_command", 0, handle_write)) >>>> + grub_print_error (); >>>> + >> if (grub_register_variable_hook ("android_bcb_status", 0, >> handle_write)) >>>> + grub_print_error (); >>>> + if >> (grub_register_variable_hook ("android_bcb_recovery", 0, >> handle_write)) >>>> + grub_print_error (); >>>> + if >> (grub_register_variable_hook ("android_bcb_stage", 0, handle_write)) >>>> >> + grub_print_error (); >>>> + if (grub_register_variable_hook >> ("android_bcb_slot_suffix", 0, handle_write)) >>>> + grub_print_error >> (); >>>> +} >>>> + >>>> +GRUB_MOD_FINI(android_bcb) >>>> +{ >>>> + >> grub_register_variable_hook ("android_bcb_disk", 0, 0); >>>> + >> grub_register_variable_hook ("android_bcb_command", 0, 0); >>>> + >> grub_register_variable_hook ("android_bcb_status", 0, 0); >>>> + >> grub_register_variable_hook ("android_bcb_recovery", 0, 0); >>>> + >> grub_register_variable_hook ("android_bcb_stage", 0, 0); >>>> + >> grub_register_variable_hook ("android_bcb_slot_suffix", 0, 0); >>>> + >>>> + >> if (state.disk) >>>> + grub_disk_close (state.disk); >>>> +} >>>> -- >>>> >> 2.7.0 >>>> >>>> _______________________________________________ >>>> >> Grub-devel mailing list >>>> Grub-devel@gnu.org >>>> >> https://lists.gnu.org/mailman/listinfo/grub-devel [4] >> >> >> >> >> Links: >> ------ >> [1] http://www.gnu.org/licenses/ >> [2] >>=20 >> https://android.googlesource.com/platform/bootable/recovery/+/9d72d417= 5b06a70c64c8867ff65b3c4c2181c9a9/bootloader.h#20 >> [3] >> http://www.apache.org/licenses/LICENSE-2.0 >> [4] >> https://lists.gnu.org/mailman/listinfo/grub-devel >> >> >> >> _______________________________________________ >> Grub-devel mailing list >> Grub-devel@gnu.org >> https://lists.gnu.org/mailman/listinfo/grub-devel >> > > > _______________________________________________ > Grub-devel mailing list > Grub-devel@gnu.org > https://lists.gnu.org/mailman/listinfo/grub-devel