Cómo mover fastboot al espacio del usuario

Fastboot es el nombre de un módulo y un modo del bootloader. Android 10 y las versiones posteriores admiten particiones redimensionables mediante la reubicación de la implementación de fastboot del bootloader al espacio de usuario. Esta reubicación permite mover el código de escritura en la memoria flash a una ubicación común que se pueda mantener y probar con solo las partes específicas del proveedor de fastboot implementadas por una capa de abstracción de hardware (HAL). Además, Android 12 y las versiones posteriores admiten la escritura en la memoria flash de ramdisks a través de un comando fastboot agregado.

Unifica fastboot y recuperación

Debido a que fastboot y la recuperación del espacio de usuario son similares, puedes combinarlos en una partición o un objeto binario. Esto proporciona ventajas, como usar menos espacio, tener menos particiones en general y que fastboot y la recuperación compartan su kernel y sus bibliotecas.

Fastbootd es el nombre de un daemon y un modo del espacio de usuario. Para admitir fastbootd, el bootloader debe implementar un nuevo comando de bloque de control de inicio (BCB) de boot-fastboot. Para ingresar al modo fastbootd, el bootloader escribe boot-fastboot en el campo de comando del mensaje BCB y deja el campo recovery de BCB sin cambios (para permitir el reinicio de cualquier tarea de recuperación interrumpida). Los campos status, stage y reserved también permanecen sin cambios. El bootloader carga la imagen de recuperación y se inicia en ella cuando ve boot-fastboot en el campo de comando BCB. Luego, la recuperación analiza el mensaje BCB y cambia al modo fastbootd.

Comandos de ADB

En esta sección, se describe el comando adb para integrar fastbootd. El comando tiene diferentes resultados, según si lo ejecuta el sistema o la recuperación.

Comando Descripción
reboot fastboot
  • Se reinicia en fastbootd (sistema).
  • Ingresa a fastbootd directamente sin reiniciar (recuperación).

Comandos de Fastboot

En esta sección, se describen los comandos de fastboot para integrar fastbootd, incluidos los comandos nuevos para escribir en la memoria flash y administrar particiones lógicas. Algunos comandos tienen diferentes resultados, según si los ejecutó el bootloader o fastbootd.

Comando Descripción
reboot recovery
  • Se reinicia en la recuperación (bootloader).
  • Ingresa a la recuperación directamente sin reiniciar (fastbootd).
reboot fastboot Se reinicia en fastbootd.
getvar is-userspace
  • Muestra yes (fastbootd).
  • Muestra no (bootloader).
getvar is-logical:<partition> Muestra yes si la partición determinada es una partición lógica; de lo contrario, muestra no. Las particiones lógicas admiten todos los comandos que se indican a continuación.
getvar super-partition-name Muestra el nombre de la superpartición. El nombre incluye el sufijo de ranura actual si la superpartición es una partición A/B (por lo general, no lo es).
create-logical-partition <partition> <size> Crea una partición lógica con el nombre y el tamaño determinados. El nombre no debe existir como una partición lógica.
delete-logical-partition <partition> Borra la partición lógica determinada (efectivamente, borra la partición).
resize-logical-partition <partition> <size> Cambia el tamaño de la partición lógica al nuevo tamaño sin cambiar su contenido. Falla si no hay suficiente espacio disponible para realizar el cambio de tamaño.
flash <partition><filename> ] Escribe un archivo en una partición de memoria flash. El dispositivo debe estar desbloqueado.
erase <partition> Borra una partición (no es necesario que sea un borrado seguro). El dispositivo debe estar desbloqueado.
getvar <variable> | all Muestra una variable del bootloader o todas las variables. Si la variable no existe, muestra un error.
set_active <slot>

Establece la ranura de inicio A/B determinada como active. En el próximo intento de inicio, el sistema se iniciará desde la ranura especificada.

Para la compatibilidad con A/B, las ranuras son conjuntos duplicados de particiones desde las que se puede iniciar de forma independiente. Las ranuras se denominan a, b, etcétera, y se diferencian agregando los sufijos _a, _b, etcétera, al nombre de la partición.

reboot Reinicia el dispositivo de forma normal.
reboot-bootloader (o reboot bootloader) Reinicia el dispositivo en el bootloader.
fastboot fetch vendor_boot <out.img>

Se usa en Android 12 y versiones posteriores para admitir la escritura en la memoria flash de ramdisks del proveedor.

Obtiene el tamaño completo de la partición y el tamaño del fragmento. Obtiene datos para cada fragmento, luego, los une a <out.img>.

Para obtener más información, consulta fastboot fetch vendor_boot <out.img>.

fastboot flash vendor_boot:default <vendor-ramdisk.img>

Se usa en Android 12 y versiones posteriores para admitir la escritura en la memoria flash de ramdisks del proveedor.

Esta es una variante especial del comando flash. Realiza una función de imagen fetch vendor_boot, como si se llamara a fastboot fetch. La nueva imagen vendor_boot que escribe en la memoria flash depende de si la versión del encabezado de inicio es la versión 3 o la versión 4.

Para obtener más información, consulta fastboot flash vendor_boot:default <vendor-ramdisk.img>.

fastboot flash vendor_boot:<foo> <vendor-ramdisk.img> Se usa en Android 12 y versiones posteriores para admitir la escritura en la memoria flash de ramdisks del proveedor.

Recupera la imagen vendor_boot. Muestra un error si el encabezado de inicio del proveedor es la versión 3. Si es la versión 4, encuentra el fragmento de ramdisk del proveedor correcto (si está disponible). Lo reemplaza por la imagen determinada, vuelve a calcular los tamaños y los desplazamientos, y escribe en la memoria flash de la nueva vendor_boot image.

Para obtener más información, consulta fastboot flash vendor_boot:<foo> <vendor-ramdisk.img>

Fastboot y bootloader

El bootloader escribe en la memoria flash de las particiones bootloader, radio y boot/recovery, después de lo cual el dispositivo se inicia en fastboot (espacio de usuario) y escribe en la memoria flash de todas las demás particiones. El bootloader debe admitir los siguientes comandos.

Comando Descripción
download Descarga la imagen para escribir en la memoria flash.
flash recovery <image>/ flash boot <image>/ flash bootloader <image>/ Escribe en la memoria flash de la partición recovery/boot y el bootloader.
reboot Reinicia el dispositivo.
reboot fastboot Se reinicia en fastboot.
reboot recovery Se reinicia en la recuperación.
getvar Obtiene una variable del bootloader que es necesaria para escribir en la memoria flash de la imagen de recuperación o de inicio (por ejemplo, current-slot y max-download-size).
oem <command> Comando definido por el OEM.

Particiones dinámicas

El bootloader no debe permitir la escritura en la memoria flash ni el borrado de particiones dinámicas y debe mostrar un error si se intentan estas operaciones. En el caso de los dispositivos con particiones dinámicas adaptadas, la herramienta de fastboot (y el bootloader) admite un modo forzado para escribir directamente en la memoria flash de una partición dinámica mientras está en modo bootloader. Por ejemplo, si system es una partición dinámica en el dispositivo adaptado, usar el comando fastboot --force flash system permite que el bootloader (en lugar de fastbootd) escriba en la memoria flash de la partición.

Carga en modo apagado

Si un dispositivo admite la carga en modo apagado o se inicia automáticamente en un modo especial cuando se aplica la alimentación, una implementación del comando fastboot oem off-mode-charge 0 debe omitir estos modos especiales para que el dispositivo se inicie como si el usuario hubiera presionado el botón de encendido.

HAL de Fastboot OEM

Para reemplazar por completo el fastboot del bootloader, fastboot debe controlar todos los comandos de fastboot existentes. Muchos de estos comandos son de OEMs y están documentados, pero requieren una implementación personalizada. Muchos comandos específicos del OEM no están documentados. Para controlar esos comandos, el HAL de fastboot especifica los comandos OEM necesarios. Los OEMs también pueden implementar sus propios comandos.

La definición de HAL de fastboot es la siguiente:

import IFastbootLogger;

/**
 * IFastboot interface implements vendor specific fastboot commands.
 */
interface IFastboot {
    /**
     * Returns a bool indicating whether the bootloader is enforcing verified
     * boot.
     *
     * @return verifiedBootState True if the bootloader is enforcing verified
     * boot and False otherwise.
     */
    isVerifiedBootEnabled() generates (bool verifiedBootState);

    /**
     * Returns a bool indicating the off-mode-charge setting. If off-mode
     * charging is enabled, the device autoboots into a special mode when
     * power is applied.
     *
     * @return offModeChargeState True if the setting is enabled and False if
     * not.
     */
    isOffModeChargeEnabled() generates (bool offModeChargeState);

    /**
     * Returns the minimum battery voltage required for flashing in mV.
     *
     * @return batteryVoltage Minimum battery voltage (in mV) required for
     * flashing to be successful.
     */
    getBatteryVoltageFlashingThreshold() generates (int32_t batteryVoltage);

    /**
     * Returns the file system type of the partition. This is only required for
     * physical partitions that need to be wiped and reformatted.
     *
     * @return type Can be ext4, f2fs or raw.
     * @return result SUCCESS if the operation is successful,
     * FAILURE_UNKNOWN if the partition is invalid or does not require
     * reformatting.
     */
    getPartitionType(string partitionName) generates (FileSystemType type, Result result);

    /**
     * Executes a fastboot OEM command.
     *
     * @param oemCmd The oem command that is passed to the fastboot HAL.
     * @response result Returns the status SUCCESS if the operation is
     * successful,
     * INVALID_ARGUMENT for bad arguments,
     * FAILURE_UNKNOWN for an invalid/unsupported command.
     */
    doOemCommand(string oemCmd) generates (Result result);

};

Habilita fastbootd

Para habilitar fastbootd en un dispositivo, haz lo siguiente:

  1. Agrega fastbootd a PRODUCT_PACKAGES en device.mk: PRODUCT_PACKAGES += fastbootd.

  2. Asegúrate de que el HAL de fastboot, el HAL de control de inicio y el HAL de estado estén empaquetados como parte de la imagen de recuperación.

  3. Agrega los permisos de SEPolicy específicos del dispositivo que requiere fastbootd. Por ejemplo, fastbootd requiere acceso de escritura a una partición específica del dispositivo para escribir en la memoria flash de esa partición. Además, la implementación del HAL de fastboot también puede requerir permisos específicos del dispositivo.

Para validar el fastboot del espacio de usuario, ejecuta el Conjunto de pruebas de proveedores (VTS).

Escritura en la memoria flash de discos RAM del proveedor

Android 12 y las versiones posteriores proporcionan compatibilidad para escribir en la memoria flash de ramdisks con un comando fastboot agregado que extrae la imagen vendor_boot completa de un dispositivo. El comando solicita a la herramienta de fastboot del host que lea el encabezado de inicio del proveedor, vuelva a crear la imagen y escriba en la memoria flash de la imagen nueva.

Para extraer la imagen vendor_boot completa, se agregó el comando fetch:vendor_boot tanto al protocolo fastboot como a la implementación de fastbootd del protocolo en Android 12. Ten en cuenta que fastbootd implementa esto, pero es posible que el bootloader no lo haga. Los OEMs pueden agregar el comando fetch:vendor_boot a su implementación del protocolo del bootloader. Sin embargo, si el comando no se reconoce en el modo bootloader, la escritura en la memoria flash de ramdisks individuales del proveedor en el modo bootloader no es una opción compatible con el proveedor.

Cambios en el bootloader

Los comandos getvar:max-fetch-size y fetch:name se implementan en fastbootd. Para admitir la escritura en la memoria flash de ramdisks del proveedor en el bootloader, debes implementar estos dos comandos.

Cambios en fastbootd

getvar:max-fetch-size es similar a max-download-size. Especifica el tamaño máximo que el dispositivo puede enviar en una respuesta DATA. El controlador no debe recuperar un tamaño mayor que este valor.

fetch:name[:offset[:size]] realiza una serie de verificaciones en el dispositivo. Si se cumplen todas las siguientes condiciones, el comando fetch:name[:offset[:size]] muestra datos:

  • El dispositivo ejecuta una compilación depurable.
  • El dispositivo está desbloqueado (estado de inicio naranja).
  • El nombre de la partición recuperada es vendor_boot.
  • El valor de size se encuentra dentro de 0 < size <= max-fetch-size.

Cuando se verifican, fetch:name[:offset[:size]] muestra el tamaño y el desplazamiento de la partición. Ten en cuenta lo siguiente:

  • fetch:name equivale a fetch:name:0, que equivale a fetch:name:0:partition_size.
  • fetch:name:offset equivale a fetch:name:offset:(partition_size - offset)

Por lo tanto, fetch:name[:offset[:size]] = fetch:name:offset:(partition_size - offset).

Cuando no se especifican offset o partition_size (o ambos), se usan los valores predeterminados, que para offset es 0 y para size es el valor calculado de partition_size - offset.

  • Desplazamiento especificado, tamaño no especificado: size = partition_size - offset
  • Ninguno especificado: valores predeterminados usados para ambos, size = partition_size - 0.

Por ejemplo, fetch:foo recupera toda la partición foo en el desplazamiento 0.

Cambios en el controlador

Se agregaron comandos a la herramienta de fastboot para implementar cambios en el controlador. Cada uno está vinculado a su definición completa en la tabla de comandos de Fastboot.

  • fastboot fetch vendor_boot out.img

    • Llama a getvar max-fetch-size para determinar el tamaño del fragmento.
    • Llama a getvar partition-size:vendor_boot[_a] para determinar el tamaño de toda la partición.
    • Llama a fastboot fetch vendor_boot[_a]:offset:size para cada fragmento. (El tamaño del fragmento es mayor que el tamaño de vendor_boot, por lo que, por lo general, solo hay un fragmento).
    • Une los datos a out.img.
  • fastboot flash vendor_boot:default vendor-ramdisk.img

    Esta es una variante especial del comando flash. Recupera la vendor_boot imagen, como si fastboot fetch se llamara.

    • Si el inicio del proveedor es la versión 3 del encabezado, hace lo siguiente:
      • Reemplaza el ramdisk del proveedor por la imagen determinada.
      • Escribe en la memoria flash de la nueva imagen vendor_boot.
    • Si el encabezado de inicio del proveedor es la versión 4, hace lo siguiente:
      • Reemplaza todo el ramdisk del proveedor por la imagen determinada para que esta se convierta en el único fragmento de ramdisk del proveedor en la imagen vendor_boot.
      • Vuelve a calcular el tamaño y el desplazamiento en la tabla de ramdisks del proveedor.
      • Escribe en la memoria flash de la nueva imagen vendor_boot.
  • fastboot flash vendor_boot:foo vendor-ramdisk.img

    Recupera la vendor_boot image, como si se llamara a fastboot fetch.

    • Si el encabezado de inicio del proveedor es la versión 3, muestra un error.
    • Si el encabezado de inicio del proveedor es la versión 4, hace lo siguiente:

      • Busca el fragmento de ramdisk del proveedor con el nombre ramdisk_<var>&lt;foo></var>. Si no se encuentra o si hay varias coincidencias, muestra un error.
      • Reemplaza el fragmento de ramdisk del proveedor por la imagen determinada.
      • Vuelve a calcular cada tamaño y desplazamiento en la tabla de ramdisks del proveedor.
      • Escribe en la memoria flash de la nueva imagen vendor_boot.
    • Si no se especifica <foo>, intenta encontrar ramdisk_.

mkbootimg

El nombre default está reservado para nombrar fragmentos de ramdisk del proveedor en Android 12 y versiones posteriores. Si bien la semántica de fastboot flash vendor_boot:default sigue siendo la misma, no debes nombrar tus fragmentos de ramdisk como default.

Cambios en SELinux

Se realizó un cambio en fastbootd.te para admitir la escritura en la memoria flash de ramdisks del proveedor.