4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright notice, this
11 * list of conditions and the following disclaimer in the documentation and/or
12 * other materials provided with the distribution.
14 * Neither the name of the NXP Semiconductor nor the names of its
15 * contributors may be used to endorse or promote products derived from this
16 * software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
33 Android fastboot protocol define at
34 https://android.googlesource.com/platform/system/core/+/master/fastboot/
47 #include "ffu_format.h"
53 int FastBoot::Transport(string cmd
, void *p
, size_t size
, vector
<uint8_t> *input
)
55 if (m_pTrans
->write((void*)cmd
.data(), cmd
.size()))
61 while ( strncmp(buff
, "OKAY", 4) && strncmp(buff
, "FAIL", 4))
65 if (m_pTrans
->read(buff
, 64, &actual
))
68 if (strncmp(buff
, "DATA",4) == 0)
71 sz
= strtoul(buff
+4, nullptr, 16);
77 if (m_pTrans
->read(input
->data(), sz
, &rz
))
86 if (m_pTrans
->write(p
, sz
))
95 nt
.type
= uuu_notify::NOTIFY_CMD_INFO
;
101 if (strncmp(buff
, "OKAY", 4) == 0)
104 set_last_err_string(m_info
);
108 int FBGetVar::parser(char *p
)
114 string param
= get_next_param(m_cmd
, pos
);
116 if (param
.find(':') != string::npos
)
117 param
= get_next_param(m_cmd
, pos
);
119 if (str_to_upper(param
) != "GETVAR")
121 string err
= "Unknown Command:";
123 set_last_err_string(err
);
127 m_var
= get_next_param(m_cmd
, pos
);
130 int FBGetVar::run(CmdCtx
*ctx
)
133 if (dev
.open(ctx
->m_dev
))
141 if (fb
.Transport(cmd
, nullptr, 0))
148 int FBCmd::parser(char *p
)
156 if (parser_protocal(p
, pos
))
159 s
= get_next_param(m_cmd
, pos
);
161 if (str_to_upper(s
) != str_to_upper(m_fb_cmd
))
163 string err
= "Unknown command: ";
165 set_last_err_string(s
);
169 if(pos
!=string::npos
&& pos
< m_cmd
.size())
170 m_uboot_cmd
= m_cmd
.substr(pos
);
174 int FBCmd::run(CmdCtx
*ctx
)
176 BulkTrans dev
{m_timeout
};
177 if (dev
.open(ctx
->m_dev
))
186 if (fb
.Transport(cmd
, nullptr, 0))
192 int FBPartNumber::run(CmdCtx
*ctx
)
194 BulkTrans dev
{m_timeout
};
195 if (dev
.open(ctx
->m_dev
))
201 cmd
.format("%s:%s:%08x", m_fb_cmd
.c_str(), m_partition_name
.c_str(), (uint32_t)m_Size
);
203 if (fb
.Transport(cmd
, nullptr, 0))
209 int FBUpdateSuper::run(CmdCtx
*ctx
)
211 BulkTrans dev
{m_timeout
};
212 if (dev
.open(ctx
->m_dev
))
218 cmd
.format("%s:%s:%s", m_fb_cmd
.c_str(), m_partition_name
.c_str(), m_opt
.c_str());
220 if (fb
.Transport(cmd
, nullptr, 0))
226 int FBDownload::run(CmdCtx
*ctx
)
229 if (dev
.open(ctx
->m_dev
))
234 shared_ptr
<FileBuffer
> buff
= get_file_buffer(m_filename
);
239 cmd
.format("download:%08x", buff
->size());
241 if (fb
.Transport(cmd
, buff
->data(), buff
->size()))
247 int FBUpload::run(CmdCtx
* ctx
)
250 if (dev
.open(ctx
->m_dev
))
256 cmd
.format("upload");
258 std::vector
<uint8_t> buff
;
259 if (fb
.Transport(cmd
, nullptr, buff
.size(), &buff
))
262 std::ofstream
fout(m_filename
, ios::out
| ios::trunc
);
263 std::copy(buff
.begin(), buff
.end(), std::ostream_iterator
<uint8_t>(fout
));
270 int FBCopy::parser(char *p
)
277 s
= get_next_param(m_cmd
, pos
);
278 if (s
.find(":") != s
.npos
)
279 s
= get_next_param(m_cmd
, pos
);
281 if ((str_to_upper(s
) != "UCP"))
283 string err
= "Unknown command: ";
285 set_last_err_string(s
);
292 source
= get_next_param(m_cmd
, pos
);
293 dest
= get_next_param(m_cmd
, pos
);
297 set_last_err_string("ucp: source missed");
303 set_last_err_string("ucp: destination missed");
307 if (source
.find("T:") == 0 || source
.find("t:") == 0)
309 if (dest
.find("T:") == 0 || dest
.find("t:") == 0)
311 set_last_err_string("ucp just support one is remote file start with t:");
314 m_target_file
= source
.substr(2);
315 m_bDownload
= false; //upload a file
318 else if (dest
.find("T:") == 0 || dest
.find("t:") == 0)
320 m_target_file
= dest
.substr(2);
322 m_local_file
= source
;
323 get_file_buffer(source
, true);
327 set_last_err_string("ucp must a remote file name, start with t:<file name>");
333 int FBCopy::run(CmdCtx
*ctx
)
336 if (dev
.open(ctx
->m_dev
))
345 shared_ptr
<FileBuffer
> buff
= get_file_buffer(m_local_file
);
351 cmd
.format("WOpen:%s", m_target_file
.c_str());
352 if (fb
.Transport(cmd
, nullptr, 0))
354 if (fb
.m_info
== "DIR")
357 p
.append(m_local_file
);
358 string target
= m_target_file
;
360 target
+= p
.get_file_name();
362 cmd
.format("WOpen:%s", target
.c_str());
363 if (fb
.Transport(cmd
, nullptr, 0))
372 nt
.type
= uuu_notify::NOTIFY_TRANS_SIZE
;
373 nt
.total
= buff
->size();
376 for (i
= 0; i
< buff
->size(); i
+= this->m_Maxsize_pre_cmd
)
378 size_t sz
= buff
->size() - i
;
379 if (sz
> m_Maxsize_pre_cmd
)
380 sz
= m_Maxsize_pre_cmd
;
382 cmd
.format("donwload:%08X", sz
);
383 if (fb
.Transport(cmd
, buff
->data() + i
, sz
))
385 if (fb
.m_info
== "EPIPE")
386 set_last_err_string("pipe closed by target");
388 set_last_err_string("target return unknown error");
391 if (fb
.Transport(cmd
, nullptr, 0))
397 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
402 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
403 nt
.index
= buff
->size();
408 cmd
.format("ROpen:%s", m_target_file
.c_str());
409 if (fb
.Transport(cmd
, nullptr, 0))
413 nt
.type
= uuu_notify::NOTIFY_TRANS_SIZE
;
414 size_t total
= nt
.total
= strtoul(fb
.m_info
.c_str(), nullptr, 16);
423 localfile
.append(m_local_file
);
425 if (stat(localfile
.c_str(), &st
) == 0)
427 if (st
.st_mode
& S_IFDIR
)
431 t
.append(m_target_file
);
432 localfile
+= t
.get_file_name();
436 of
.open(localfile
, ofstream::binary
);
441 err
= "Fail to open file";
443 set_last_err_string(err
);
447 vector
<uint8_t> data
;
448 if (fb
.Transport("upload", nullptr, 0, &data
))
451 of
.write((const char*)data
.data(), data
.size());
453 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
454 nt
.index
+= data
.size();
457 if (data
.size() == 0)
460 } while (nt
.index
< total
|| total
== 0 ); // If total is 0, it is stream
462 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
467 if (fb
.Transport(cmd
, nullptr, 0))
473 int FBFlashCmd::parser(char *p
)
475 if (FBCmd::parser(p
))
478 string subcmd
= m_uboot_cmd
;
480 m_partition
= get_next_param(subcmd
, pos
);
482 if (m_partition
== "-raw2sparse")
485 m_partition
= get_next_param(subcmd
, pos
);
488 if (m_partition
== "-scanterm")
491 m_partition
= get_next_param(subcmd
, pos
);
494 if (m_partition
== "-S")
496 m_partition
= get_next_param(subcmd
, pos
);
497 bool conversion_success
= false;
498 m_sparse_limit
= str_to_uint64(m_partition
, &conversion_success
);
499 if (!conversion_success
)
501 set_last_err_string("FB: flash failed to parse size argument given to -S: "s
+ m_partition
);
504 m_partition
= get_next_param(subcmd
, pos
);
507 if (pos
== string::npos
|| m_partition
.empty())
509 set_last_err_string("Missed partition name");
512 m_filename
= get_next_param(subcmd
, pos
);
513 if (m_filename
.empty())
515 set_last_err_string("Missed file name");
519 if (!check_file_exist(m_filename
))
525 int FBFlashCmd::flash(FastBoot
*fb
, void * pdata
, size_t sz
)
528 cmd
.format("download:%08x", sz
);
530 if (fb
->Transport(cmd
, pdata
, sz
))
533 cmd
.format("flash:%s", m_partition
.c_str());
534 if (fb
->Transport(cmd
, nullptr, 0))
540 int FBFlashCmd::flash_raw2sparse(FastBoot
*fb
, shared_ptr
<FileBuffer
> pdata
, size_t block_size
, size_t max
)
544 vector
<uint8_t> data
;
546 if (max
> m_sparse_limit
)
547 max
= m_sparse_limit
;
549 sf
.init_header(block_size
, (max
+ block_size
-1) / block_size
);
551 data
.resize(block_size
);
554 bool bload
= pdata
->IsKnownSize();
556 nt
.type
= uuu_notify::NOTIFY_TRANS_SIZE
;
558 nt
.total
= pdata
->size();
567 while (!(r
=pdata
->request_data(data
, i
*block_size
, block_size
)))
569 int ret
= sf
.push_one_block(data
.data());
572 if (flash(fb
, sf
.m_data
.data(), sf
.m_data
.size()))
575 sf
.init_header(block_size
, (max
+ block_size
- 1) / block_size
);
578 ct
.chunk_type
= CHUNK_TYPE_DONT_CARE
;
581 ct
.total_sz
= sizeof(ct
);
583 sf
.push_one_chuck(&ct
, nullptr);
585 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
586 nt
.total
= i
* block_size
;
592 if (bload
!= pdata
->IsKnownSize())
594 nt
.type
= uuu_notify::NOTIFY_TRANS_SIZE
;
595 nt
.total
= pdata
->size();
598 bload
= pdata
->IsKnownSize();
602 if (r
== ERR_OUT_MEMORY
)
605 if (flash(fb
, sf
.m_data
.data(), sf
.m_data
.size()))
608 nt
.type
= uuu_notify::NOTIFY_TRANS_SIZE
;
609 nt
.total
= pdata
->size();
612 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
613 nt
.total
= pdata
->size();
619 int FBFlashCmd::run(CmdCtx
*ctx
)
621 FBGetVar
getvar((char*)"FB: getvar max-download-size");
622 if (getvar
.parser(nullptr))
627 size_t max
= getvar
.m_val
.empty() ? m_sparse_limit
: str_to_uint32(getvar
.m_val
);
629 BulkTrans dev
{m_timeout
};
630 if (dev
.open(ctx
->m_dev
))
637 size_t block_size
= 4096;
639 if (getvar
.parser((char*)"FB: getvar logical-block-size"))
641 if (!getvar
.run(ctx
))
642 block_size
= str_to_uint32(getvar
.m_val
);
646 set_last_err_string("Device report block_size is 0");
650 shared_ptr
<FileBuffer
> pdata
= get_file_buffer(m_filename
, true);
655 str
= "FB: getvar partition-size:";
658 if (getvar
.parser((char*)str
.c_str()))
664 m_totalsize
= str_to_uint64(getvar
.m_val
);
666 return flash_ffu(&fb
, pdata
);
669 return flash_raw2sparse(&fb
, pdata
, block_size
, max
);
672 shared_ptr
<FileBuffer
> pdata
= get_file_buffer(m_filename
, true);
673 if (pdata
== nullptr)
676 pdata
->request_data(sizeof(sparse_header
));
677 if (SparseFile::is_validate_sparse_file(pdata
->data(), sizeof(sparse_header
)))
678 { /* Limited max size to 16M for sparse file to avoid long timeout at read status*/
679 if (max
> m_sparse_limit
)
680 max
= m_sparse_limit
;
685 pdata
->request_data(WIC_BOOTPART_SIZE
);
689 length
= ScanTerm(pdata
, pos
);
692 set_last_err_string("This wic have NOT terminate tag after bootloader, please use new yocto");
695 size_t offset
= pos
- length
;
698 set_last_err_string("This wic boot length is wrong");
701 return flash(&fb
, pdata
->data() + offset
, length
);
705 if (pdata
->size() <= max
)
707 pdata
->request_data(pdata
->size());
709 if (flash(&fb
, pdata
->data(), pdata
->size()))
715 pdata
->request_data(sizeof(sparse_header
));
716 sparse_header
* pfile
= (sparse_header
*)pdata
->data();
718 if (!SparseFile::is_validate_sparse_file(pdata
->data(), sizeof(sparse_header
)))
720 set_last_err_string("Sparse file magic miss matched");
726 chunk_header_t
* pheader
;
729 nt
.type
= uuu_notify::NOTIFY_TRANS_SIZE
;
730 nt
.total
= pfile
->total_blks
;
733 sf
.init_header(pfile
->blk_sz
, max
/ pfile
->blk_sz
);
736 for(size_t nblk
=0; nblk
< pfile
->total_chunks
&& pos
<= pdata
->size(); nblk
++)
738 pdata
->request_data(pos
+sizeof(chunk_header_t
)+sizeof(sparse_header
));
740 pheader
= SparseFile::get_next_chunk(pdata
->data(), pos
);
741 pdata
->request_data(pos
);
743 size_t sz
= sf
.push_one_chuck(pheader
, pheader
+ 1);
744 if (sz
== pheader
->total_sz
- sizeof(chunk_header_t
))
746 startblock
+= pheader
->chunk_sz
;
750 //whole chuck have not push into data.
751 if (flash(&fb
, sf
.m_data
.data(), sf
.m_data
.size()))
754 sf
.init_header(pfile
->blk_sz
, max
/ pfile
->blk_sz
);
757 ct
.chunk_type
= CHUNK_TYPE_DONT_CARE
;
758 ct
.chunk_sz
= startblock
;
760 ct
.total_sz
= sizeof(ct
);
762 sz
= sf
.push_one_chuck(&ct
, nullptr);
765 roll back pos to previous failure chunck and let it push again into new sparse file.
766 can't push it here because next chuck may big size chuck and need split as below else logic.
772 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
773 nt
.total
= startblock
;
778 size_t off
= ((uint8_t*)pheader
) - pdata
->data() + sz
+ sizeof(chunk_header_t
);
779 startblock
+= sz
/ pfile
->blk_sz
;
783 if (flash(&fb
, sf
.m_data
.data(), sf
.m_data
.size()))
786 sf
.init_header(pfile
->blk_sz
, max
/ pfile
->blk_sz
);
789 ct
.chunk_type
= CHUNK_TYPE_DONT_CARE
;
790 ct
.chunk_sz
= startblock
;
792 ct
.total_sz
= sizeof(ct
);
794 sz
= sf
.push_one_chuck(&ct
, nullptr);
796 sz
= sf
.push_raw_data(pdata
->data() + off
, pos
- off
);
798 startblock
+= sz
/ pfile
->blk_sz
;
801 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
802 nt
.total
= startblock
;
810 if (flash(&fb
, sf
.m_data
.data(), sf
.m_data
.size()))
813 sparse_header
* pf
= (sparse_header
*)sf
.m_data
.data();
814 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
815 nt
.total
= startblock
+ pf
->total_blks
;
821 bool FBFlashCmd::isffu(shared_ptr
<FileBuffer
> p
)
823 vector
<uint8_t> data
;
824 data
.resize(sizeof(FFU_SECURITY_HEADER
));
825 p
->request_data(data
, 0, sizeof(FFU_SECURITY_HEADER
));
827 FFU_SECURITY_HEADER
*h
= (FFU_SECURITY_HEADER
*)data
.data();
828 if (strncmp((const char*)h
->signature
, FFU_SECURITY_SIGNATURE
, sizeof(h
->signature
)) == 0)
834 int FBFlashCmd::flash_ffu_oneblk(FastBoot
*fb
, shared_ptr
<FileBuffer
> p
, size_t off
, size_t blksz
, size_t blkindex
)
838 sf
.init_header(blksz
, 10);
840 p
->request_data(off
+ blksz
);
843 ct
.chunk_type
= CHUNK_TYPE_DONT_CARE
;
844 ct
.chunk_sz
= blkindex
;
846 ct
.total_sz
= sizeof(ct
);
848 sf
.push_one_chuck(&ct
, nullptr);
850 if (sf
.push_one_block(p
->data() + off
))
853 return flash(fb
, sf
.m_data
.data(), sf
.m_data
.size());
856 int FBFlashCmd::flash_ffu(FastBoot
*fb
, shared_ptr
<FileBuffer
> p
)
858 p
->request_data(sizeof(FFU_SECURITY_HEADER
));
859 FFU_SECURITY_HEADER
*h
= (FFU_SECURITY_HEADER
*)p
->data();
860 if (strncmp((const char*)h
->signature
, FFU_SECURITY_SIGNATURE
, sizeof(h
->signature
)) != 0)
862 set_last_err_string("Invalidate FFU Security header signature");
867 off
= h
->dwCatalogSize
+ h
->dwHashTableSize
;
868 off
= round_up(off
, (size_t)h
->dwChunkSizeInKb
* 1024);
870 p
->request_data(off
+ sizeof(FFU_IMAGE_HEADER
));
871 FFU_IMAGE_HEADER
*pIh
= (FFU_IMAGE_HEADER
*)(p
->data() + off
);
873 if (strncmp((const char*)pIh
->Signature
, FFU_SIGNATURE
, sizeof(pIh
->Signature
)) != 0)
875 set_last_err_string("Invalidate FFU Security header signature");
879 off
+= pIh
->ManifestLength
+ pIh
->cbSize
;
880 off
= round_up(off
, (size_t)h
->dwChunkSizeInKb
* 1024);
882 p
->request_data(off
+ sizeof(FFU_STORE_HEADER
));
883 FFU_STORE_HEADER
*pIs
= (FFU_STORE_HEADER
*) (p
->data() + off
);
885 if(pIs
->MajorVersion
== 1)
886 off
+= pIs
->dwValidateDescriptorLength
+ offsetof(FFU_STORE_HEADER
, NumOfStores
);
888 off
+= pIs
->dwValidateDescriptorLength
+ sizeof(FFU_STORE_HEADER
);
890 p
->request_data(off
+ pIs
->dwWriteDescriptorLength
);
892 size_t block_off
= off
+ pIs
->dwWriteDescriptorLength
;
893 block_off
= round_up(block_off
, (size_t)h
->dwChunkSizeInKb
* 1024);
896 nt
.type
= uuu_notify::NOTIFY_TRANS_SIZE
;
897 nt
.total
= pIs
->dwWriteDescriptorCount
;
900 size_t currrent_block
= 0;
902 for (i
= 0; i
< pIs
->dwWriteDescriptorCount
; i
++)
904 FFU_BLOCK_DATA_ENTRY
*entry
= (FFU_BLOCK_DATA_ENTRY
*)(p
->data() + off
);
906 off
+= sizeof(FFU_BLOCK_DATA_ENTRY
) + (entry
->dwLocationCount
-1) * sizeof(_DISK_LOCATION
);
908 if (currrent_block
>= pIs
->dwInitialTableIndex
&& currrent_block
< pIs
->dwInitialTableIndex
+ pIs
->dwInitialTableCount
)
914 for (uint32_t loc
= 0; loc
< entry
->dwLocationCount
; loc
++)
916 //printf("block 0x%x write to 0x%x seek %d\n", currrent_block, entry->rgDiskLocations[loc].dwBlockIndex, entry->rgDiskLocations[loc].dwDiskAccessMethod);
917 uint32_t access
= entry
->rgDiskLocations
[loc
].dwDiskAccessMethod
;
919 if (entry
->rgDiskLocations
[loc
].dwDiskAccessMethod
== DISK_BEGIN
)
920 blockindex
= entry
->rgDiskLocations
[loc
].dwBlockIndex
;
922 blockindex
= m_totalsize
/ pIs
->dwBlockSizeInBytes
- 1 - entry
->rgDiskLocations
[loc
].dwBlockIndex
;
924 for (uint32_t blk
= 0; blk
< entry
->dwBlockCount
; blk
++)
926 if (flash_ffu_oneblk(fb
,
928 block_off
+ (currrent_block
+ blk
) * pIs
->dwBlockSizeInBytes
,
929 pIs
->dwBlockSizeInBytes
,
936 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
940 currrent_block
+= entry
->dwBlockCount
;
943 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;