Linux内核中如何获取位图任意位段?寻找便捷位段读取宏
我有一段裸机环境下的代码(其中printf为裸机适配版本):
rd_data = *(__GICD + GICD_TYPER); printf("--- GICD Page Status (GICD_TYPER)----------------------\n"); printf("ITLineNumber(=SPIS/32): %d\n", BIT_FIELD_READER(rd_data, 3, 0)); printf("SecurityExtn: %d\n", BIT_FIELD_READER(rd_data,10,10)); printf("MBIS: %d\n", BIT_FIELD_READER(rd_data,16,16)); printf("LPIS: %d\n", BIT_FIELD_READER(rd_data,17,17)); printf("DVIS: %d\n", BIT_FIELD_READER(rd_data,18,18)); printf("IDbits: %d\n", BIT_FIELD_READER(rd_data,23,19)); printf("A3V: %d\n", BIT_FIELD_READER(rd_data,24,24)); printf("No1N: %d\n", BIT_FIELD_READER(rd_data,25,25));
其中BIT_FIELD_READER(rd_data, end_bit, start_bit)宏可以通过起止位快速提取寄存器位段,用法十分便捷。现在我想在内核代码中用printk打印外设寄存器内容,需要找一个可以直接复制替换的类似宏。
我已经在include/linux/bitmap.h中找到bitmap_get_value8(map, start),但它仅支持8位值;在include/linux/bitfield.h中发现了#define REG_FIELD_A GENMASK(6, 0)配合a = FIELD_GET(REG_FIELD_A, reg);的用法,但这种方式需要为每个字段单独定义掩码,繁琐且不够简洁。
请问Linux内核代码中是否存在可通过起止位(或长度)获取任意位段的宏?
Linux内核中没有直接提供和BIT_FIELD_READER(rd_data, end_bit, start_bit)完全一致的现成宏,但你可以基于内核已有的宏快速封装一个,或者用纯位运算实现一个兼容的版本,两种方式都可以直接复制替换使用:
方式一:基于内核bitfield.h封装
利用GENMASK(end, start)生成位掩码,再通过FIELD_GET提取值,封装后用法和原宏完全一致:
#include <linux/bitfield.h> #define BIT_FIELD_READER(val, end_bit, start_bit) \ FIELD_GET(GENMASK(end_bit, start_bit), val)
这个封装依赖include/linux/bitfield.h,但不需要额外定义每个字段,直接传入起止位即可提取对应位段。
方式二:纯位运算实现(不依赖内核特定头文件)
如果不想引入bitfield.h,可以用基础位运算实现相同逻辑:
#define BIT_FIELD_READER(val, end_bit, start_bit) \ (((val) >> (start_bit)) & ((1UL << ((end_bit) - (start_bit) + 1)) - 1))
该宏通过右移将目标位段移到最低位,再用生成的掩码提取对应值,支持任意合法的起止位组合。
两种方式都可以直接替换你裸机代码中的BIT_FIELD_READER,只需要把printf替换为printk即可在内核中使用。
内容的提问来源于stack exchange,提问作者Chan Kim

