2021-05-12 14:32:11
Linux核心crypto子系統深入理解
本文分析Linux kernel裡crypto子系統的大概實現,寫crypto子系統下的加速器驅動的時候可以參考下。crypto子系統支援加解密,壓縮解壓縮等功能。
Analysis will start from crypto test cases in crypto/testmgr.c, e.g. deflate.
上面的路徑上是核心裡這對crypto子系統的一個測試程式。通過分析這個程式可以大概看出crypto子系統向外提供的API. 整個系統的情況大概是這樣的:
crypto API <—> crypto core <—> crypto_register_alg
裝置驅動通過crypto_register_alg把一個裝置支援的演算法註冊到crypto系統裡。
註冊的時候會通過struct crypto_alg把相關的資訊傳遞給crypto core.
struct crypto_alg的結構是這樣的:
struct crypto_alg {
struct list_head cra_list;
struct list_head cra_users;
u32 cra_flags;
unsigned int cra_blocksize;
unsigned int cra_ctxsize;
unsigned int cra_alignmask;
int cra_priority;
atomic_t cra_refcnt;
char cra_name[CRYPTO_MAX_ALG_NAME];
char cra_driver_name[CRYPTO_MAX_ALG_NAME];
const struct crypto_type *cra_type;
union {
struct ablkcipher_alg ablkcipher;
struct blkcipher_alg blkcipher;
struct cipher_alg cipher;
struct compress_alg compress;
} cra_u;
int (*cra_init)(struct crypto_tfm *tfm);
void (*cra_exit)(struct crypto_tfm *tfm);
void (*cra_destroy)(struct crypto_alg *alg);
struct module *cra_module;
} CRYPTO_MINALIGN_ATTR;
這個結構的幾個關鍵的資訊是: cra_ctxsize, cra_u(下面以compress_alg說明), cra_init,
cra_exit.
這個結構表述的是演算法相關的系統,但是在執行一個請求的時候,還有維護一組上下文的資訊, 這些資訊記錄在結構體: struct crypto_tfm.
struct crypto_tfm {
u32 crt_flags;
union {
struct ablkcipher_tfm ablkcipher;
struct blkcipher_tfm blkcipher;
struct cipher_tfm cipher;
struct compress_tfm compress;
--> cot_compress
--> cot_decompress
} crt_u;
void (*exit)(struct crypto_tfm *tfm);
struct crypto_alg *__crt_alg;
void *__crt_ctx[] CRYPTO_MINALIGN_ATTR;
};
其中最後一個__crt_ctx是這個上下文的私有資料。上面的cra_ctxsize就是這個私有資料的size. cra_init是準備上下文的函數,比如,你用一個硬體裝置壓縮資料,實際的物理操作發生在這個硬體的一個佇列上,那麼就需要準備這個佇列,準備必要的快取等等。cra_exit 是退出上下文。cra_u裡是具體執行演算法的函數,比如可以壓縮和解壓縮的函數。
從裝置驅動的角度講, 裝置驅動只是看到了crypto_alg這個結構。這個結構裡的crypt_tfm 即一個操作執行的上下問是從哪裡知道的呢?畢竟crypto_alg這個結構裡的.cra_init, .cra_exit, .cra_u裡的.coa_compress和.coa_decompress都需要這個執行上下文。
我們在下面具體看一下。
知道這些內部的資料結構對我們理解外部的API有幫助。現在假設crypto的裝置驅動已經有了,那麼,其他的核心模組怎麼用呢? 其實一開頭我們已經講到crypto/testmgr.c測試程式。
測試的程式碼裡有非同步的測試和同步的測試流程,我們這裡先看同步的測試:
主要的邏輯就三個函數, 第一先需要分配一個壓縮的上下文(本文用壓縮的例子), 其實它就是crypto_tfm的包裝,和cryto_tfm是一樣的:
struct crypto_comp {
struct crypto_tfm base;
};
struct crypto_comp = crypto_alloc_comp(driver, type, mask), 這個過程中會呼叫到cra_init函數,這個函數是裝置驅動實現的,完成硬體相關的設定,上面已經提到過。
呼叫關係如下:
/* 分配一個壓縮解壓縮的上下文, 可以看到這裡的壓縮解壓縮的上下文完全就是crypto_tfm */
struct crypto_comp = crypto_alloc_comp(driver, type, mask);
--> crypto_alloc_base(alg_name, type, mask)
/* find algrithm: use alg_name, driver name */
--> alg = crypto_alg_mod_lookup(alg_name, type, mask);
/* 上下文是依據具體的演算法去分配的 */
--> tfm = __crypto_alloc_tfm(alg, type, mask);
/* 上下文中指定相關的演算法 */
--> tfm->__crt_alg = alg;
--> crypto_init_ops
/* 把相應的演算法中的壓縮解壓縮函數傳遞給上下文 */
--> crypto_init_compress_ops(tfm)
/* ops is struct compress_tfm */
--> ops->cot_compress = crypto_compress;
/* tfm->__crt_alg->cra_u.compress.coa_compress */
/*
* e.g. drivers/crypto/cavium/zip/zip_main.c
* struct crypto_alg zip_comp_deflate.
* will finally call zip_comp_compress!
*/
--> tfm->__crt_alg->cra_compress.coa_compress
--> ops->cot_decompress = crypto_decompress;
/*
* 在建立上下文的最後呼叫下,演算法裡的初始化函數,如果是和一個硬體
* 的驅動適配,那麼這裡就可以執行相應硬體初始化的內容。
*/
--> if (!tfm->exit && alg->cra_init && (err = alg->cra_init(tfm)))
第二,就是執行壓縮的操作:
crypto_comp_compress(tfm, input, ilen, result, &dlen)
crypto_comp_compress(crypto_comp, src, slen, dst, dlen)
/* so hardware can do compress here! */
--> compress_tfm->cot_compress;
第三,就是釋放這個壓縮的上下文
crypto_free_comp(comp)
核心雖然現在提供了壓縮的非同步介面,但是貌似還沒有驅動會用到。非同步介面的使用要比同步介面複雜一點。下面具體看看。
In alg_test_comp, async branch:
/* 和同步一樣,這裡也建立一個非同步的上下文 */
acomp = crypto_alloc_acomp(driver, type, mask);
/*
* 不過和同步介面不一樣的是,這裡又建立一個acomp_req的上下文, 後續的操作都圍繞著這個req結構展開。可以看到req裡面包含了非同步介面需要的回撥函數。
*/
req = acomp_request_alloc(tfm);
acomp_request_set_params(req, &src, &dst, ilen, dlen);
acomp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
crypto_req_done, &wait);
crypto_acomp_compress(req)
這裡需要說明的是,testmsg.c裡的這個acomp的測試程式裡加了wait/complete的相關內容。這裡應該是為了測試方便而加的,一般的非同步介面裡, 當硬體完成操作的時候,在中斷函數裡直接呼叫非同步介面的回撥函數就可以了。
相關文章