[RFC PATCH v2 0/2] Introduce fastboot oem board command

Changes v2 since v1 at [1]: - Added an example of using the command as requsted by Sean Anderson [2].
Links: [1] https://lore.kernel.org/all/20231228152522.83291-1-avromanov@salutedevices.c... [2] https://lore.kernel.org/all/72ac233d-c18d-4f57-bc66-451fe0bd2997@seco.com/
Alexey Romanov (2): fastboot: introduce 'oem board' subcommand board: ad401: example of fastboot oem board realization
board/amlogic/ad401/fastboot.c | 222 +++++++++++++++++++++++++++++++++ drivers/fastboot/Kconfig | 7 ++ drivers/fastboot/fb_command.c | 15 +++ include/fastboot.h | 1 + 4 files changed, 245 insertions(+) create mode 100644 board/amlogic/ad401/fastboot.c

Currently, fastboot protocol in U-Boot has no opportunity to execute vendor custom code with verifed boot. This patch introduce new fastboot subcommand fastboot oem board:<cmd>, which allow to run custom oem_board function.
Default implementation is __weak. Vendor must redefine it in board/ folder with his own logic.
For example, some vendors have their custom nand/emmc partition flashing or erasing. Here some typical command for such use cases:
- flashing:
$ fastboot stage bootloader.img $ fastboot oem board:write_bootloader
- erasing:
$ fastboot oem board:erase_env
Signed-off-by: Alexey Romanov avromanov@salutedevices.com --- drivers/fastboot/Kconfig | 7 +++++++ drivers/fastboot/fb_command.c | 15 +++++++++++++++ include/fastboot.h | 1 + 3 files changed, 23 insertions(+)
diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig index a4313d60a9..4d94391a76 100644 --- a/drivers/fastboot/Kconfig +++ b/drivers/fastboot/Kconfig @@ -241,6 +241,13 @@ config FASTBOOT_OEM_RUN this feature if you are using verified boot, as it will allow an attacker to bypass any restrictions you have in place.
+config FASTBOOT_OEM_BOARD + bool "Enable the 'oem board' command" + help + This extends the fastboot protocol with an "oem board" command. This + command allows running vendor custom code defined in board/ files. + Otherwise, it will do nothing and send fastboot fail. + endif # FASTBOOT
endmenu diff --git a/drivers/fastboot/fb_command.c b/drivers/fastboot/fb_command.c index 5fcadcdf50..2298815770 100644 --- a/drivers/fastboot/fb_command.c +++ b/drivers/fastboot/fb_command.c @@ -40,6 +40,7 @@ static void reboot_recovery(char *, char *); static void oem_format(char *, char *); static void oem_partconf(char *, char *); static void oem_bootbus(char *, char *); +static void oem_board(char *, char *); static void run_ucmd(char *, char *); static void run_acmd(char *, char *);
@@ -107,6 +108,10 @@ static const struct { .command = "oem run", .dispatch = CONFIG_IS_ENABLED(FASTBOOT_OEM_RUN, (run_ucmd), (NULL)) }, + [FASTBOOT_COMMAND_OEM_BOARD] = { + .command = "oem board", + .dispatch = CONFIG_IS_ENABLED(FASTBOOT_OEM_BOARD, (oem_board), (NULL)) + }, [FASTBOOT_COMMAND_UCMD] = { .command = "UCmd", .dispatch = CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT, (run_ucmd), (NULL)) @@ -490,3 +495,13 @@ static void __maybe_unused oem_bootbus(char *cmd_parameter, char *response) else fastboot_okay(NULL, response); } + +void __weak fastboot_oem_board(char *cmd_parameter, void *data, u32 size, char *response) +{ + fastboot_fail("oem board function not defined", response); +} + +static void __maybe_unused oem_board(char *cmd_parameter, char *response) +{ + fastboot_oem_board(cmd_parameter, fastboot_buf_addr, image_size, response); +} diff --git a/include/fastboot.h b/include/fastboot.h index 296451f89d..06c1f26b6c 100644 --- a/include/fastboot.h +++ b/include/fastboot.h @@ -37,6 +37,7 @@ enum { FASTBOOT_COMMAND_OEM_PARTCONF, FASTBOOT_COMMAND_OEM_BOOTBUS, FASTBOOT_COMMAND_OEM_RUN, + FASTBOOT_COMMAND_OEM_BOARD, FASTBOOT_COMMAND_ACMD, FASTBOOT_COMMAND_UCMD, FASTBOOT_COMMAND_COUNT

Hi Alexey,
Thank you for the patch.
On jeu., févr. 01, 2024 at 12:20, Alexey Romanov avromanov@salutedevices.com wrote:
Currently, fastboot protocol in U-Boot has no opportunity to execute vendor custom code with verifed boot. This patch introduce new fastboot subcommand fastboot oem board:<cmd>, which allow to run custom oem_board function.
Default implementation is __weak. Vendor must redefine it in board/ folder with his own logic.
For example, some vendors have their custom nand/emmc partition flashing or erasing. Here some typical command for such use cases:
flashing:
$ fastboot stage bootloader.img $ fastboot oem board:write_bootloader
erasing:
$ fastboot oem board:erase_env
Signed-off-by: Alexey Romanov avromanov@salutedevices.com
Sorry for the delay. I needed time to give this some thoughts and I waited for Sean to chime as well on this.
I've heard from Neil that this might be related to: https://github.com/superna9999/pyamlboot/pull/20
I think this can be useful. Not necessarily for writing custom partitions, but I see this could be used for other things:
1. Provision SoC-specific fuses (serialno/mac addr) at the factory line (for production devices) Examples: $ fastboot oem board:write_serialno ABCDEF $ fastboot oem board:write_macaddr AA:BB:CC:DD:EE
2. Access secure storage (via an Trusted Application)
But both examples could also be in a fastboot flash format: $ fastboot flash serialno ABCDEF
One concern I have is that U-Boot forks might use this command as an excuse to not makes things generic.
I hope that others will chime in on this as well. I'd like to discuss this more because once this command is in we cannot remove it later.
drivers/fastboot/Kconfig | 7 +++++++ drivers/fastboot/fb_command.c | 15 +++++++++++++++ include/fastboot.h | 1 + 3 files changed, 23 insertions(+)
diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig index a4313d60a9..4d94391a76 100644 --- a/drivers/fastboot/Kconfig +++ b/drivers/fastboot/Kconfig @@ -241,6 +241,13 @@ config FASTBOOT_OEM_RUN this feature if you are using verified boot, as it will allow an attacker to bypass any restrictions you have in place.
+config FASTBOOT_OEM_BOARD
- bool "Enable the 'oem board' command"
- help
This extends the fastboot protocol with an "oem board" command. This
command allows running vendor custom code defined in board/ files.
Otherwise, it will do nothing and send fastboot fail.
If we move forward with this, please also document the new command in: doc/android/fastboot.rst
endif # FASTBOOT
endmenu diff --git a/drivers/fastboot/fb_command.c b/drivers/fastboot/fb_command.c index 5fcadcdf50..2298815770 100644 --- a/drivers/fastboot/fb_command.c +++ b/drivers/fastboot/fb_command.c @@ -40,6 +40,7 @@ static void reboot_recovery(char *, char *); static void oem_format(char *, char *); static void oem_partconf(char *, char *); static void oem_bootbus(char *, char *); +static void oem_board(char *, char *); static void run_ucmd(char *, char *); static void run_acmd(char *, char *);
@@ -107,6 +108,10 @@ static const struct { .command = "oem run", .dispatch = CONFIG_IS_ENABLED(FASTBOOT_OEM_RUN, (run_ucmd), (NULL)) },
- [FASTBOOT_COMMAND_OEM_BOARD] = {
.command = "oem board",
.dispatch = CONFIG_IS_ENABLED(FASTBOOT_OEM_BOARD, (oem_board), (NULL))
- }, [FASTBOOT_COMMAND_UCMD] = { .command = "UCmd", .dispatch = CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT, (run_ucmd), (NULL))
@@ -490,3 +495,13 @@ static void __maybe_unused oem_bootbus(char *cmd_parameter, char *response) else fastboot_okay(NULL, response); }
+void __weak fastboot_oem_board(char *cmd_parameter, void *data, u32 size, char *response) +{
- fastboot_fail("oem board function not defined", response);
+}
+static void __maybe_unused oem_board(char *cmd_parameter, char *response) +{
- fastboot_oem_board(cmd_parameter, fastboot_buf_addr, image_size, response);
+} diff --git a/include/fastboot.h b/include/fastboot.h index 296451f89d..06c1f26b6c 100644 --- a/include/fastboot.h +++ b/include/fastboot.h @@ -37,6 +37,7 @@ enum { FASTBOOT_COMMAND_OEM_PARTCONF, FASTBOOT_COMMAND_OEM_BOOTBUS, FASTBOOT_COMMAND_OEM_RUN,
- FASTBOOT_COMMAND_OEM_BOARD, FASTBOOT_COMMAND_ACMD, FASTBOOT_COMMAND_UCMD, FASTBOOT_COMMAND_COUNT
-- 2.30.1

Hello,
On Thu, Feb 15, 2024 at 10:14:13AM +0100, Mattijs Korpershoek wrote:
Hi Alexey,
Thank you for the patch.
On jeu., f'evr. 01, 2024 at 12:20, Alexey Romanov avromanov@salutedevices.com wrote:
Currently, fastboot protocol in U-Boot has no opportunity to execute vendor custom code with verifed boot. This patch introduce new fastboot subcommand fastboot oem board:<cmd>, which allow to run custom oem_board function.
Default implementation is __weak. Vendor must redefine it in board/ folder with his own logic.
For example, some vendors have their custom nand/emmc partition flashing or erasing. Here some typical command for such use cases:
flashing:
$ fastboot stage bootloader.img $ fastboot oem board:write_bootloader
erasing:
$ fastboot oem board:erase_env
Signed-off-by: Alexey Romanov avromanov@salutedevices.com
Sorry for the delay. I needed time to give this some thoughts and I waited for Sean to chime as well on this.
I've heard from Neil that this might be related to: https://github.com/superna9999/pyamlboot/pull/20
Yeah, pyamlboot also uses the same 'bootloader' partition flashing scheme as I present in the patch 2. This is custom Amlogic protocol.
I think this can be useful. Not necessarily for writing custom partitions, but I see this could be used for other things:
Provision SoC-specific fuses (serialno/mac addr) at the factory line (for production devices) Examples: $ fastboot oem board:write_serialno ABCDEF $ fastboot oem board:write_macaddr AA:BB:CC:DD:EE
Access secure storage (via an Trusted Application)
Agree, you are completely right.
But both examples could also be in a fastboot flash format: $ fastboot flash serialno ABCDEF
But this case requires to 'serialno' partition definition in schema? I didn't fully understand you.
One concern I have is that U-Boot forks might use this command as an excuse to not makes things generic.
I hope that others will chime in on this as well. I'd like to discuss this more because once this command is in we cannot remove it later.
drivers/fastboot/Kconfig | 7 +++++++ drivers/fastboot/fb_command.c | 15 +++++++++++++++ include/fastboot.h | 1 + 3 files changed, 23 insertions(+)
diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig index a4313d60a9..4d94391a76 100644 --- a/drivers/fastboot/Kconfig +++ b/drivers/fastboot/Kconfig @@ -241,6 +241,13 @@ config FASTBOOT_OEM_RUN this feature if you are using verified boot, as it will allow an attacker to bypass any restrictions you have in place.
+config FASTBOOT_OEM_BOARD
- bool "Enable the 'oem board' command"
- help
This extends the fastboot protocol with an "oem board" command. This
command allows running vendor custom code defined in board/ files.
Otherwise, it will do nothing and send fastboot fail.
If we move forward with this, please also document the new command in: doc/android/fastboot.rst
endif # FASTBOOT
endmenu diff --git a/drivers/fastboot/fb_command.c b/drivers/fastboot/fb_command.c index 5fcadcdf50..2298815770 100644 --- a/drivers/fastboot/fb_command.c +++ b/drivers/fastboot/fb_command.c @@ -40,6 +40,7 @@ static void reboot_recovery(char *, char *); static void oem_format(char *, char *); static void oem_partconf(char *, char *); static void oem_bootbus(char *, char *); +static void oem_board(char *, char *); static void run_ucmd(char *, char *); static void run_acmd(char *, char *);
@@ -107,6 +108,10 @@ static const struct { .command = "oem run", .dispatch = CONFIG_IS_ENABLED(FASTBOOT_OEM_RUN, (run_ucmd), (NULL)) },
- [FASTBOOT_COMMAND_OEM_BOARD] = {
.command = "oem board",
.dispatch = CONFIG_IS_ENABLED(FASTBOOT_OEM_BOARD, (oem_board), (NULL))
- }, [FASTBOOT_COMMAND_UCMD] = { .command = "UCmd", .dispatch = CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT, (run_ucmd), (NULL))
@@ -490,3 +495,13 @@ static void __maybe_unused oem_bootbus(char *cmd_parameter, char *response) else fastboot_okay(NULL, response); }
+void __weak fastboot_oem_board(char *cmd_parameter, void *data, u32 size, char *response) +{
- fastboot_fail("oem board function not defined", response);
+}
+static void __maybe_unused oem_board(char *cmd_parameter, char *response) +{
- fastboot_oem_board(cmd_parameter, fastboot_buf_addr, image_size, response);
+} diff --git a/include/fastboot.h b/include/fastboot.h index 296451f89d..06c1f26b6c 100644 --- a/include/fastboot.h +++ b/include/fastboot.h @@ -37,6 +37,7 @@ enum { FASTBOOT_COMMAND_OEM_PARTCONF, FASTBOOT_COMMAND_OEM_BOOTBUS, FASTBOOT_COMMAND_OEM_RUN,
- FASTBOOT_COMMAND_OEM_BOARD, FASTBOOT_COMMAND_ACMD, FASTBOOT_COMMAND_UCMD, FASTBOOT_COMMAND_COUNT
-- 2.30.1

Hi Alexey,
On lun., mars 04, 2024 at 14:11, Alexey Romanov avromanov@salutedevices.com wrote:
Hello,
On Thu, Feb 15, 2024 at 10:14:13AM +0100, Mattijs Korpershoek wrote:
Hi Alexey,
Thank you for the patch.
On jeu., f'evr. 01, 2024 at 12:20, Alexey Romanov avromanov@salutedevices.com wrote:
Currently, fastboot protocol in U-Boot has no opportunity to execute vendor custom code with verifed boot. This patch introduce new fastboot subcommand fastboot oem board:<cmd>, which allow to run custom oem_board function.
Default implementation is __weak. Vendor must redefine it in board/ folder with his own logic.
For example, some vendors have their custom nand/emmc partition flashing or erasing. Here some typical command for such use cases:
flashing:
$ fastboot stage bootloader.img $ fastboot oem board:write_bootloader
erasing:
$ fastboot oem board:erase_env
Signed-off-by: Alexey Romanov avromanov@salutedevices.com
Sorry for the delay. I needed time to give this some thoughts and I waited for Sean to chime as well on this.
I've heard from Neil that this might be related to: https://github.com/superna9999/pyamlboot/pull/20
Yeah, pyamlboot also uses the same 'bootloader' partition flashing scheme as I present in the patch 2. This is custom Amlogic protocol.
I think this can be useful. Not necessarily for writing custom partitions, but I see this could be used for other things:
Provision SoC-specific fuses (serialno/mac addr) at the factory line (for production devices) Examples: $ fastboot oem board:write_serialno ABCDEF $ fastboot oem board:write_macaddr AA:BB:CC:DD:EE
Access secure storage (via an Trusted Application)
Agree, you are completely right.
But both examples could also be in a fastboot flash format: $ fastboot flash serialno ABCDEF
But this case requires to 'serialno' partition definition in schema? I didn't fully understand you.
I meant more in a "conceptual way". (from a end user perspective)
"fastboot flash" is generic command that's just supposed to write data somewhere.
The back-end (partitioning etc) depends on the storage the device uses so that's a "implementation detail".
In any case, I don't have a proper alternative to what you are proposing so as send in [1], I'm okay picking this up after some minor review comments are addressed.
[1] https://lore.kernel.org/all/87jzlcfang.fsf@baylibre.com/
One concern I have is that U-Boot forks might use this command as an excuse to not makes things generic.
I hope that others will chime in on this as well. I'd like to discuss this more because once this command is in we cannot remove it later.
drivers/fastboot/Kconfig | 7 +++++++ drivers/fastboot/fb_command.c | 15 +++++++++++++++ include/fastboot.h | 1 + 3 files changed, 23 insertions(+)
diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig index a4313d60a9..4d94391a76 100644 --- a/drivers/fastboot/Kconfig +++ b/drivers/fastboot/Kconfig @@ -241,6 +241,13 @@ config FASTBOOT_OEM_RUN this feature if you are using verified boot, as it will allow an attacker to bypass any restrictions you have in place.
+config FASTBOOT_OEM_BOARD
- bool "Enable the 'oem board' command"
- help
This extends the fastboot protocol with an "oem board" command. This
command allows running vendor custom code defined in board/ files.
Otherwise, it will do nothing and send fastboot fail.
If we move forward with this, please also document the new command in: doc/android/fastboot.rst
endif # FASTBOOT
endmenu diff --git a/drivers/fastboot/fb_command.c b/drivers/fastboot/fb_command.c index 5fcadcdf50..2298815770 100644 --- a/drivers/fastboot/fb_command.c +++ b/drivers/fastboot/fb_command.c @@ -40,6 +40,7 @@ static void reboot_recovery(char *, char *); static void oem_format(char *, char *); static void oem_partconf(char *, char *); static void oem_bootbus(char *, char *); +static void oem_board(char *, char *); static void run_ucmd(char *, char *); static void run_acmd(char *, char *);
@@ -107,6 +108,10 @@ static const struct { .command = "oem run", .dispatch = CONFIG_IS_ENABLED(FASTBOOT_OEM_RUN, (run_ucmd), (NULL)) },
- [FASTBOOT_COMMAND_OEM_BOARD] = {
.command = "oem board",
.dispatch = CONFIG_IS_ENABLED(FASTBOOT_OEM_BOARD, (oem_board), (NULL))
- }, [FASTBOOT_COMMAND_UCMD] = { .command = "UCmd", .dispatch = CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT, (run_ucmd), (NULL))
@@ -490,3 +495,13 @@ static void __maybe_unused oem_bootbus(char *cmd_parameter, char *response) else fastboot_okay(NULL, response); }
+void __weak fastboot_oem_board(char *cmd_parameter, void *data, u32 size, char *response) +{
- fastboot_fail("oem board function not defined", response);
+}
+static void __maybe_unused oem_board(char *cmd_parameter, char *response) +{
- fastboot_oem_board(cmd_parameter, fastboot_buf_addr, image_size, response);
+} diff --git a/include/fastboot.h b/include/fastboot.h index 296451f89d..06c1f26b6c 100644 --- a/include/fastboot.h +++ b/include/fastboot.h @@ -37,6 +37,7 @@ enum { FASTBOOT_COMMAND_OEM_PARTCONF, FASTBOOT_COMMAND_OEM_BOOTBUS, FASTBOOT_COMMAND_OEM_RUN,
- FASTBOOT_COMMAND_OEM_BOARD, FASTBOOT_COMMAND_ACMD, FASTBOOT_COMMAND_UCMD, FASTBOOT_COMMAND_COUNT
-- 2.30.1
-- Thank you, Alexey

Hello Mattijs, is there any feedback?
On Thu, Feb 15, 2024 at 10:14:13AM +0100, Mattijs Korpershoek wrote:
Hi Alexey,
Thank you for the patch.
On jeu., f'evr. 01, 2024 at 12:20, Alexey Romanov avromanov@salutedevices.com wrote:
Currently, fastboot protocol in U-Boot has no opportunity to execute vendor custom code with verifed boot. This patch introduce new fastboot subcommand fastboot oem board:<cmd>, which allow to run custom oem_board function.
Default implementation is __weak. Vendor must redefine it in board/ folder with his own logic.
For example, some vendors have their custom nand/emmc partition flashing or erasing. Here some typical command for such use cases:
flashing:
$ fastboot stage bootloader.img $ fastboot oem board:write_bootloader
erasing:
$ fastboot oem board:erase_env
Signed-off-by: Alexey Romanov avromanov@salutedevices.com
Sorry for the delay. I needed time to give this some thoughts and I waited for Sean to chime as well on this.
I've heard from Neil that this might be related to: https://github.com/superna9999/pyamlboot/pull/20
I think this can be useful. Not necessarily for writing custom partitions, but I see this could be used for other things:
Provision SoC-specific fuses (serialno/mac addr) at the factory line (for production devices) Examples: $ fastboot oem board:write_serialno ABCDEF $ fastboot oem board:write_macaddr AA:BB:CC:DD:EE
Access secure storage (via an Trusted Application)
But both examples could also be in a fastboot flash format: $ fastboot flash serialno ABCDEF
One concern I have is that U-Boot forks might use this command as an excuse to not makes things generic.
I hope that others will chime in on this as well. I'd like to discuss this more because once this command is in we cannot remove it later.
drivers/fastboot/Kconfig | 7 +++++++ drivers/fastboot/fb_command.c | 15 +++++++++++++++ include/fastboot.h | 1 + 3 files changed, 23 insertions(+)
diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig index a4313d60a9..4d94391a76 100644 --- a/drivers/fastboot/Kconfig +++ b/drivers/fastboot/Kconfig @@ -241,6 +241,13 @@ config FASTBOOT_OEM_RUN this feature if you are using verified boot, as it will allow an attacker to bypass any restrictions you have in place.
+config FASTBOOT_OEM_BOARD
- bool "Enable the 'oem board' command"
- help
This extends the fastboot protocol with an "oem board" command. This
command allows running vendor custom code defined in board/ files.
Otherwise, it will do nothing and send fastboot fail.
If we move forward with this, please also document the new command in: doc/android/fastboot.rst
endif # FASTBOOT
endmenu diff --git a/drivers/fastboot/fb_command.c b/drivers/fastboot/fb_command.c index 5fcadcdf50..2298815770 100644 --- a/drivers/fastboot/fb_command.c +++ b/drivers/fastboot/fb_command.c @@ -40,6 +40,7 @@ static void reboot_recovery(char *, char *); static void oem_format(char *, char *); static void oem_partconf(char *, char *); static void oem_bootbus(char *, char *); +static void oem_board(char *, char *); static void run_ucmd(char *, char *); static void run_acmd(char *, char *);
@@ -107,6 +108,10 @@ static const struct { .command = "oem run", .dispatch = CONFIG_IS_ENABLED(FASTBOOT_OEM_RUN, (run_ucmd), (NULL)) },
- [FASTBOOT_COMMAND_OEM_BOARD] = {
.command = "oem board",
.dispatch = CONFIG_IS_ENABLED(FASTBOOT_OEM_BOARD, (oem_board), (NULL))
- }, [FASTBOOT_COMMAND_UCMD] = { .command = "UCmd", .dispatch = CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT, (run_ucmd), (NULL))
@@ -490,3 +495,13 @@ static void __maybe_unused oem_bootbus(char *cmd_parameter, char *response) else fastboot_okay(NULL, response); }
+void __weak fastboot_oem_board(char *cmd_parameter, void *data, u32 size, char *response) +{
- fastboot_fail("oem board function not defined", response);
+}
+static void __maybe_unused oem_board(char *cmd_parameter, char *response) +{
- fastboot_oem_board(cmd_parameter, fastboot_buf_addr, image_size, response);
+} diff --git a/include/fastboot.h b/include/fastboot.h index 296451f89d..06c1f26b6c 100644 --- a/include/fastboot.h +++ b/include/fastboot.h @@ -37,6 +37,7 @@ enum { FASTBOOT_COMMAND_OEM_PARTCONF, FASTBOOT_COMMAND_OEM_BOOTBUS, FASTBOOT_COMMAND_OEM_RUN,
- FASTBOOT_COMMAND_OEM_BOARD, FASTBOOT_COMMAND_ACMD, FASTBOOT_COMMAND_UCMD, FASTBOOT_COMMAND_COUNT
-- 2.30.1

Hi Alexey,
On mer., avril 03, 2024 at 08:49, Alexey Romanov avromanov@salutedevices.com wrote:
Hello Mattijs, is there any feedback?
Sorry for the late reply. I was both swamped with other work and awaiting. feedback from others.
I don't have strong enough arguments to state that this is not useful to others, I have re-considered this and I'm willing to pick it up.
Please rebase, as this no longer applies.
Also see some review comments below
On Thu, Feb 15, 2024 at 10:14:13AM +0100, Mattijs Korpershoek wrote:
Hi Alexey,
Thank you for the patch.
On jeu., f'evr. 01, 2024 at 12:20, Alexey Romanov avromanov@salutedevices.com wrote:
Currently, fastboot protocol in U-Boot has no opportunity to execute vendor custom code with verifed boot. This patch introduce new fastboot subcommand fastboot oem board:<cmd>, which allow to run custom oem_board function.
Default implementation is __weak. Vendor must redefine it in board/ folder with his own logic.
For example, some vendors have their custom nand/emmc partition flashing or erasing. Here some typical command for such use cases:
flashing:
$ fastboot stage bootloader.img $ fastboot oem board:write_bootloader
erasing:
$ fastboot oem board:erase_env
Signed-off-by: Alexey Romanov avromanov@salutedevices.com
Sorry for the delay. I needed time to give this some thoughts and I waited for Sean to chime as well on this.
I've heard from Neil that this might be related to: https://github.com/superna9999/pyamlboot/pull/20
I think this can be useful. Not necessarily for writing custom partitions, but I see this could be used for other things:
Provision SoC-specific fuses (serialno/mac addr) at the factory line (for production devices) Examples: $ fastboot oem board:write_serialno ABCDEF $ fastboot oem board:write_macaddr AA:BB:CC:DD:EE
Access secure storage (via an Trusted Application)
But both examples could also be in a fastboot flash format: $ fastboot flash serialno ABCDEF
One concern I have is that U-Boot forks might use this command as an excuse to not makes things generic.
I hope that others will chime in on this as well. I'd like to discuss this more because once this command is in we cannot remove it later.
drivers/fastboot/Kconfig | 7 +++++++ drivers/fastboot/fb_command.c | 15 +++++++++++++++ include/fastboot.h | 1 + 3 files changed, 23 insertions(+)
diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig index a4313d60a9..4d94391a76 100644 --- a/drivers/fastboot/Kconfig +++ b/drivers/fastboot/Kconfig @@ -241,6 +241,13 @@ config FASTBOOT_OEM_RUN this feature if you are using verified boot, as it will allow an attacker to bypass any restrictions you have in place.
+config FASTBOOT_OEM_BOARD
- bool "Enable the 'oem board' command"
- help
This extends the fastboot protocol with an "oem board" command. This
command allows running vendor custom code defined in board/ files.
Otherwise, it will do nothing and send fastboot fail.
If we move forward with this, please also document the new command in: doc/android/fastboot.rst
This still applies, document the command please.
endif # FASTBOOT
endmenu diff --git a/drivers/fastboot/fb_command.c b/drivers/fastboot/fb_command.c index 5fcadcdf50..2298815770 100644 --- a/drivers/fastboot/fb_command.c +++ b/drivers/fastboot/fb_command.c @@ -40,6 +40,7 @@ static void reboot_recovery(char *, char *); static void oem_format(char *, char *); static void oem_partconf(char *, char *); static void oem_bootbus(char *, char *); +static void oem_board(char *, char *); static void run_ucmd(char *, char *); static void run_acmd(char *, char *);
@@ -107,6 +108,10 @@ static const struct { .command = "oem run", .dispatch = CONFIG_IS_ENABLED(FASTBOOT_OEM_RUN, (run_ucmd), (NULL)) },
- [FASTBOOT_COMMAND_OEM_BOARD] = {
.command = "oem board",
.dispatch = CONFIG_IS_ENABLED(FASTBOOT_OEM_BOARD, (oem_board), (NULL))
- }, [FASTBOOT_COMMAND_UCMD] = { .command = "UCmd", .dispatch = CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT, (run_ucmd), (NULL))
@@ -490,3 +495,13 @@ static void __maybe_unused oem_bootbus(char *cmd_parameter, char *response) else fastboot_okay(NULL, response); }
+void __weak fastboot_oem_board(char *cmd_parameter, void *data, u32 size, char *response) +{
- fastboot_fail("oem board function not defined", response);
+}
+static void __maybe_unused oem_board(char *cmd_parameter, char *response) +{
- fastboot_oem_board(cmd_parameter, fastboot_buf_addr, image_size, response);
+}
Please also document the functions with comment headers, as done for the other oem_ functions.
diff --git a/include/fastboot.h b/include/fastboot.h index 296451f89d..06c1f26b6c 100644 --- a/include/fastboot.h +++ b/include/fastboot.h @@ -37,6 +37,7 @@ enum { FASTBOOT_COMMAND_OEM_PARTCONF, FASTBOOT_COMMAND_OEM_BOOTBUS, FASTBOOT_COMMAND_OEM_RUN,
- FASTBOOT_COMMAND_OEM_BOARD, FASTBOOT_COMMAND_ACMD, FASTBOOT_COMMAND_UCMD, FASTBOOT_COMMAND_COUNT
-- 2.30.1
-- Thank you, Alexey

An example of how we use fastboot oeam board subcommand for Sean Anderson.
1 - OEM_BOARD_WRITE_BOOTLOADER_CMD:
We use it for custom Amlogic bootloader + tpl flashing protocol.
2 - OEM_BOARD_ERASE_CMD:
Custom logic for erasing the env-emulated partition, which isn't in the mtd markup map.
Example of the script which completely flashes the device:
$ fastboot erase bootloader $ fastboot stage u-boot.bin $ fastboot oem board:write_bootloader $ fastboot reboot-bootloader $ fastboot oem board:erase_env $ fastboot erase misc $ fastboot erase super $ fastboot flash super rootfs $ fastboot reboot
Signed-off-by: Alexey Romanov avromanov@salutedevices.com --- board/amlogic/ad401/fastboot.c | 222 +++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 board/amlogic/ad401/fastboot.c
diff --git a/board/amlogic/ad401/fastboot.c b/board/amlogic/ad401/fastboot.c new file mode 100644 index 0000000000..01da8efa5b --- /dev/null +++ b/board/amlogic/ad401/fastboot.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2023 SaluteDevices, Inc. + */ + +#include <common.h> +#include <env.h> +#include <fastboot.h> +#include <nand.h> +#include <asm/arch/nand.h> +#include <jffs2/load_kernel.h> +#include <linux/sizes.h> +#include <linux/types.h> +#include <linux/mtd/mtd.h> + +enum { + OEM_BOARD_ERASE_CMD, + OEM_BOARD_WRITE_BOOTLOADER_CMD, +}; + +struct defenv { + char *name; + char value[256]; +}; + +static void save_defenv(struct defenv *e, size_t cnt) +{ + int i; + + for (i = 0; i < cnt; i++) { + const char *env_val = env_get(e[i].name); + + if (env_val) + strlcpy(e[i].value, env_val, sizeof(e[i].value)); + else + e[i].value[0] = '\0'; + } +} + +static void set_defenv(struct defenv *e, size_t cnt) +{ + int i; + + for (i = 0; i < cnt; i++) + env_set(e[i].name, e[i].value); +} + +static int fastboot_erase_env(void) +{ + char *const defenv_names[] = { "lock", "mtdparts", "mtdids" }; + struct defenv env[ARRAY_SIZE(defenv_names)]; + int err, i; + + for (i = 0; i < ARRAY_SIZE(env); i++) + env[i].name = defenv_names[i]; + + printf("ENV is being erased...\n"); + + /* + * Reset environment to the default, excluding 'lock' variable, + * because it reflects the fastboot's state after execution of + * 'flashing unlock' command, hence it must survive the env-erasing. + * Otherwise, further erase commands will fail on check_lock(). + * + * Also, we have to save 'mtdparts' and 'mtdids' variables + * because they are necessary to obtain partition map. + */ + + save_defenv(env, ARRAY_SIZE(env)); + env_set_default(NULL, 0); + set_defenv(env, ARRAY_SIZE(env)); + + err = env_save(); + if (err) { + pr_err("Can't overwrite ENV-partition\n"); + return err; + } + + return 0; +} + +static int fastboot_nand_write_tpl(struct mtd_info *mtd, void *buffer, + u32 offset, size_t size, int flags) +{ + int boot_cpy_num = meson_bootloader_copy_num(BOOT_TPL); + u64 size_per_copy = meson_bootloader_copy_size(mtd, BOOT_TPL); + int i; + + for (i = 0; i < boot_cpy_num; i++) { + size_t retlen, len = size; + int ret; + + ret = nand_write_skip_bad(mtd, offset + (i * size_per_copy), + &len, &retlen, offset + size_per_copy, + buffer, flags); + if (ret) + return ret; + } + + return 0; +} + +static int fastboot_nand_write_bl2(struct mtd_info *mtd, void *buffer, + u32 offset, size_t size, int flags) +{ + int boot_cpy_num = meson_bootloader_copy_num(BOOT_BL2); + u64 size_per_copy = meson_bootloader_copy_size(mtd, BOOT_BL2); + int i; + + for (i = 0; i < boot_cpy_num; i++) { + int ret; + + ret = meson_bootloader_write_bl2(mtd, buffer, + offset + (i * size_per_copy), + size, flags); + if (ret) + return ret; + } + + return meson_bootloader_write_info_pages(); +} + +static int fastboot_nand_write_bootloader(void *buffer, u32 size) +{ + struct part_info *part; + struct mtd_info *mtd = NULL; + struct mtd_device *dev; + u8 pnum; + int ret; + + if (size < BL2_SIZE) + return 0; + + if (!buffer) + return -EINVAL; + + ret = mtdparts_init(); + if (ret) { + pr_err("Cannot initialize MTD partitions\n"); + return ret; + } + + ret = find_dev_and_part("bootloader", &dev, &pnum, &part); + if (ret) { + pr_err("cannot find 'bootloader' partition\n"); + return ret; + } + + mtd = get_nand_dev_by_index(dev->id->num); + if (!mtd) + return -EINVAL; + + ret = fastboot_nand_write_bl2(mtd, buffer, part->offset, + BL2_SIZE, WITH_WR_VERIFY); + if (ret) { + pr_err("fastboot: failed to write BL2\n"); + return ret; + } + + ret = find_dev_and_part("tpl", &dev, &pnum, &part); + if (ret) { + pr_err("cannot find 'bootloader' partition\n"); + return ret; + } + + mtd = get_nand_dev_by_index(dev->id->num); + if (!mtd) + return -EINVAL; + + ret = fastboot_nand_write_tpl(mtd, buffer + BL2_SIZE, part->offset, + size - BL2_SIZE, WITH_WR_VERIFY); + if (ret) { + pr_err("fastboot: failed to write TPL\n"); + return ret; + } + + return 0; +} + +int get_oem_board_command(const char *cmd) +{ + const char *oem_commands[] = { + [OEM_BOARD_ERASE_CMD] = "erase_env", + [OEM_BOARD_WRITE_BOOTLOADER_CMD] = "write_bootloader", + }; + int i; + + for (i = 0; i < ARRAY_SIZE(oem_commands); i++) + if (!strcmp(cmd, oem_commands[i])) + return i; + + return -EINVAL; +} + +void fastboot_oem_board(const char *cmd_parameter, void *data, u32 size, + char *response) +{ + char buf[128] = { 0 }; + int ret, cmd; + + cmd = get_oem_board_command(cmd_parameter); + + switch (cmd) { + case OEM_BOARD_ERASE_CMD: + ret = fastboot_erase_env(); + break; + case OEM_BOARD_WRITE_BOOTLOADER_CMD: + ret = fastboot_nand_write_bootloader(data, size); + break; + default: + snprintf(buf, sizeof(buf), + "Command 'oem board %s' not supported", + cmd_parameter); + fastboot_fail(buf, response); + return; + } + + if (ret < 0) + fastboot_fail("Failed to erase env partition", response); + else + fastboot_okay(NULL, response); +}

On Thu, Feb 01, 2024 at 12:20:27PM +0300, Alexey Romanov wrote:
+static int fastboot_nand_write_tpl(struct mtd_info *mtd, void *buffer,
u32 offset, size_t size, int flags)
+{
- int boot_cpy_num = meson_bootloader_copy_num(BOOT_TPL);
- u64 size_per_copy = meson_bootloader_copy_size(mtd, BOOT_TPL);
- int i;
- for (i = 0; i < boot_cpy_num; i++) {
size_t retlen, len = size;
int ret;
ret = nand_write_skip_bad(mtd, offset + (i * size_per_copy),
&len, &retlen, offset + size_per_copy,
^^^^^^^^^^^^^^^^^^^^^^ Sorry if I'm missing something obvious, but why is the limit "offset + size_per_copy"? I would have expected it to be just "size_per_copy" or maybe some kind of limit minus the offset...
buffer, flags);
if (ret)
return ret;
- }
- return 0;
+}
regards, dan carpenter

Hi,
On Thu, Feb 01, 2024 at 04:56:34PM +0300, Dan Carpenter wrote:
On Thu, Feb 01, 2024 at 12:20:27PM +0300, Alexey Romanov wrote:
+static int fastboot_nand_write_tpl(struct mtd_info *mtd, void *buffer,
u32 offset, size_t size, int flags)
+{
- int boot_cpy_num = meson_bootloader_copy_num(BOOT_TPL);
- u64 size_per_copy = meson_bootloader_copy_size(mtd, BOOT_TPL);
- int i;
- for (i = 0; i < boot_cpy_num; i++) {
size_t retlen, len = size;
int ret;
ret = nand_write_skip_bad(mtd, offset + (i * size_per_copy),
&len, &retlen, offset + size_per_copy,
^^^^^^^^^^^^^^^^^^^^^^
Sorry if I'm missing something obvious, but why is the limit "offset + size_per_copy"? I would have expected it to be just "size_per_copy" or maybe some kind of limit minus the offset...
Yeah, this is incorrect. Should be size_per_copy.
But this patch is just an example of realization.
buffer, flags);
if (ret)
return ret;
- }
- return 0;
+}
regards, dan carpenter

On jeu., févr. 01, 2024 at 12:20, Alexey Romanov avromanov@salutedevices.com wrote:
An example of how we use fastboot oeam board subcommand for Sean Anderson.
1 - OEM_BOARD_WRITE_BOOTLOADER_CMD:
We use it for custom Amlogic bootloader + tpl flashing protocol.
2 - OEM_BOARD_ERASE_CMD:
Custom logic for erasing the env-emulated partition, which isn't in the mtd markup map.
Example of the script which completely flashes the device:
$ fastboot erase bootloader $ fastboot stage u-boot.bin $ fastboot oem board:write_bootloader $ fastboot reboot-bootloader $ fastboot oem board:erase_env $ fastboot erase misc $ fastboot erase super $ fastboot flash super rootfs $ fastboot reboot
Signed-off-by: Alexey Romanov avromanov@salutedevices.com
board/amlogic/ad401/fastboot.c | 222 +++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 board/amlogic/ad401/fastboot.c
diff --git a/board/amlogic/ad401/fastboot.c b/board/amlogic/ad401/fastboot.c new file mode 100644 index 0000000000..01da8efa5b --- /dev/null +++ b/board/amlogic/ad401/fastboot.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2023 SaluteDevices, Inc.
- */
+#include <common.h> +#include <env.h> +#include <fastboot.h> +#include <nand.h> +#include <asm/arch/nand.h> +#include <jffs2/load_kernel.h> +#include <linux/sizes.h> +#include <linux/types.h> +#include <linux/mtd/mtd.h>
+enum {
- OEM_BOARD_ERASE_CMD,
- OEM_BOARD_WRITE_BOOTLOADER_CMD,
+};
+struct defenv {
- char *name;
- char value[256];
+};
+static void save_defenv(struct defenv *e, size_t cnt) +{
- int i;
- for (i = 0; i < cnt; i++) {
const char *env_val = env_get(e[i].name);
if (env_val)
strlcpy(e[i].value, env_val, sizeof(e[i].value));
else
e[i].value[0] = '\0';
- }
+}
+static void set_defenv(struct defenv *e, size_t cnt) +{
- int i;
- for (i = 0; i < cnt; i++)
env_set(e[i].name, e[i].value);
+}
+static int fastboot_erase_env(void) +{
- char *const defenv_names[] = { "lock", "mtdparts", "mtdids" };
- struct defenv env[ARRAY_SIZE(defenv_names)];
- int err, i;
- for (i = 0; i < ARRAY_SIZE(env); i++)
env[i].name = defenv_names[i];
- printf("ENV is being erased...\n");
- /*
* Reset environment to the default, excluding 'lock' variable,
* because it reflects the fastboot's state after execution of
* 'flashing unlock' command, hence it must survive the env-erasing.
* Otherwise, further erase commands will fail on check_lock().
*
* Also, we have to save 'mtdparts' and 'mtdids' variables
* because they are necessary to obtain partition map.
*/
- save_defenv(env, ARRAY_SIZE(env));
- env_set_default(NULL, 0);
- set_defenv(env, ARRAY_SIZE(env));
- err = env_save();
- if (err) {
pr_err("Can't overwrite ENV-partition\n");
return err;
- }
Hmm so the fastboot locked state is saved in the U-Boot environment. There is probably a good reason for this (no secure storage for example). But this does not feel board specific.
Wouldn't it be better if we could just run "fastboot erase bootenv" and that the generic fastboot code does the right thing? (which is env default, and ignoring some magic/specific variables)
- return 0;
+}
+static int fastboot_nand_write_tpl(struct mtd_info *mtd, void *buffer,
u32 offset, size_t size, int flags)
+{
- int boot_cpy_num = meson_bootloader_copy_num(BOOT_TPL);
- u64 size_per_copy = meson_bootloader_copy_size(mtd, BOOT_TPL);
- int i;
- for (i = 0; i < boot_cpy_num; i++) {
size_t retlen, len = size;
int ret;
ret = nand_write_skip_bad(mtd, offset + (i * size_per_copy),
&len, &retlen, offset + size_per_copy,
buffer, flags);
if (ret)
return ret;
- }
- return 0;
+}
+static int fastboot_nand_write_bl2(struct mtd_info *mtd, void *buffer,
u32 offset, size_t size, int flags)
+{
- int boot_cpy_num = meson_bootloader_copy_num(BOOT_BL2);
- u64 size_per_copy = meson_bootloader_copy_size(mtd, BOOT_BL2);
- int i;
- for (i = 0; i < boot_cpy_num; i++) {
int ret;
ret = meson_bootloader_write_bl2(mtd, buffer,
offset + (i * size_per_copy),
size, flags);
if (ret)
return ret;
- }
- return meson_bootloader_write_info_pages();
+}
+static int fastboot_nand_write_bootloader(void *buffer, u32 size) +{
- struct part_info *part;
- struct mtd_info *mtd = NULL;
- struct mtd_device *dev;
- u8 pnum;
- int ret;
- if (size < BL2_SIZE)
return 0;
- if (!buffer)
return -EINVAL;
- ret = mtdparts_init();
- if (ret) {
pr_err("Cannot initialize MTD partitions\n");
return ret;
- }
- ret = find_dev_and_part("bootloader", &dev, &pnum, &part);
- if (ret) {
pr_err("cannot find 'bootloader' partition\n");
return ret;
- }
- mtd = get_nand_dev_by_index(dev->id->num);
- if (!mtd)
return -EINVAL;
- ret = fastboot_nand_write_bl2(mtd, buffer, part->offset,
BL2_SIZE, WITH_WR_VERIFY);
- if (ret) {
pr_err("fastboot: failed to write BL2\n");
return ret;
- }
- ret = find_dev_and_part("tpl", &dev, &pnum, &part);
- if (ret) {
pr_err("cannot find 'bootloader' partition\n");
return ret;
- }
- mtd = get_nand_dev_by_index(dev->id->num);
- if (!mtd)
return -EINVAL;
- ret = fastboot_nand_write_tpl(mtd, buffer + BL2_SIZE, part->offset,
size - BL2_SIZE, WITH_WR_VERIFY);
- if (ret) {
pr_err("fastboot: failed to write TPL\n");
return ret;
- }
- return 0;
+}
+int get_oem_board_command(const char *cmd) +{
- const char *oem_commands[] = {
[OEM_BOARD_ERASE_CMD] = "erase_env",
[OEM_BOARD_WRITE_BOOTLOADER_CMD] = "write_bootloader",
- };
- int i;
- for (i = 0; i < ARRAY_SIZE(oem_commands); i++)
if (!strcmp(cmd, oem_commands[i]))
return i;
- return -EINVAL;
+}
+void fastboot_oem_board(const char *cmd_parameter, void *data, u32 size,
char *response)
+{
- char buf[128] = { 0 };
- int ret, cmd;
- cmd = get_oem_board_command(cmd_parameter);
- switch (cmd) {
case OEM_BOARD_ERASE_CMD:
ret = fastboot_erase_env();
break;
case OEM_BOARD_WRITE_BOOTLOADER_CMD:
ret = fastboot_nand_write_bootloader(data, size);
break;
default:
snprintf(buf, sizeof(buf),
"Command 'oem board %s' not supported",
cmd_parameter);
fastboot_fail(buf, response);
return;
- }
- if (ret < 0)
fastboot_fail("Failed to erase env partition", response);
- else
fastboot_okay(NULL, response);
+}
2.30.1

Hello Mattijs,
On Thu, Feb 15, 2024 at 10:24:11AM +0100, Mattijs Korpershoek wrote:
On jeu., f'evr. 01, 2024 at 12:20, Alexey Romanov avromanov@salutedevices.com wrote:
An example of how we use fastboot oeam board subcommand for Sean Anderson.
1 - OEM_BOARD_WRITE_BOOTLOADER_CMD:
We use it for custom Amlogic bootloader + tpl flashing protocol.
2 - OEM_BOARD_ERASE_CMD:
Custom logic for erasing the env-emulated partition, which isn't in the mtd markup map.
Example of the script which completely flashes the device:
$ fastboot erase bootloader $ fastboot stage u-boot.bin $ fastboot oem board:write_bootloader $ fastboot reboot-bootloader $ fastboot oem board:erase_env $ fastboot erase misc $ fastboot erase super $ fastboot flash super rootfs $ fastboot reboot
Signed-off-by: Alexey Romanov avromanov@salutedevices.com
board/amlogic/ad401/fastboot.c | 222 +++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 board/amlogic/ad401/fastboot.c
diff --git a/board/amlogic/ad401/fastboot.c b/board/amlogic/ad401/fastboot.c new file mode 100644 index 0000000000..01da8efa5b --- /dev/null +++ b/board/amlogic/ad401/fastboot.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2023 SaluteDevices, Inc.
- */
+#include <common.h> +#include <env.h> +#include <fastboot.h> +#include <nand.h> +#include <asm/arch/nand.h> +#include <jffs2/load_kernel.h> +#include <linux/sizes.h> +#include <linux/types.h> +#include <linux/mtd/mtd.h>
+enum {
- OEM_BOARD_ERASE_CMD,
- OEM_BOARD_WRITE_BOOTLOADER_CMD,
+};
+struct defenv {
- char *name;
- char value[256];
+};
+static void save_defenv(struct defenv *e, size_t cnt) +{
- int i;
- for (i = 0; i < cnt; i++) {
const char *env_val = env_get(e[i].name);
if (env_val)
strlcpy(e[i].value, env_val, sizeof(e[i].value));
else
e[i].value[0] = '\0';
- }
+}
+static void set_defenv(struct defenv *e, size_t cnt) +{
- int i;
- for (i = 0; i < cnt; i++)
env_set(e[i].name, e[i].value);
+}
+static int fastboot_erase_env(void) +{
- char *const defenv_names[] = { "lock", "mtdparts", "mtdids" };
- struct defenv env[ARRAY_SIZE(defenv_names)];
- int err, i;
- for (i = 0; i < ARRAY_SIZE(env); i++)
env[i].name = defenv_names[i];
- printf("ENV is being erased...\n");
- /*
* Reset environment to the default, excluding 'lock' variable,
* because it reflects the fastboot's state after execution of
* 'flashing unlock' command, hence it must survive the env-erasing.
* Otherwise, further erase commands will fail on check_lock().
*
* Also, we have to save 'mtdparts' and 'mtdids' variables
* because they are necessary to obtain partition map.
*/
- save_defenv(env, ARRAY_SIZE(env));
- env_set_default(NULL, 0);
- set_defenv(env, ARRAY_SIZE(env));
- err = env_save();
- if (err) {
pr_err("Can't overwrite ENV-partition\n");
return err;
- }
Hmm so the fastboot locked state is saved in the U-Boot environment. There is probably a good reason for this (no secure storage for example). But this does not feel board specific.
Wouldn't it be better if we could just run "fastboot erase bootenv" and that the generic fastboot code does the right thing?
Are you proposing to modify the code of fastboot in such a way that if user send 'erase bootenv' string, then we call generic function to cleanup environment, instead of try to search (and erase) in partition schema 'bootenv' partition?
(which is env default, and ignoring some magic/specific variables)
- return 0;
+}
+static int fastboot_nand_write_tpl(struct mtd_info *mtd, void *buffer,
u32 offset, size_t size, int flags)
+{
- int boot_cpy_num = meson_bootloader_copy_num(BOOT_TPL);
- u64 size_per_copy = meson_bootloader_copy_size(mtd, BOOT_TPL);
- int i;
- for (i = 0; i < boot_cpy_num; i++) {
size_t retlen, len = size;
int ret;
ret = nand_write_skip_bad(mtd, offset + (i * size_per_copy),
&len, &retlen, offset + size_per_copy,
buffer, flags);
if (ret)
return ret;
- }
- return 0;
+}
+static int fastboot_nand_write_bl2(struct mtd_info *mtd, void *buffer,
u32 offset, size_t size, int flags)
+{
- int boot_cpy_num = meson_bootloader_copy_num(BOOT_BL2);
- u64 size_per_copy = meson_bootloader_copy_size(mtd, BOOT_BL2);
- int i;
- for (i = 0; i < boot_cpy_num; i++) {
int ret;
ret = meson_bootloader_write_bl2(mtd, buffer,
offset + (i * size_per_copy),
size, flags);
if (ret)
return ret;
- }
- return meson_bootloader_write_info_pages();
+}
+static int fastboot_nand_write_bootloader(void *buffer, u32 size) +{
- struct part_info *part;
- struct mtd_info *mtd = NULL;
- struct mtd_device *dev;
- u8 pnum;
- int ret;
- if (size < BL2_SIZE)
return 0;
- if (!buffer)
return -EINVAL;
- ret = mtdparts_init();
- if (ret) {
pr_err("Cannot initialize MTD partitions\n");
return ret;
- }
- ret = find_dev_and_part("bootloader", &dev, &pnum, &part);
- if (ret) {
pr_err("cannot find 'bootloader' partition\n");
return ret;
- }
- mtd = get_nand_dev_by_index(dev->id->num);
- if (!mtd)
return -EINVAL;
- ret = fastboot_nand_write_bl2(mtd, buffer, part->offset,
BL2_SIZE, WITH_WR_VERIFY);
- if (ret) {
pr_err("fastboot: failed to write BL2\n");
return ret;
- }
- ret = find_dev_and_part("tpl", &dev, &pnum, &part);
- if (ret) {
pr_err("cannot find 'bootloader' partition\n");
return ret;
- }
- mtd = get_nand_dev_by_index(dev->id->num);
- if (!mtd)
return -EINVAL;
- ret = fastboot_nand_write_tpl(mtd, buffer + BL2_SIZE, part->offset,
size - BL2_SIZE, WITH_WR_VERIFY);
- if (ret) {
pr_err("fastboot: failed to write TPL\n");
return ret;
- }
- return 0;
+}
+int get_oem_board_command(const char *cmd) +{
- const char *oem_commands[] = {
[OEM_BOARD_ERASE_CMD] = "erase_env",
[OEM_BOARD_WRITE_BOOTLOADER_CMD] = "write_bootloader",
- };
- int i;
- for (i = 0; i < ARRAY_SIZE(oem_commands); i++)
if (!strcmp(cmd, oem_commands[i]))
return i;
- return -EINVAL;
+}
+void fastboot_oem_board(const char *cmd_parameter, void *data, u32 size,
char *response)
+{
- char buf[128] = { 0 };
- int ret, cmd;
- cmd = get_oem_board_command(cmd_parameter);
- switch (cmd) {
case OEM_BOARD_ERASE_CMD:
ret = fastboot_erase_env();
break;
case OEM_BOARD_WRITE_BOOTLOADER_CMD:
ret = fastboot_nand_write_bootloader(data, size);
break;
default:
snprintf(buf, sizeof(buf),
"Command 'oem board %s' not supported",
cmd_parameter);
fastboot_fail(buf, response);
return;
- }
- if (ret < 0)
fastboot_fail("Failed to erase env partition", response);
- else
fastboot_okay(NULL, response);
+}
2.30.1

Hi Alexey,
On lun., mars 04, 2024 at 14:03, Alexey Romanov avromanov@salutedevices.com wrote:
Hello Mattijs,
On Thu, Feb 15, 2024 at 10:24:11AM +0100, Mattijs Korpershoek wrote:
On jeu., f'evr. 01, 2024 at 12:20, Alexey Romanov avromanov@salutedevices.com wrote:
An example of how we use fastboot oeam board subcommand for Sean Anderson.
1 - OEM_BOARD_WRITE_BOOTLOADER_CMD:
We use it for custom Amlogic bootloader + tpl flashing protocol.
2 - OEM_BOARD_ERASE_CMD:
Custom logic for erasing the env-emulated partition, which isn't in the mtd markup map.
Example of the script which completely flashes the device:
$ fastboot erase bootloader $ fastboot stage u-boot.bin $ fastboot oem board:write_bootloader $ fastboot reboot-bootloader $ fastboot oem board:erase_env $ fastboot erase misc $ fastboot erase super $ fastboot flash super rootfs $ fastboot reboot
Signed-off-by: Alexey Romanov avromanov@salutedevices.com
board/amlogic/ad401/fastboot.c | 222 +++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 board/amlogic/ad401/fastboot.c
diff --git a/board/amlogic/ad401/fastboot.c b/board/amlogic/ad401/fastboot.c new file mode 100644 index 0000000000..01da8efa5b --- /dev/null +++ b/board/amlogic/ad401/fastboot.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2023 SaluteDevices, Inc.
- */
+#include <common.h> +#include <env.h> +#include <fastboot.h> +#include <nand.h> +#include <asm/arch/nand.h> +#include <jffs2/load_kernel.h> +#include <linux/sizes.h> +#include <linux/types.h> +#include <linux/mtd/mtd.h>
+enum {
- OEM_BOARD_ERASE_CMD,
- OEM_BOARD_WRITE_BOOTLOADER_CMD,
+};
+struct defenv {
- char *name;
- char value[256];
+};
+static void save_defenv(struct defenv *e, size_t cnt) +{
- int i;
- for (i = 0; i < cnt; i++) {
const char *env_val = env_get(e[i].name);
if (env_val)
strlcpy(e[i].value, env_val, sizeof(e[i].value));
else
e[i].value[0] = '\0';
- }
+}
+static void set_defenv(struct defenv *e, size_t cnt) +{
- int i;
- for (i = 0; i < cnt; i++)
env_set(e[i].name, e[i].value);
+}
+static int fastboot_erase_env(void) +{
- char *const defenv_names[] = { "lock", "mtdparts", "mtdids" };
- struct defenv env[ARRAY_SIZE(defenv_names)];
- int err, i;
- for (i = 0; i < ARRAY_SIZE(env); i++)
env[i].name = defenv_names[i];
- printf("ENV is being erased...\n");
- /*
* Reset environment to the default, excluding 'lock' variable,
* because it reflects the fastboot's state after execution of
* 'flashing unlock' command, hence it must survive the env-erasing.
* Otherwise, further erase commands will fail on check_lock().
*
* Also, we have to save 'mtdparts' and 'mtdids' variables
* because they are necessary to obtain partition map.
*/
- save_defenv(env, ARRAY_SIZE(env));
- env_set_default(NULL, 0);
- set_defenv(env, ARRAY_SIZE(env));
- err = env_save();
- if (err) {
pr_err("Can't overwrite ENV-partition\n");
return err;
- }
Hmm so the fastboot locked state is saved in the U-Boot environment. There is probably a good reason for this (no secure storage for example). But this does not feel board specific.
Wouldn't it be better if we could just run "fastboot erase bootenv" and that the generic fastboot code does the right thing?
Are you proposing to modify the code of fastboot in such a way that if user send 'erase bootenv' string, then we call generic function to cleanup environment, instead of try to search (and erase) in partition schema 'bootenv' partition?
I would have liked to have a generic "erase bootenv" command yes, but your solution seems fine so no need to do something different.
Thank you for your patience with this !
(which is env default, and ignoring some magic/specific variables)
- return 0;
+}
+static int fastboot_nand_write_tpl(struct mtd_info *mtd, void *buffer,
u32 offset, size_t size, int flags)
+{
- int boot_cpy_num = meson_bootloader_copy_num(BOOT_TPL);
- u64 size_per_copy = meson_bootloader_copy_size(mtd, BOOT_TPL);
- int i;
- for (i = 0; i < boot_cpy_num; i++) {
size_t retlen, len = size;
int ret;
ret = nand_write_skip_bad(mtd, offset + (i * size_per_copy),
&len, &retlen, offset + size_per_copy,
buffer, flags);
if (ret)
return ret;
- }
- return 0;
+}
+static int fastboot_nand_write_bl2(struct mtd_info *mtd, void *buffer,
u32 offset, size_t size, int flags)
+{
- int boot_cpy_num = meson_bootloader_copy_num(BOOT_BL2);
- u64 size_per_copy = meson_bootloader_copy_size(mtd, BOOT_BL2);
- int i;
- for (i = 0; i < boot_cpy_num; i++) {
int ret;
ret = meson_bootloader_write_bl2(mtd, buffer,
offset + (i * size_per_copy),
size, flags);
if (ret)
return ret;
- }
- return meson_bootloader_write_info_pages();
+}
+static int fastboot_nand_write_bootloader(void *buffer, u32 size) +{
- struct part_info *part;
- struct mtd_info *mtd = NULL;
- struct mtd_device *dev;
- u8 pnum;
- int ret;
- if (size < BL2_SIZE)
return 0;
- if (!buffer)
return -EINVAL;
- ret = mtdparts_init();
- if (ret) {
pr_err("Cannot initialize MTD partitions\n");
return ret;
- }
- ret = find_dev_and_part("bootloader", &dev, &pnum, &part);
- if (ret) {
pr_err("cannot find 'bootloader' partition\n");
return ret;
- }
- mtd = get_nand_dev_by_index(dev->id->num);
- if (!mtd)
return -EINVAL;
- ret = fastboot_nand_write_bl2(mtd, buffer, part->offset,
BL2_SIZE, WITH_WR_VERIFY);
- if (ret) {
pr_err("fastboot: failed to write BL2\n");
return ret;
- }
- ret = find_dev_and_part("tpl", &dev, &pnum, &part);
- if (ret) {
pr_err("cannot find 'bootloader' partition\n");
return ret;
- }
- mtd = get_nand_dev_by_index(dev->id->num);
- if (!mtd)
return -EINVAL;
- ret = fastboot_nand_write_tpl(mtd, buffer + BL2_SIZE, part->offset,
size - BL2_SIZE, WITH_WR_VERIFY);
- if (ret) {
pr_err("fastboot: failed to write TPL\n");
return ret;
- }
- return 0;
+}
+int get_oem_board_command(const char *cmd) +{
- const char *oem_commands[] = {
[OEM_BOARD_ERASE_CMD] = "erase_env",
[OEM_BOARD_WRITE_BOOTLOADER_CMD] = "write_bootloader",
- };
- int i;
- for (i = 0; i < ARRAY_SIZE(oem_commands); i++)
if (!strcmp(cmd, oem_commands[i]))
return i;
- return -EINVAL;
+}
+void fastboot_oem_board(const char *cmd_parameter, void *data, u32 size,
char *response)
+{
- char buf[128] = { 0 };
- int ret, cmd;
- cmd = get_oem_board_command(cmd_parameter);
- switch (cmd) {
case OEM_BOARD_ERASE_CMD:
ret = fastboot_erase_env();
break;
case OEM_BOARD_WRITE_BOOTLOADER_CMD:
ret = fastboot_nand_write_bootloader(data, size);
break;
default:
snprintf(buf, sizeof(buf),
"Command 'oem board %s' not supported",
cmd_parameter);
fastboot_fail(buf, response);
return;
- }
- if (ret < 0)
fastboot_fail("Failed to erase env partition", response);
- else
fastboot_okay(NULL, response);
+}
2.30.1
-- Thank you, Alexey

Hello! Ping.
On Thu, Feb 01, 2024 at 12:20:25PM +0300, Alexey Romanov wrote:
Changes v2 since v1 at [1]: - Added an example of using the command as requsted by Sean Anderson [2].
Links: [1] https://lore.kernel.org/all/20231228152522.83291-1-avromanov@salutedevices.c... [2] https://lore.kernel.org/all/72ac233d-c18d-4f57-bc66-451fe0bd2997@seco.com/
Alexey Romanov (2): fastboot: introduce 'oem board' subcommand board: ad401: example of fastboot oem board realization
board/amlogic/ad401/fastboot.c | 222 +++++++++++++++++++++++++++++++++ drivers/fastboot/Kconfig | 7 ++ drivers/fastboot/fb_command.c | 15 +++ include/fastboot.h | 1 + 4 files changed, 245 insertions(+) create mode 100644 board/amlogic/ad401/fastboot.c
-- 2.30.1
participants (3)
-
Alexey Romanov
-
Dan Carpenter
-
Mattijs Korpershoek