在linux中,firmware是指“韌體”,是硬體設備本身執行的一段程序,一般存放在設備flash內。在Linux系統中,裝置驅動程式處於核心態,而韌體檔案處於用戶態,因此需要一個安全穩定可靠的機制,用來確保裝置驅動程式成功載入韌體檔案。
本教學操作環境:linux7.3系統、Dell G3電腦。
linux firmware是什麼
#韌體(firmware)是硬體設備本身執行的程式。韌體一般存放在設備flash內。而出於成本和便利性的考慮,通常是先將硬體設備的運行程序打包為一個特定格式的固件文件,存儲到終端系統內,通過終端系統給硬體設備進行升級。
Linux核心開發過程中,開發人員調試外設驅動設備,例如觸控,充電,線性馬達,存儲,WIFI設備等,同樣存在需要更新韌體的情況。在Linux系統中,裝置驅動程式處於核心態,而韌體檔案處於用戶態,因此需要一個安全穩定可靠的機制,用來確保裝置驅動程式成功載入韌體檔案。
為了解決裝置驅動程式從核心態穩定載入用戶態韌體檔案的問題,Linux系統提供了韌體子系統。
Linux韌體子系統流程簡介
Linux韌體子系統基於sysfs 和uevent機制實作。
驅動程式呼叫韌體系統函數介面申請韌體之後,韌體子系統使用韌體編譯內核的方式去獲取韌體;如果獲取失敗,請使用韌體快取的方式去獲取韌體;如果仍然獲取失敗,就使用預設路徑核心直接尋找的方式去取得韌體。如果還是取得失敗,就透過回報uevent訊息給init進程。 init進程則接收到uevent訊息,過濾出subsystem類型為firmware的訊息。 init進程根據uevent訊息內指向的韌體資訊去查找固件,透過sysfs提供的文件節點接口,把獲取的固件內容從用戶態寫入內核態,從而使驅動程序,獲取到固件文件的數據。
Linux韌體系統提供了多種在不同場景下取得韌體檔案的方法。
1)直接編譯到核心的方式;
2)韌體快取的方式;
3)直接根據核心指定路徑的方式:
# 4)透過init程序來協助處理的方式;
Linux韌體子系統流程框圖
#Linux韌體子系統主要函數介面
主要函數介面:
通常申請韌體的過程比較耗時,以及處理韌體升級的過程比較耗時,因此可以採用非同步函數介面實現,或者在驅動程式內先創建工作隊列調用同步函數介面實現。
其中:#request_firmware_direct介面只在核心指定的路徑內尋找韌體,不使用uevent機制來取得韌體。
request_firmware_nowait介面是透過非同步的工作佇列去取得韌體,可以起到不阻塞驅動probe時間的作用。
request_firmware實現流程
request_firmware函數透過呼叫_request_firmware_prepare函數,設定不同的標誌位,實現不同的差異功能。 ############_request_firmware_prepare函數:############在開啟CONFIG_FW_LOADER巨集開關基礎上,先透過呼叫fw_get_builtin_firmware函數的方式,判斷韌體檔案是否編譯到內核。 ###############接著呼叫fw_lookup_and_allocate_buf函數,判斷全域fw_cache結構內鍊錶是否記錄過目前請求firmware的name。如果不存在目前請求firmware的name,則動態分配對應的記憶體空間並且新增目前請求firmware的name到全域的fw_cache結構內的鍊錶。 ###fw_get_filesystem_firmware函數
主要是透過核心提供的預設路徑去尋找韌體文件,呼叫kernel_read_file_from_path函數。如果沒有查找到韌體文件,則透過標誌位FW_OPT_USERHELPER判斷,是否啟用USER_HELPER模式實作。
其中:
Firmware系統內的預設路徑如下:
預設路徑可以透過kernel command line 來增加一個路徑,透過module_param_string介面傳遞給變數path來客製化新增路徑。
USER_HELPER模式
#在核心開啟CONFIG_FW_LOADER_USER_HELPER之後,才支援此功能。主要功能就是透過kernel上報uevent訊息給到init進程,透過init進程取得韌體資訊寫入底層sysfs節點。
fw_load_from_user_helper函數:
先呼叫fw_create_instance函數建立device設備,class文件和屬性文件,以及分配firmware_priv結構體。
接著在 /sys/class/firmware 下將建立一個目錄,該目錄使用裝置名稱作為它的目錄名稱。
此目錄包含三個屬性:
loading:
#設定為1:此屬性由負責裝載韌體的使用者空間設定1開始;
設定為0:當裝載過程完成;
設定為-1:將終止韌體裝載過程。
data:
用來接收韌體數據,設定完 loading 後,用戶空間進程把韌體寫入該屬性。
device:
/sys/devices 下對應入口的符號連結。
timeout:
預設申請firmware透過uevent方式最大逾時時間為60S,支援上層寫入逾時時間。
_request_firmware_load函數:
先停用uevent上報,透過呼叫device_add函數加入設備,觸發呼叫firmware_uevent函數。其中,填入uevent上報的資訊格式,包括韌體的名稱,超時時間,是否非同步。
下一步則啟用uevent上報功能,同時呼叫kobject_uevent函數,上報add動作型別給到上層ueventd。
接著呼叫fw_state_wait_timeout函數,在預設的逾時時間內等待上層ueventd的處理。
若逾時時間達到或收到完成量喚醒,則釋放先前申請的內存,釋放device,class等記憶體資訊。
ueventd相關firmware處理流程
Ueventd是init進程內重要的模組,它主要處理selinux,dev設備創建,監聽kernel上報uevent訊息,firmware韌體加載等內容。
FirmwareHandler處理流程:
FirmwareHandler內的HandleUevent方法主要是處理firmware韌體載入和底層節點的互動流程。
先判斷uevent訊息的subsystem類型是firmware欄位才處理,這個類型只有kernel內firmware模組才會被回報。
HandleUevent主要是透過一個主執行緒建立不同的子線程,並行分別處理來自kernel的不同驅動的firmware請求。
ProcessFirmwareEvent函數
首先是循環判斷ueventd支援的路徑內檢索韌體檔案是否存在;若存在,則寫入底層loading屬性檔案為1 ,同時拷貝取得的韌體文件,寫入到底層data檔案。完成之後則寫入底層loading屬性檔為0。
至此,kernel就取得到了用戶空間寫入的韌體檔案資訊。
其中:
ueventd 預設支援搜尋韌體的路徑:
來自 ueventd.rc檔案內指定的firmware_directory。
相關推薦:《Linux影片教學》
以上是linux firmware是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!