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"
51 int FastBoot::Transport(string cmd
, void *p
, size_t size
, vector
<uint8_t> *input
)
53 if (m_pTrans
->write((void*)cmd
.data(), cmd
.size()))
59 while ( strncmp(buff
, "OKAY", 4) && strncmp(buff
, "FAIL", 4))
63 if (m_pTrans
->read(buff
, 64, &actual
))
66 if (strncmp(buff
, "DATA",4) == 0)
69 sz
= strtoul(buff
+4, nullptr, 16);
75 if (m_pTrans
->read(input
->data(), sz
, &rz
))
84 if (m_pTrans
->write(p
, sz
))
93 nt
.type
= uuu_notify::NOTIFY_CMD_INFO
;
99 if (strncmp(buff
, "OKAY", 4) == 0)
102 set_last_err_string(m_info
);
106 int FBGetVar::parser(char *p
)
112 string param
= get_next_param(m_cmd
, pos
);
114 if (param
.find(':') != string::npos
)
115 param
= get_next_param(m_cmd
, pos
);
117 if (str_to_upper(param
) != "GETVAR")
119 string err
= "Unknown Command:";
121 set_last_err_string(err
);
125 m_var
= get_next_param(m_cmd
, pos
);
128 int FBGetVar::run(CmdCtx
*ctx
)
131 if (dev
.open(ctx
->m_dev
))
139 if (fb
.Transport(cmd
, nullptr, 0))
146 int FBCmd::parser(char *p
)
154 if (parser_protocal(p
, pos
))
157 s
= get_next_param(m_cmd
, pos
);
159 if (str_to_upper(s
) != str_to_upper(m_fb_cmd
))
161 string err
= "Unknown command: ";
163 set_last_err_string(s
);
167 if(pos
!=string::npos
&& pos
< m_cmd
.size())
168 m_uboot_cmd
= m_cmd
.substr(pos
);
172 int FBCmd::run(CmdCtx
*ctx
)
174 BulkTrans dev
{m_timeout
};
175 if (dev
.open(ctx
->m_dev
))
184 if (fb
.Transport(cmd
, nullptr, 0))
190 int FBPartNumber::run(CmdCtx
*ctx
)
192 BulkTrans dev
{m_timeout
};
193 if (dev
.open(ctx
->m_dev
))
199 cmd
.format("%s:%s:%08x", m_fb_cmd
.c_str(), m_partition_name
.c_str(), (uint32_t)m_Size
);
201 if (fb
.Transport(cmd
, nullptr, 0))
207 int FBUpdateSuper::run(CmdCtx
*ctx
)
209 BulkTrans dev
{m_timeout
};
210 if (dev
.open(ctx
->m_dev
))
216 cmd
.format("%s:%s:%s", m_fb_cmd
.c_str(), m_partition_name
.c_str(), m_opt
.c_str());
218 if (fb
.Transport(cmd
, nullptr, 0))
224 int FBDownload::run(CmdCtx
*ctx
)
227 if (dev
.open(ctx
->m_dev
))
232 shared_ptr
<FileBuffer
> buff
= get_file_buffer(m_filename
);
237 cmd
.format("download:%08x", buff
->size());
239 if (fb
.Transport(cmd
, buff
->data(), buff
->size()))
245 int FBCopy::parser(char *p
)
252 s
= get_next_param(m_cmd
, pos
);
253 if (s
.find(":") != s
.npos
)
254 s
= get_next_param(m_cmd
, pos
);
256 if ((str_to_upper(s
) != "UCP"))
258 string err
= "Unknown command: ";
260 set_last_err_string(s
);
267 source
= get_next_param(m_cmd
, pos
);
268 dest
= get_next_param(m_cmd
, pos
);
272 set_last_err_string("ucp: source missed");
278 set_last_err_string("ucp: destination missed");
282 if (source
.find("T:") == 0 || source
.find("t:") == 0)
284 if (dest
.find("T:") == 0 || dest
.find("t:") == 0)
286 set_last_err_string("ucp just support one is remote file start with t:");
289 m_target_file
= source
.substr(2);
290 m_bDownload
= false; //upload a file
293 else if (dest
.find("T:") == 0 || dest
.find("t:") == 0)
295 m_target_file
= dest
.substr(2);
297 m_local_file
= source
;
298 get_file_buffer(source
, true);
302 set_last_err_string("ucp must a remote file name, start with t:<file name>");
308 int FBCopy::run(CmdCtx
*ctx
)
311 if (dev
.open(ctx
->m_dev
))
320 shared_ptr
<FileBuffer
> buff
= get_file_buffer(m_local_file
);
326 cmd
.format("WOpen:%s", m_target_file
.c_str());
327 if (fb
.Transport(cmd
, nullptr, 0))
329 if (fb
.m_info
== "DIR")
332 p
.append(m_local_file
);
333 string target
= m_target_file
;
335 target
+= p
.get_file_name();
337 cmd
.format("WOpen:%s", target
.c_str());
338 if (fb
.Transport(cmd
, nullptr, 0))
347 nt
.type
= uuu_notify::NOTIFY_TRANS_SIZE
;
348 nt
.total
= buff
->size();
351 for (i
= 0; i
< buff
->size(); i
+= this->m_Maxsize_pre_cmd
)
353 size_t sz
= buff
->size() - i
;
354 if (sz
> m_Maxsize_pre_cmd
)
355 sz
= m_Maxsize_pre_cmd
;
357 cmd
.format("donwload:%08X", sz
);
358 if (fb
.Transport(cmd
, buff
->data() + i
, sz
))
360 if (fb
.m_info
== "EPIPE")
361 set_last_err_string("pipe closed by target");
363 set_last_err_string("target return unknown error");
366 if (fb
.Transport(cmd
, nullptr, 0))
372 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
377 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
378 nt
.index
= buff
->size();
383 cmd
.format("ROpen:%s", m_target_file
.c_str());
384 if (fb
.Transport(cmd
, nullptr, 0))
388 nt
.type
= uuu_notify::NOTIFY_TRANS_SIZE
;
389 size_t total
= nt
.total
= strtoul(fb
.m_info
.c_str(), nullptr, 16);
398 localfile
.append(m_local_file
);
400 if (stat(localfile
.c_str(), &st
) == 0)
402 if (st
.st_mode
& S_IFDIR
)
406 t
.append(m_target_file
);
407 localfile
+= t
.get_file_name();
411 of
.open(localfile
, ofstream::binary
);
416 err
= "Fail to open file";
418 set_last_err_string(err
);
422 vector
<uint8_t> data
;
423 if (fb
.Transport("upload", nullptr, 0, &data
))
426 of
.write((const char*)data
.data(), data
.size());
428 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
429 nt
.index
+= data
.size();
432 if (data
.size() == 0)
435 } while (nt
.index
< total
|| total
== 0 ); // If total is 0, it is stream
437 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
442 if (fb
.Transport(cmd
, nullptr, 0))
448 int FBFlashCmd::parser(char *p
)
450 if (FBCmd::parser(p
))
453 string subcmd
= m_uboot_cmd
;
455 m_partition
= get_next_param(subcmd
, pos
);
456 if (m_partition
== "-raw2sparse")
459 m_partition
= get_next_param(subcmd
, pos
);
461 else if (m_partition
== "-S")
463 m_partition
= get_next_param(subcmd
, pos
);
464 bool conversion_success
= false;
465 m_sparse_limit
= str_to_uint64(m_partition
, &conversion_success
);
466 if (!conversion_success
)
468 set_last_err_string("FB: flash failed to parse size argument given to -S: "s
+ m_partition
);
471 m_partition
= get_next_param(subcmd
, pos
);
474 if (pos
== string::npos
|| m_partition
.empty())
476 set_last_err_string("Missed partition name");
479 m_filename
= get_next_param(subcmd
, pos
);
480 if (m_filename
.empty())
482 set_last_err_string("Missed file name");
486 if (!check_file_exist(m_filename
))
492 int FBFlashCmd::flash(FastBoot
*fb
, void * pdata
, size_t sz
)
495 cmd
.format("download:%08x", sz
);
497 if (fb
->Transport(cmd
, pdata
, sz
))
500 cmd
.format("flash:%s", m_partition
.c_str());
501 if (fb
->Transport(cmd
, nullptr, 0))
507 int FBFlashCmd::flash_raw2sparse(FastBoot
*fb
, shared_ptr
<FileBuffer
> pdata
, size_t block_size
, size_t max
)
511 vector
<uint8_t> data
;
513 if (max
> m_sparse_limit
)
514 max
= m_sparse_limit
;
516 sf
.init_header(block_size
, (max
+ block_size
-1) / block_size
);
518 data
.resize(block_size
);
521 bool bload
= pdata
->IsKnownSize();
523 nt
.type
= uuu_notify::NOTIFY_TRANS_SIZE
;
525 nt
.total
= pdata
->size();
534 while (!(r
=pdata
->request_data(data
, i
*block_size
, block_size
)))
536 int ret
= sf
.push_one_block(data
.data());
539 if (flash(fb
, sf
.m_data
.data(), sf
.m_data
.size()))
542 sf
.init_header(block_size
, (max
+ block_size
- 1) / block_size
);
545 ct
.chunk_type
= CHUNK_TYPE_DONT_CARE
;
548 ct
.total_sz
= sizeof(ct
);
550 sf
.push_one_chuck(&ct
, nullptr);
552 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
553 nt
.total
= i
* block_size
;
559 if (bload
!= pdata
->IsKnownSize())
561 nt
.type
= uuu_notify::NOTIFY_TRANS_SIZE
;
562 nt
.total
= pdata
->size();
565 bload
= pdata
->IsKnownSize();
569 if (r
== ERR_OUT_MEMORY
)
572 if (flash(fb
, sf
.m_data
.data(), sf
.m_data
.size()))
575 nt
.type
= uuu_notify::NOTIFY_TRANS_SIZE
;
576 nt
.total
= pdata
->size();
579 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
580 nt
.total
= pdata
->size();
586 int FBFlashCmd::run(CmdCtx
*ctx
)
588 FBGetVar
getvar((char*)"FB: getvar max-download-size");
589 if (getvar
.parser(nullptr))
594 size_t max
= getvar
.m_val
.empty() ? m_sparse_limit
: str_to_uint32(getvar
.m_val
);
596 BulkTrans dev
{m_timeout
};
597 if (dev
.open(ctx
->m_dev
))
604 size_t block_size
= 4096;
606 if (getvar
.parser((char*)"FB: getvar logical-block-size"))
608 if (!getvar
.run(ctx
))
609 block_size
= str_to_uint32(getvar
.m_val
);
613 set_last_err_string("Device report block_size is 0");
617 shared_ptr
<FileBuffer
> pdata
= get_file_buffer(m_filename
, true);
622 str
= "FB: getvar partition-size:";
625 if (getvar
.parser((char*)str
.c_str()))
631 m_totalsize
= str_to_uint64(getvar
.m_val
);
633 return flash_ffu(&fb
, pdata
);
636 return flash_raw2sparse(&fb
, pdata
, block_size
, max
);
639 shared_ptr
<FileBuffer
> pdata
= get_file_buffer(m_filename
, true);
640 if (pdata
== nullptr)
643 pdata
->request_data(sizeof(sparse_header
));
644 if (SparseFile::is_validate_sparse_file(pdata
->data(), sizeof(sparse_header
)))
645 { /* Limited max size to 16M for sparse file to avoid long timeout at read status*/
646 if (max
> m_sparse_limit
)
647 max
= m_sparse_limit
;
650 if (pdata
->size() <= max
)
652 pdata
->request_data(pdata
->size());
654 if (flash(&fb
, pdata
->data(), pdata
->size()))
660 pdata
->request_data(sizeof(sparse_header
));
661 sparse_header
* pfile
= (sparse_header
*)pdata
->data();
663 if (!SparseFile::is_validate_sparse_file(pdata
->data(), sizeof(sparse_header
)))
665 set_last_err_string("Sparse file magic miss matched");
671 chunk_header_t
* pheader
;
674 nt
.type
= uuu_notify::NOTIFY_TRANS_SIZE
;
675 nt
.total
= pfile
->total_blks
;
678 sf
.init_header(pfile
->blk_sz
, max
/ pfile
->blk_sz
);
681 for(size_t nblk
=0; nblk
< pfile
->total_chunks
&& pos
<= pdata
->size(); nblk
++)
683 pdata
->request_data(pos
+sizeof(chunk_header_t
)+sizeof(sparse_header
));
685 pheader
= SparseFile::get_next_chunk(pdata
->data(), pos
);
686 pdata
->request_data(pos
);
688 size_t sz
= sf
.push_one_chuck(pheader
, pheader
+ 1);
689 if (sz
== pheader
->total_sz
- sizeof(chunk_header_t
))
691 startblock
+= pheader
->chunk_sz
;
695 //whole chuck have not push into data.
696 if (flash(&fb
, sf
.m_data
.data(), sf
.m_data
.size()))
699 sf
.init_header(pfile
->blk_sz
, max
/ pfile
->blk_sz
);
702 ct
.chunk_type
= CHUNK_TYPE_DONT_CARE
;
703 ct
.chunk_sz
= startblock
;
705 ct
.total_sz
= sizeof(ct
);
707 sz
= sf
.push_one_chuck(&ct
, nullptr);
710 roll back pos to previous failure chunck and let it push again into new sparse file.
711 can't push it here because next chuck may big size chuck and need split as below else logic.
717 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
718 nt
.total
= startblock
;
723 size_t off
= ((uint8_t*)pheader
) - pdata
->data() + sz
+ sizeof(chunk_header_t
);
724 startblock
+= sz
/ pfile
->blk_sz
;
728 if (flash(&fb
, sf
.m_data
.data(), sf
.m_data
.size()))
731 sf
.init_header(pfile
->blk_sz
, max
/ pfile
->blk_sz
);
734 ct
.chunk_type
= CHUNK_TYPE_DONT_CARE
;
735 ct
.chunk_sz
= startblock
;
737 ct
.total_sz
= sizeof(ct
);
739 sz
= sf
.push_one_chuck(&ct
, nullptr);
741 sz
= sf
.push_raw_data(pdata
->data() + off
, pos
- off
);
743 startblock
+= sz
/ pfile
->blk_sz
;
746 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
747 nt
.total
= startblock
;
755 if (flash(&fb
, sf
.m_data
.data(), sf
.m_data
.size()))
758 sparse_header
* pf
= (sparse_header
*)sf
.m_data
.data();
759 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
760 nt
.total
= startblock
+ pf
->total_blks
;
766 bool FBFlashCmd::isffu(shared_ptr
<FileBuffer
> p
)
768 vector
<uint8_t> data
;
769 data
.resize(sizeof(FFU_SECURITY_HEADER
));
770 p
->request_data(data
, 0, sizeof(FFU_SECURITY_HEADER
));
772 FFU_SECURITY_HEADER
*h
= (FFU_SECURITY_HEADER
*)data
.data();
773 if (strncmp((const char*)h
->signature
, FFU_SECURITY_SIGNATURE
, sizeof(h
->signature
)) == 0)
779 int FBFlashCmd::flash_ffu_oneblk(FastBoot
*fb
, shared_ptr
<FileBuffer
> p
, size_t off
, size_t blksz
, size_t blkindex
)
783 sf
.init_header(blksz
, 10);
785 p
->request_data(off
+ blksz
);
788 ct
.chunk_type
= CHUNK_TYPE_DONT_CARE
;
789 ct
.chunk_sz
= blkindex
;
791 ct
.total_sz
= sizeof(ct
);
793 sf
.push_one_chuck(&ct
, nullptr);
795 if (sf
.push_one_block(p
->data() + off
))
798 return flash(fb
, sf
.m_data
.data(), sf
.m_data
.size());
801 int FBFlashCmd::flash_ffu(FastBoot
*fb
, shared_ptr
<FileBuffer
> p
)
803 p
->request_data(sizeof(FFU_SECURITY_HEADER
));
804 FFU_SECURITY_HEADER
*h
= (FFU_SECURITY_HEADER
*)p
->data();
805 if (strncmp((const char*)h
->signature
, FFU_SECURITY_SIGNATURE
, sizeof(h
->signature
)) != 0)
807 set_last_err_string("Invalidate FFU Security header signature");
812 off
= h
->dwCatalogSize
+ h
->dwHashTableSize
;
813 off
= round_up(off
, (size_t)h
->dwChunkSizeInKb
* 1024);
815 p
->request_data(off
+ sizeof(FFU_IMAGE_HEADER
));
816 FFU_IMAGE_HEADER
*pIh
= (FFU_IMAGE_HEADER
*)(p
->data() + off
);
818 if (strncmp((const char*)pIh
->Signature
, FFU_SIGNATURE
, sizeof(pIh
->Signature
)) != 0)
820 set_last_err_string("Invalidate FFU Security header signature");
824 off
+= pIh
->ManifestLength
+ pIh
->cbSize
;
825 off
= round_up(off
, (size_t)h
->dwChunkSizeInKb
* 1024);
827 p
->request_data(off
+ sizeof(FFU_STORE_HEADER
));
828 FFU_STORE_HEADER
*pIs
= (FFU_STORE_HEADER
*) (p
->data() + off
);
830 if(pIs
->MajorVersion
== 1)
831 off
+= pIs
->dwValidateDescriptorLength
+ offsetof(FFU_STORE_HEADER
, NumOfStores
);
833 off
+= pIs
->dwValidateDescriptorLength
+ sizeof(FFU_STORE_HEADER
);
835 p
->request_data(off
+ pIs
->dwWriteDescriptorLength
);
837 size_t block_off
= off
+ pIs
->dwWriteDescriptorLength
;
838 block_off
= round_up(block_off
, (size_t)h
->dwChunkSizeInKb
* 1024);
841 nt
.type
= uuu_notify::NOTIFY_TRANS_SIZE
;
842 nt
.total
= pIs
->dwWriteDescriptorCount
;
845 size_t currrent_block
= 0;
847 for (i
= 0; i
< pIs
->dwWriteDescriptorCount
; i
++)
849 FFU_BLOCK_DATA_ENTRY
*entry
= (FFU_BLOCK_DATA_ENTRY
*)(p
->data() + off
);
851 off
+= sizeof(FFU_BLOCK_DATA_ENTRY
) + (entry
->dwLocationCount
-1) * sizeof(_DISK_LOCATION
);
853 if (currrent_block
>= pIs
->dwInitialTableIndex
&& currrent_block
< pIs
->dwInitialTableIndex
+ pIs
->dwInitialTableCount
)
859 for (uint32_t loc
= 0; loc
< entry
->dwLocationCount
; loc
++)
861 //printf("block 0x%x write to 0x%x seek %d\n", currrent_block, entry->rgDiskLocations[loc].dwBlockIndex, entry->rgDiskLocations[loc].dwDiskAccessMethod);
862 uint32_t access
= entry
->rgDiskLocations
[loc
].dwDiskAccessMethod
;
864 if (entry
->rgDiskLocations
[loc
].dwDiskAccessMethod
== DISK_BEGIN
)
865 blockindex
= entry
->rgDiskLocations
[loc
].dwBlockIndex
;
867 blockindex
= m_totalsize
/ pIs
->dwBlockSizeInBytes
- 1 - entry
->rgDiskLocations
[loc
].dwBlockIndex
;
869 for (uint32_t blk
= 0; blk
< entry
->dwBlockCount
; blk
++)
871 if (flash_ffu_oneblk(fb
,
873 block_off
+ (currrent_block
+ blk
) * pIs
->dwBlockSizeInBytes
,
874 pIs
->dwBlockSizeInBytes
,
881 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;
885 currrent_block
+= entry
->dwBlockCount
;
888 nt
.type
= uuu_notify::NOTIFY_TRANS_POS
;