痞子衡嵌入式:飛思卡爾i.MX RT系列微控制器啟動篇(6)- Bootable image格式與載入(elftosb/.bd)

来源:https://www.cnblogs.com/henjay724/archive/2018/06/02/9125869.html
-Advertisement-
Play Games

i.MXRT沒有內部FLASH,需要外接FLASH存儲器以存儲image。眾所周知,FLASH從結構上分為NOR和NAND,i.MXRT啟動同時支持這兩種FLASH,NOR FLASH可以實現XIP,NAND FLASH不可以XIP,為了相容所有FLASH,在設計i.MXRT bootable im... ...



  大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是飛思卡爾i.MX RT系列MCU的Bootable image格式與載入過程

  在i.MXRT啟動系列第三篇文章 飛思卡爾i.MX RT系列微控制器啟動篇(3)- Serial Downloader模式(sdphost, mfgtool) 里痞子衡在介紹使用sdphost引導啟動Flashloader時使用過一個名叫ivt_flashloader.bin的image文件,其實這個image文件就是Bootable image的一種,雖然痞子衡簡單分析過ivt_flashloader的組成,但介紹得並不詳盡,今天痞子衡會為大家系統地講解i.MXRT Bootable image。

一、什麼是Bootable image?

  如果你是一個有經驗的嵌入式開發者,肯定對image格式有所瞭解,我們通常開發的Application都是針對含內部FLASH的MCU而言的,比如Kinetis、LPC、STM32等MCU,其內部集成了一塊Parallel NOR FLASH,且FLASH地址是映射在ARM 4GB system address內的(一般從0x0地址開始),FLASH里存儲的直接就是我們編譯鏈接後生成的原始Application binary(.bin),沒有任何多餘的數據組成。或許你會說還有.hex, .srec等其他image格式,是的,但這些帶地址信息的image格式是為編程器或下載器服務的,這些image格式經過編程器或者下載器解析後真正下載進MCU內部FLASH的數據還是原始Application binary。這類MCU上電後CPU能直接從內部FLASH獲取Application代碼並原地執行(XIP),所以對這類MCU而言,Bootable image就是存儲在內部FLASH的Application binary(.bin)。
  但是以上經驗在開發i.MXRT時遇到了問題,i.MXRT沒有內部FLASH,需要外接FLASH存儲器以存儲image。眾所周知,FLASH從結構上分為NOR和NAND,i.MXRT啟動同時支持這兩種FLASH,NOR FLASH可以實現XIP,NAND FLASH不可以XIP,為了相容所有FLASH,在設計i.MXRT bootable image格式時必須以非XIP這種情況為基準。既然是非XIP執行,即意味著i.MXRT上電時會將image從外接FLASH拷貝到內部SRAM中去執行,在拷貝時必不可免要知道兩個重要的數據:image鏈接起始地址(決定image被拷貝到SRAM哪個地址)、image總長度(決定要從外部FLASH拷貝多長的image數據進SRAM),實際上除了這兩個最基本的數據外還有其他更高級的數據(配置、安全等特性),因此存儲在外接FLASH的i.MXRT Bootable image除了含有Application binary數據之外還必須含有額外的信息,這些額外的信息數據與Application binary共同組成i.MXRT Bootable image。至於這些額外的信息在Bootable image里是如何組織的,痞子衡在後面會繼續聊。

二、Bootable image鏈接空間

  一個image的鏈接空間分兩種,一種是只讀段(readonly code,data)的鏈接空間,另一種是讀寫段(readwrite data, STACK)的鏈接空間,這兩種鏈接空間要求的存儲介質特性不一樣,痞子衡逐一講解:
  前面講了i.MXRT同時支持外接NOR和NAND FLASH,其中NAND FLASH無法XIP,那麼存儲在NAND FLASH中的image只讀段必須要鏈接在SRAM里。i.MXRT內部有三種SRAM,分別是ITCM, DTCM, OCRAM,是不是這三種SRAM都可以被隨意鏈接呢?答案並不是!因為在Boot期間,BootROM也需要占用SRAM,用於存放BootROM的讀寫段,所以被BootROM占用的SRAM無法用於鏈接image的只讀段,如果強行鏈接,會導致BootROM在拷貝image只讀段時破壞自身讀寫段,從而發生不可預料的行為。下圖是RT1050 BootROM的memory map,從圖中可以得知BootROM占用的是0x20200000開始的OCRAM,並且看起來是整塊OCRAM都被占用了,所以不推薦使用OCRAM去鏈接image只讀段。
  黑科技:如果有朋友表示不服,RT1060/RT1050/RT1020的OCRAM是1MB/512KB/256KB,BootROM讀寫段不可能有這麼大,是的,痞子衡告訴你,其實BootROM數據段只要32KB(0x20200000 - 0x20207FFF),另外還需要4KB用載入initial non-XIP image(0x20208000 - 0x20208FFF),所以對於存儲在non-XIP FLASH的image你可以從0x20209000之後的空間里鏈接image只讀段,而對於存儲在XIP FLASH的image你可以從0x20208000之後的空間里鏈接image只讀段,這個秘密一般人痞子衡是不會告訴他的。

  前面講了存儲在NAND FLASH中的image只讀段鏈接註意事項,而對於可以XIP的NOR FLASH,除了跟NAND一樣可以將只讀段鏈接在SRAM外,還可以鏈接在i.MXRT分配給外接存儲器的XIP映射空間里,下表給出了Serial NOR(QSPI)和Parallel NOR(SEMC)各自的映射起始地址,需要註意的是Serial NOR支持的最大XIP空間為504MB,但是Parallel NOR支持的最大XIP空間只有16MB,別問痞子衡是怎麼知道的,痞子衡無所不知。

  至於image的讀寫段,在鏈接時就不用區別Non-XIP/XIP FLASH了,都只能放在SRAM里,並且不用考慮BootROM對SRAM的占用問題(因為不在一個時間域里被使用),只要註意不和image自身只讀段衝突就行。
  黑科技:有朋友註意到了SDRAM,是的i.MXRT也支持SDRAM,通過SEMC介面去實現SDRAM讀寫,所以如果外接了SDRAM並且使能的話,也可以將image只讀段/讀寫段放入SDRAM,關於SDRAM的使用,痞子衡會在後面文章里介紹。

三、Bootable image七大組成

  Bootable image是由一些額外的信息數據與Application binary共同組成的,那些額外的信息數據按功能分有6類,但這6類信息數據並不都是必須的,其中有4類是可選的,因此一個Bootable image最多由7部分組成,最少由3部分組成。下麵痞子衡按在FLASH里存儲位置從低到高的順序逐一介紹組成Bootable image的7大部分:

3.1 偏移0x0000: FDCB(Flash Device Configuration Block)

  第一個組成部分叫FDCB,是個可選組成,目前只用於Serial/Parallel NOR FLASH,FDCB是從FLASH的起始地址處開始存放的,也是Bootable image最開始部分。FDCB最大4KB,其本身沒有統一的與FLASH無關的structure,具體structure根據啟動FLASH的介面類型(Serial/Parallel)而定,其一般是用來存儲當前連接的FLASH的具體特性參數,BootROM上電會使用通用且可靠的FLASH介面控制器配置(即BootROM中預設參數配置,一般是比較低速的配置)去訪問外接FLASH並獲取FDCB,然後根據FDCB存儲的參數去重新配置FLASH介面控制器再去進一步訪問FLASH。下麵的結構體是Serial NOR的FDCB原型,此處痞子衡不會展開介紹這個結構體,留到後續介紹Serial NOR啟動再詳細介紹。

typedef struct _flexspi_nor_config
{
    flexspi_mem_config_t memConfig; //!< Common memory configuration info via FlexSPI
    uint32_t pageSize;              //!< Page size of Serial NOR
    uint32_t sectorSize;            //!< Sector size of Serial NOR
    uint8_t ipcmdSerialClkFreq;     //!< Clock frequency for IP command
    uint8_t isUniformBlockSize;     //!< Sector/Block size is the same
    uint8_t reserved0[2];           //!< Reserved for future use
    uint8_t serialNorType;          //!< Serial NOR Flash type: 0/1/2/3
    uint8_t needExitNoCmdMode;      //!< Need to exit NoCmd mode before other IP command
    uint8_t halfClkForNonReadCmd;   //!< Half the Serial Clock for non-read command: true/false
    uint8_t needRestoreNoCmdMode;   //!< Need to Restore NoCmd mode after IP commmand execution
    uint32_t blockSize;             //!< Block size
    uint32_t reserve2[11];          //!< Reserved for future use
} flexspi_nor_config_t;

3.2 偏移0x0400/0x1000: IVT(Image Vector Table)

  第二個組成部分叫IVT,是個必備組成,也是6類信息數據里的最核心數據,IVT是一個統一的與FLASH無關的structure,其原型如下麵結構體所示,從結構體定義我們得知,IVT中記錄了Application、DCD、BD、CSF的位置信息,這些信息對BootROM載入啟動至關重要。IVT大小固定為32byte,其在Bootable image中的偏移位置也是固定的(對於XIP FLASH而言偏移是0x1000,對於Non-XIP FLASH而言偏移是0x400)。有朋友會疑問為何IVT偏移地址是固定的?其實答案很簡單,因為BootROM必須要首先獲取IVT才能進一步找到其他信息數據,而IVT本身的位置信息沒有在其他地方被標明,所以只能在BootROM里用一個常量來記錄。

#define HAB_TAG_IVT0 0xd1     /**< Image Vector Table V0 */

/** @ref hab_header structure */
typedef struct hab_hdr {
    uint8_t tag;              /**< Tag field */
    uint8_t len[2];           /**< Length field in bytes (big-endian) */
    uint8_t par;              /**< Parameters field */
} hab_hdr_t;

/** @ref ivt structure */
struct hab_ivt_v0 {
    /** @ref hdr with tag #HAB_TAG_IVT0, length and HAB version fields */
    hab_hdr_t hdr;
    /** Absolute address of the first instruction to execute from the image */
    uint32_t entry;
    /** Reserved in this version of HAB: should be NULL. */
    uint32_t reserved1;
    /** Absolute address of the image DCD: may be NULL. */
    uint32_t dcd;
    /** Absolute address of the Boot Data: may be NULL, but not interpreted any further by HAB */
    uint32_t boot_data;
    /** Absolute address of the IVT.*/
    uint32_t self;
    /** Absolute address of the image CSF.*/
    uint32_t csf;
    /** Reserved in this version of HAB: should be zero. */
    uint32_t reserved2;
};

3.3 偏移0x0420/0x1020: BD(Boot Data)

  第三個組成部分叫BD,是個必備組成,是僅次於IVT的核心數據,BD也是一個統一的與FLASH無關的structure,其原型如下麵結構體所示,BD中記錄了Bootable image的起始地址與總長度。BD大小固定為16byte,BD信息雖然記錄在了IVT中,但其在Bootable image中的偏移位置並不是任意的,BD是緊挨著IVT的。

/** @ref boot_data structure */
typedef struct boot_data{
    uint32_t start;           /* Start address of the image */
    uint32_t size;            /* Size of the image */
    uint32_t plugin;          /* Plugin flag */
    uint32_t placeholder;     /* placehoder to make even 0x10 size */
} BOOT_DATA_T;

3.4 DCD(Device Configuration Data)

  第四個組成部分叫DCD,是個可選組成,目前主要用於SDRAM介面控制器(SEMC)的配置。由於i.MXRT內部SRAM size通常是夠用的,且訪問速度也很快,所以SDRAM並不一定要被使能,Bootable image常常不會包含DCD,所以痞子衡在這裡先不做展開,後續有必要會再介紹。下麵是SDK_2.3.1_EVKB-IMXRT1050包里hello_world工程(flexspi_nor)所使用DCD示例:

#define DCD_TAG_HEADER (0xD2)

const uint8_t dcd_data[] = {
    /*0000*/ DCD_TAG_HEADER,
    0x04,0x30,0x41,0xCC,0x03,0xAC,0x04,0x40,0x0F,0xC0,0x68,0xFF,0xFF,0xFF,0xFF,
    /*0010*/ 0x40,
    0x0F,0xC0,0x6C,0xFF,0xFF,0xFF,0xFF,0x40,0x0F,0xC0,0x70,0xFF,0xFF,0xFF,0xFF,

    ...

    /*0420*/ 0x00,
    0x00,0x00,0x01,0xCC,0x00,0x0C,0x04,0x40,0x2F,0x00,0x4C,0x50,0x21,0x0A,0x09,
};

3.5 偏移0x2000: Application Binary

  第五個組成部分是你最熟悉的Application binary,當然是個必備組成,其在Bootable image中的偏移位置是固定的(0x2000),關於Application本身這裡就不再贅述了。只特別提一點,那就是i.MXRT的Application只讀段(主要指ARM中斷向量表)並不可以從任意地址開始鏈接,有一個小小的限制,必須從選定的存儲器地址空間偏移0x2000之後開始鏈接(如選中ITCM,則必須要鏈接在0x00002000之後;如選中DTCM,則必須鏈接在0x20002000之後...),因為要預留至少8KB空間給IVT、BD、DCD等數據,這個限制是BootROM自身決定的,務必要註意

3.6 CSF(Command Sequence File)

  第六個組成部分叫CSF,是個特性組成,主要用於安全啟動的認證相關特性,痞子衡會在安全啟動里進一步介紹。

3.7 KeyBlob

  第七個組成部分叫KeyBlob,是個特性組成,主要用於安全啟動的加密相關特性,痞子衡會在安全啟動里進一步介紹。

  上圖是包含IVT、BD、DCD、Application、CSF的Bootable image的layout,這張圖很好地詮釋了IVT的作用。

四、Bootable image三種分類

  前面介紹了Bootable image最多有7大組成,有些是必備,有些是可選,有的是特性。而在實際應用中,主要是必備+特性的組合形成如下三種常用分類:

  • Unsigned Image: 這是最簡單的image類型,由IVT+BD+Application組成,主要用於產品開發階段。
  • Signed Image: 這是較複雜的image類型,由IVT+BD+Application+CSF組成,一般用於產品發佈階段。
  • Encrypted Image: 這是最複雜的image類型,由IVT+BD+Application+CSF+KeyBlob組成,主要用於對安全要求較高的產品中。

五、使用elftosb生成Bootable image

  恩智浦官方提供了一個用於生成Bootable image的工具,名叫elftosb,這個工具就在\Flashloader_i.MXRT1050_GA\Flashloader_RT1050_1.1\Tools\elftosb目錄下,這個工具可以用來生成所有類型的Bootable image,命令格式固定如下:

elftosb.exe -f imx -V -c config_application.bd -o ivt_application.bin application.out

  其中ivt_application.bin就是最終生成的Bootable image,命令所需要的2個輸入文件分別是application.out、config_application.bd,application.out就是你的Application工程編譯鏈接生成的ELF文件,config_application.bd是用戶配置文件,這個.bd文件主要是指示elftosb工具如何在Application binary基礎上添加IVT、BD等其他信息數據從而形成Bootable image,所以編寫.bd文件是關鍵步驟,bd文件有專門語法格式,但\Flashloader_i.MXRT1050_GA\Flashloader_RT1050_1.1\Tools\bd_file\imx10xx目錄下給了很多bd文件示例,我們只需要在某一個bd文件基礎上修改即可

  如果你追過痞子衡博客文章,你應該知道痞子衡曾經實測過RT1052的coremark性能,coremark工程已經上傳到痞子衡的github https://github.com/JayHeng/cortex-m_app,工程路徑在\cortex-m_app\apps\coremark_imxrt1052\bsp\build\coremark.eww,編譯此工程可得到coremark_a000.out和coremark_a000.bin文件,coremark程式只讀段鏈接在ITCM地址(0x0000a000),我們來試著使用elftosb將coremark程式轉換成bootable image,bd文件可參考imx-itcm-unsigned.bd,打開這個參考bd文件:

options {
    flags = 0x00;
    # Note: This is an example address, it can be any non-zero address in ITCM region
    startAddress = 0x8000;
    ivtOffset = 0x400;
    initialLoadSize = 0x2000;
    # Note: This is required if the default entrypoint is not the Reset_Handler 
    #       Please set the entryPointAddress to Reset_Handler address 
    // entryPointAddress = 0x60002411;
}

sources {
    elfFile = extern(0);
}

section (0)
{
}

  ivtOffset和initialLoadSize不用改,分別代表IVT和Application在Bootable image中的偏移地址,startAddress即BOOT_DATA_T.start,這個是可以修改的,牢記下麵公式:

startAddress + initialLoadSize = Application只讀段起始鏈接地址

  coremark_a000.out是鏈接在0xa000地址處的,0x8000 + 0x2000 = 0xa000,所以此處startAddress也無需改,唯一需要確認的是entryPointAddress,保險起見統一將entryPointAddress設成Application的複位中斷地址,即entryPointAddress = 0x0000ecd1。bd文件修改完成之後另存為config_coremark_a000.bd,讓我們試著執行下麵命令:

elftosb.exe -f imx -V -c config_coremark_a000.bd -o ivt_coremark_a000.bin coremark_a000.out

  分別打開coremark_a000.bin和ivt_coremark_a000.bin,可以看到ivt_coremark_a000.bin比coremark_a000.bin多了前8KB的數據,這前8KB里包含了有效的IVT(偏移0x400)和BD(偏移0x420)。

六、Bootable image的載入過程

  知道了Bootable image的構成,痞子衡最後再簡要為大家介紹一下i.MXRT BootROM是如何從外部存儲器中載入Bootable image進SRAM記憶體的。以non-XIP image載入為例(image鏈接在ITCM里),下圖顯示了i.MXRT載入image的四個階段:

  • 第一個階段即載入前,此時Bootable image完全存儲在外部Flash中,SRAM中沒有任何image數據;
  • 第二階段即初始載入,BootROM首先會從外部Flash讀取Bootable image前4KB數據進SRAM臨時緩存區(OCRAM:0x20208000 - 0x20208FFF),我們知道這4KB數據里包含了IVT和BD,BootROM從IVT和BD里獲取到Bootable image的目標地址(BOOT_DATA_T.start)以及總長度(BOOT_DATA_T.size),此時便可以開始做進一步載入;
  • 第三階段即內部轉移,由於BootROM已經從外部Flash讀取了4KB進SRAM臨時緩存區,為了避免重覆讀取,BootROM會把這4KB數據首先複製到Bootable image的目標地址(ITCM);
  • 第四階段即載入完成,BootROM會接著將剩下的Bootable image(BOOT_DATA_T.size - 4KB)從外部Flash中全部讀取出來存到目標區域(ITCM)完成全部載入。

  至此,飛思卡爾i.MX RT系列MCU的Bootable image格式與載入過程痞子衡便介紹完畢了,掌聲在哪裡~~~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本來想用正則Split一下sql語句中簡單場景的的GO,於是用^GO$(配合忽略大小寫和多行模式),可居然連這種情況都搞不掂: 如果刪掉$就能匹配了,但這顯然不是辦法,遂又在VS的C#交互視窗、RegexTester(.net寫的)、chrome控制台等地方試,發現只有chrome能匹配,而只要是基 ...
  • 最近有一個疑問:IList已經繼承了ICollection<T>,而ICollection<T>繼承了 IEnumerable<T>, IEnumerable,那為什麼IList還要繼承 IEnumerable<T>, IEnumerable? 於是我自己寫了介面測試:用dnSpy反編譯看到,Tes ...
  • ASP.NET Core MVC的Model Binding會將HTTP Request數據,以映射的方式對應到參數中。基本上跟ASP.NET MVC差不多,但能Binding的來源更多了一些。本篇將介紹ASP.NET Core的Model Binding。 Model Binding 要接收Cli ...
  • 巨硬build後發了15.7.1滿載期待的升級了。。結果NM淚奔................... 為啥 淚奔? 使用Chrome 調試閃退,MMP ,一想肯定是VS的鍋咯,各種抓頭髮。。 試試看看VS配置發現 ,多了點東西。。 都勾上後,瞬間跑起來了,但是問題來了,每次會新運行一個Chrome ...
  • 原文地址:http://www.cnblogs.com/qingyuan/archive/2010/05/11/1732415.html 1.什麼是委托,為什麼要使用委托 我正在埋頭苦寫程式,突然想喝水,但是又不想自己去掉杯水而打斷自己的思路,於是我就想讓女朋友去給我倒水。她去給我倒水,首先我得讓她 ...
  • Dapper.net的速度很快,最近看源碼,原來他orm的實現是通過編寫大量IL代碼實現的。 使用DynamicMethod,自己編織一個給實體賦值的方法。這種寫法效率很高,接近直接對屬性賦值。比使用反射賦值效率高10倍左右。 下麵分別使用Emit,反射,直接賦值100000次來進行對比測試。 下麵 ...
  • Mit6.824 是我在學習一些分散式系統方面的知識的時候偶然看到的,然後就開始嘗試跟課。不得不說,國外的課程難度是真的大,一周的時間居然要學一門 Go 語言,然後還要讀論文,進而做MapReduce 實驗。 由於 MR(MapReduce) 框架需要建立在 DFS(Distributed... ...
  • 開始前我們必須先認識絕對路徑與相對路徑 絕對路徑是從盤符開始的路徑 ;例如:/etc/sysconfig/network (從根直接指到network) 相對路徑是從當前自己所在位置開始的路徑;例如我當前不在/(根)之下,而在etc這個文件夾里。那我要去sysconfig這個文件。只需要cd sys ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...