1 | // SPDX-License-Identifier: GPL-2.0+ |
---|---|

2 | /* |

3 | * Test cases for bitfield helpers. |

4 | */ |

5 | |

6 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |

7 | |

8 | #include <linux/kernel.h> |

9 | #include <linux/module.h> |

10 | #include <linux/bitfield.h> |

11 | |

12 | #define CHECK_ENC_GET_U(tp, v, field, res) do { \ |

13 | { \ |

14 | u##tp _res; \ |

15 | \ |

16 | _res = u##tp##_encode_bits(v, field); \ |

17 | if (_res != res) { \ |

18 | pr_warn("u" #tp "_encode_bits(" #v ", " #field ") is 0x%llx != " #res "\n",\ |

19 | (u64)_res); \ |

20 | return -EINVAL; \ |

21 | } \ |

22 | if (u##tp##_get_bits(_res, field) != v) \ |

23 | return -EINVAL; \ |

24 | } \ |

25 | } while (0) |

26 | |

27 | #define CHECK_ENC_GET_LE(tp, v, field, res) do { \ |

28 | { \ |

29 | __le##tp _res; \ |

30 | \ |

31 | _res = le##tp##_encode_bits(v, field); \ |

32 | if (_res != cpu_to_le##tp(res)) { \ |

33 | pr_warn("le" #tp "_encode_bits(" #v ", " #field ") is 0x%llx != 0x%llx\n",\ |

34 | (u64)le##tp##_to_cpu(_res), \ |

35 | (u64)(res)); \ |

36 | return -EINVAL; \ |

37 | } \ |

38 | if (le##tp##_get_bits(_res, field) != v) \ |

39 | return -EINVAL; \ |

40 | } \ |

41 | } while (0) |

42 | |

43 | #define CHECK_ENC_GET_BE(tp, v, field, res) do { \ |

44 | { \ |

45 | __be##tp _res; \ |

46 | \ |

47 | _res = be##tp##_encode_bits(v, field); \ |

48 | if (_res != cpu_to_be##tp(res)) { \ |

49 | pr_warn("be" #tp "_encode_bits(" #v ", " #field ") is 0x%llx != 0x%llx\n",\ |

50 | (u64)be##tp##_to_cpu(_res), \ |

51 | (u64)(res)); \ |

52 | return -EINVAL; \ |

53 | } \ |

54 | if (be##tp##_get_bits(_res, field) != v) \ |

55 | return -EINVAL; \ |

56 | } \ |

57 | } while (0) |

58 | |

59 | #define CHECK_ENC_GET(tp, v, field, res) do { \ |

60 | CHECK_ENC_GET_U(tp, v, field, res); \ |

61 | CHECK_ENC_GET_LE(tp, v, field, res); \ |

62 | CHECK_ENC_GET_BE(tp, v, field, res); \ |

63 | } while (0) |

64 | |

65 | static int test_constants(void) |

66 | { |

67 | /* |

68 | * NOTE |

69 | * This whole function compiles (or at least should, if everything |

70 | * is going according to plan) to nothing after optimisation. |

71 | */ |

72 | |

73 | CHECK_ENC_GET(16, 1, 0x000f, 0x0001); |

74 | CHECK_ENC_GET(16, 3, 0x00f0, 0x0030); |

75 | CHECK_ENC_GET(16, 5, 0x0f00, 0x0500); |

76 | CHECK_ENC_GET(16, 7, 0xf000, 0x7000); |

77 | CHECK_ENC_GET(16, 14, 0x000f, 0x000e); |

78 | CHECK_ENC_GET(16, 15, 0x00f0, 0x00f0); |

79 | |

80 | CHECK_ENC_GET_U(8, 1, 0x0f, 0x01); |

81 | CHECK_ENC_GET_U(8, 3, 0xf0, 0x30); |

82 | CHECK_ENC_GET_U(8, 14, 0x0f, 0x0e); |

83 | CHECK_ENC_GET_U(8, 15, 0xf0, 0xf0); |

84 | |

85 | CHECK_ENC_GET(32, 1, 0x00000f00, 0x00000100); |

86 | CHECK_ENC_GET(32, 3, 0x0000f000, 0x00003000); |

87 | CHECK_ENC_GET(32, 5, 0x000f0000, 0x00050000); |

88 | CHECK_ENC_GET(32, 7, 0x00f00000, 0x00700000); |

89 | CHECK_ENC_GET(32, 14, 0x0f000000, 0x0e000000); |

90 | CHECK_ENC_GET(32, 15, 0xf0000000, 0xf0000000); |

91 | |

92 | CHECK_ENC_GET(64, 1, 0x00000f0000000000ull, 0x0000010000000000ull); |

93 | CHECK_ENC_GET(64, 3, 0x0000f00000000000ull, 0x0000300000000000ull); |

94 | CHECK_ENC_GET(64, 5, 0x000f000000000000ull, 0x0005000000000000ull); |

95 | CHECK_ENC_GET(64, 7, 0x00f0000000000000ull, 0x0070000000000000ull); |

96 | CHECK_ENC_GET(64, 14, 0x0f00000000000000ull, 0x0e00000000000000ull); |

97 | CHECK_ENC_GET(64, 15, 0xf000000000000000ull, 0xf000000000000000ull); |

98 | |

99 | return 0; |

100 | } |

101 | |

102 | #define CHECK(tp, mask) do { \ |

103 | u64 v; \ |

104 | \ |

105 | for (v = 0; v < 1 << hweight32(mask); v++) \ |

106 | if (tp##_encode_bits(v, mask) != v << __ffs64(mask)) \ |

107 | return -EINVAL; \ |

108 | } while (0) |

109 | |

110 | static int test_variables(void) |

111 | { |

112 | CHECK(u8, 0x0f); |

113 | CHECK(u8, 0xf0); |

114 | CHECK(u8, 0x38); |

115 | |

116 | CHECK(u16, 0x0038); |

117 | CHECK(u16, 0x0380); |

118 | CHECK(u16, 0x3800); |

119 | CHECK(u16, 0x8000); |

120 | |

121 | CHECK(u32, 0x80000000); |

122 | CHECK(u32, 0x7f000000); |

123 | CHECK(u32, 0x07e00000); |

124 | CHECK(u32, 0x00018000); |

125 | |

126 | CHECK(u64, 0x8000000000000000ull); |

127 | CHECK(u64, 0x7f00000000000000ull); |

128 | CHECK(u64, 0x0001800000000000ull); |

129 | CHECK(u64, 0x0000000080000000ull); |

130 | CHECK(u64, 0x000000007f000000ull); |

131 | CHECK(u64, 0x0000000018000000ull); |

132 | CHECK(u64, 0x0000001f8000000ull); |

133 | |

134 | return 0; |

135 | } |

136 | |

137 | static int __init test_bitfields(void) |

138 | { |

139 | int ret = test_constants(); |

140 | |

141 | if (ret) { |

142 | pr_warn("constant tests failed!\n"); |

143 | return ret; |

144 | } |

145 | |

146 | ret = test_variables(); |

147 | if (ret) { |

148 | pr_warn("variable tests failed!\n"); |

149 | return ret; |

150 | } |

151 | |

152 | #ifdef TEST_BITFIELD_COMPILE |

153 | /* these should fail compilation */ |

154 | CHECK_ENC_GET(16, 16, 0x0f00, 0x1000); |

155 | u32_encode_bits(7, 0x06000000); |

156 | |

157 | /* this should at least give a warning */ |

158 | u16_encode_bits(0, 0x60000); |

159 | #endif |

160 | |

161 | pr_info("tests passed\n"); |

162 | |

163 | return 0; |

164 | } |

165 | module_init(test_bitfields) |

166 | |

167 | MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); |

168 | MODULE_LICENSE("GPL"); |

169 |