xref: /OK3568_Linux_fs/kernel/lib/packing.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2*4882a593Smuzhiyun /* Copyright (c) 2016-2018, NXP Semiconductors
3*4882a593Smuzhiyun  * Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com>
4*4882a593Smuzhiyun  */
5*4882a593Smuzhiyun #include <linux/packing.h>
6*4882a593Smuzhiyun #include <linux/module.h>
7*4882a593Smuzhiyun #include <linux/bitops.h>
8*4882a593Smuzhiyun #include <linux/errno.h>
9*4882a593Smuzhiyun #include <linux/types.h>
10*4882a593Smuzhiyun 
get_le_offset(int offset)11*4882a593Smuzhiyun static int get_le_offset(int offset)
12*4882a593Smuzhiyun {
13*4882a593Smuzhiyun 	int closest_multiple_of_4;
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun 	closest_multiple_of_4 = (offset / 4) * 4;
16*4882a593Smuzhiyun 	offset -= closest_multiple_of_4;
17*4882a593Smuzhiyun 	return closest_multiple_of_4 + (3 - offset);
18*4882a593Smuzhiyun }
19*4882a593Smuzhiyun 
get_reverse_lsw32_offset(int offset,size_t len)20*4882a593Smuzhiyun static int get_reverse_lsw32_offset(int offset, size_t len)
21*4882a593Smuzhiyun {
22*4882a593Smuzhiyun 	int closest_multiple_of_4;
23*4882a593Smuzhiyun 	int word_index;
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun 	word_index = offset / 4;
26*4882a593Smuzhiyun 	closest_multiple_of_4 = word_index * 4;
27*4882a593Smuzhiyun 	offset -= closest_multiple_of_4;
28*4882a593Smuzhiyun 	word_index = (len / 4) - word_index - 1;
29*4882a593Smuzhiyun 	return word_index * 4 + offset;
30*4882a593Smuzhiyun }
31*4882a593Smuzhiyun 
bit_reverse(u64 val,unsigned int width)32*4882a593Smuzhiyun static u64 bit_reverse(u64 val, unsigned int width)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun 	u64 new_val = 0;
35*4882a593Smuzhiyun 	unsigned int bit;
36*4882a593Smuzhiyun 	unsigned int i;
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun 	for (i = 0; i < width; i++) {
39*4882a593Smuzhiyun 		bit = (val & (1 << i)) != 0;
40*4882a593Smuzhiyun 		new_val |= (bit << (width - i - 1));
41*4882a593Smuzhiyun 	}
42*4882a593Smuzhiyun 	return new_val;
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun 
adjust_for_msb_right_quirk(u64 * to_write,int * box_start_bit,int * box_end_bit,u8 * box_mask)45*4882a593Smuzhiyun static void adjust_for_msb_right_quirk(u64 *to_write, int *box_start_bit,
46*4882a593Smuzhiyun 				       int *box_end_bit, u8 *box_mask)
47*4882a593Smuzhiyun {
48*4882a593Smuzhiyun 	int box_bit_width = *box_start_bit - *box_end_bit + 1;
49*4882a593Smuzhiyun 	int new_box_start_bit, new_box_end_bit;
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 	*to_write >>= *box_end_bit;
52*4882a593Smuzhiyun 	*to_write = bit_reverse(*to_write, box_bit_width);
53*4882a593Smuzhiyun 	*to_write <<= *box_end_bit;
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 	new_box_end_bit   = box_bit_width - *box_start_bit - 1;
56*4882a593Smuzhiyun 	new_box_start_bit = box_bit_width - *box_end_bit - 1;
57*4882a593Smuzhiyun 	*box_mask = GENMASK_ULL(new_box_start_bit, new_box_end_bit);
58*4882a593Smuzhiyun 	*box_start_bit = new_box_start_bit;
59*4882a593Smuzhiyun 	*box_end_bit   = new_box_end_bit;
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun /**
63*4882a593Smuzhiyun  * packing - Convert numbers (currently u64) between a packed and an unpacked
64*4882a593Smuzhiyun  *	     format. Unpacked means laid out in memory in the CPU's native
65*4882a593Smuzhiyun  *	     understanding of integers, while packed means anything else that
66*4882a593Smuzhiyun  *	     requires translation.
67*4882a593Smuzhiyun  *
68*4882a593Smuzhiyun  * @pbuf: Pointer to a buffer holding the packed value.
69*4882a593Smuzhiyun  * @uval: Pointer to an u64 holding the unpacked value.
70*4882a593Smuzhiyun  * @startbit: The index (in logical notation, compensated for quirks) where
71*4882a593Smuzhiyun  *	      the packed value starts within pbuf. Must be larger than, or
72*4882a593Smuzhiyun  *	      equal to, endbit.
73*4882a593Smuzhiyun  * @endbit: The index (in logical notation, compensated for quirks) where
74*4882a593Smuzhiyun  *	    the packed value ends within pbuf. Must be smaller than, or equal
75*4882a593Smuzhiyun  *	    to, startbit.
76*4882a593Smuzhiyun  * @pbuflen: The length in bytes of the packed buffer pointed to by @pbuf.
77*4882a593Smuzhiyun  * @op: If PACK, then uval will be treated as const pointer and copied (packed)
78*4882a593Smuzhiyun  *	into pbuf, between startbit and endbit.
79*4882a593Smuzhiyun  *	If UNPACK, then pbuf will be treated as const pointer and the logical
80*4882a593Smuzhiyun  *	value between startbit and endbit will be copied (unpacked) to uval.
81*4882a593Smuzhiyun  * @quirks: A bit mask of QUIRK_LITTLE_ENDIAN, QUIRK_LSW32_IS_FIRST and
82*4882a593Smuzhiyun  *	    QUIRK_MSB_ON_THE_RIGHT.
83*4882a593Smuzhiyun  *
84*4882a593Smuzhiyun  * Return: 0 on success, EINVAL or ERANGE if called incorrectly. Assuming
85*4882a593Smuzhiyun  *	   correct usage, return code may be discarded.
86*4882a593Smuzhiyun  *	   If op is PACK, pbuf is modified.
87*4882a593Smuzhiyun  *	   If op is UNPACK, uval is modified.
88*4882a593Smuzhiyun  */
packing(void * pbuf,u64 * uval,int startbit,int endbit,size_t pbuflen,enum packing_op op,u8 quirks)89*4882a593Smuzhiyun int packing(void *pbuf, u64 *uval, int startbit, int endbit, size_t pbuflen,
90*4882a593Smuzhiyun 	    enum packing_op op, u8 quirks)
91*4882a593Smuzhiyun {
92*4882a593Smuzhiyun 	/* Number of bits for storing "uval"
93*4882a593Smuzhiyun 	 * also width of the field to access in the pbuf
94*4882a593Smuzhiyun 	 */
95*4882a593Smuzhiyun 	u64 value_width;
96*4882a593Smuzhiyun 	/* Logical byte indices corresponding to the
97*4882a593Smuzhiyun 	 * start and end of the field.
98*4882a593Smuzhiyun 	 */
99*4882a593Smuzhiyun 	int plogical_first_u8, plogical_last_u8, box;
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	/* startbit is expected to be larger than endbit */
102*4882a593Smuzhiyun 	if (startbit < endbit)
103*4882a593Smuzhiyun 		/* Invalid function call */
104*4882a593Smuzhiyun 		return -EINVAL;
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	value_width = startbit - endbit + 1;
107*4882a593Smuzhiyun 	if (value_width > 64)
108*4882a593Smuzhiyun 		return -ERANGE;
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	/* Check if "uval" fits in "value_width" bits.
111*4882a593Smuzhiyun 	 * If value_width is 64, the check will fail, but any
112*4882a593Smuzhiyun 	 * 64-bit uval will surely fit.
113*4882a593Smuzhiyun 	 */
114*4882a593Smuzhiyun 	if (op == PACK && value_width < 64 && (*uval >= (1ull << value_width)))
115*4882a593Smuzhiyun 		/* Cannot store "uval" inside "value_width" bits.
116*4882a593Smuzhiyun 		 * Truncating "uval" is most certainly not desirable,
117*4882a593Smuzhiyun 		 * so simply erroring out is appropriate.
118*4882a593Smuzhiyun 		 */
119*4882a593Smuzhiyun 		return -ERANGE;
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	/* Initialize parameter */
122*4882a593Smuzhiyun 	if (op == UNPACK)
123*4882a593Smuzhiyun 		*uval = 0;
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	/* Iterate through an idealistic view of the pbuf as an u64 with
126*4882a593Smuzhiyun 	 * no quirks, u8 by u8 (aligned at u8 boundaries), from high to low
127*4882a593Smuzhiyun 	 * logical bit significance. "box" denotes the current logical u8.
128*4882a593Smuzhiyun 	 */
129*4882a593Smuzhiyun 	plogical_first_u8 = startbit / 8;
130*4882a593Smuzhiyun 	plogical_last_u8  = endbit / 8;
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 	for (box = plogical_first_u8; box >= plogical_last_u8; box--) {
133*4882a593Smuzhiyun 		/* Bit indices into the currently accessed 8-bit box */
134*4882a593Smuzhiyun 		int box_start_bit, box_end_bit, box_addr;
135*4882a593Smuzhiyun 		u8  box_mask;
136*4882a593Smuzhiyun 		/* Corresponding bits from the unpacked u64 parameter */
137*4882a593Smuzhiyun 		int proj_start_bit, proj_end_bit;
138*4882a593Smuzhiyun 		u64 proj_mask;
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 		/* This u8 may need to be accessed in its entirety
141*4882a593Smuzhiyun 		 * (from bit 7 to bit 0), or not, depending on the
142*4882a593Smuzhiyun 		 * input arguments startbit and endbit.
143*4882a593Smuzhiyun 		 */
144*4882a593Smuzhiyun 		if (box == plogical_first_u8)
145*4882a593Smuzhiyun 			box_start_bit = startbit % 8;
146*4882a593Smuzhiyun 		else
147*4882a593Smuzhiyun 			box_start_bit = 7;
148*4882a593Smuzhiyun 		if (box == plogical_last_u8)
149*4882a593Smuzhiyun 			box_end_bit = endbit % 8;
150*4882a593Smuzhiyun 		else
151*4882a593Smuzhiyun 			box_end_bit = 0;
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 		/* We have determined the box bit start and end.
154*4882a593Smuzhiyun 		 * Now we calculate where this (masked) u8 box would fit
155*4882a593Smuzhiyun 		 * in the unpacked (CPU-readable) u64 - the u8 box's
156*4882a593Smuzhiyun 		 * projection onto the unpacked u64. Though the
157*4882a593Smuzhiyun 		 * box is u8, the projection is u64 because it may fall
158*4882a593Smuzhiyun 		 * anywhere within the unpacked u64.
159*4882a593Smuzhiyun 		 */
160*4882a593Smuzhiyun 		proj_start_bit = ((box * 8) + box_start_bit) - endbit;
161*4882a593Smuzhiyun 		proj_end_bit   = ((box * 8) + box_end_bit) - endbit;
162*4882a593Smuzhiyun 		proj_mask = GENMASK_ULL(proj_start_bit, proj_end_bit);
163*4882a593Smuzhiyun 		box_mask  = GENMASK_ULL(box_start_bit, box_end_bit);
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 		/* Determine the offset of the u8 box inside the pbuf,
166*4882a593Smuzhiyun 		 * adjusted for quirks. The adjusted box_addr will be used for
167*4882a593Smuzhiyun 		 * effective addressing inside the pbuf (so it's not
168*4882a593Smuzhiyun 		 * logical any longer).
169*4882a593Smuzhiyun 		 */
170*4882a593Smuzhiyun 		box_addr = pbuflen - box - 1;
171*4882a593Smuzhiyun 		if (quirks & QUIRK_LITTLE_ENDIAN)
172*4882a593Smuzhiyun 			box_addr = get_le_offset(box_addr);
173*4882a593Smuzhiyun 		if (quirks & QUIRK_LSW32_IS_FIRST)
174*4882a593Smuzhiyun 			box_addr = get_reverse_lsw32_offset(box_addr,
175*4882a593Smuzhiyun 							    pbuflen);
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 		if (op == UNPACK) {
178*4882a593Smuzhiyun 			u64 pval;
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 			/* Read from pbuf, write to uval */
181*4882a593Smuzhiyun 			pval = ((u8 *)pbuf)[box_addr] & box_mask;
182*4882a593Smuzhiyun 			if (quirks & QUIRK_MSB_ON_THE_RIGHT)
183*4882a593Smuzhiyun 				adjust_for_msb_right_quirk(&pval,
184*4882a593Smuzhiyun 							   &box_start_bit,
185*4882a593Smuzhiyun 							   &box_end_bit,
186*4882a593Smuzhiyun 							   &box_mask);
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 			pval >>= box_end_bit;
189*4882a593Smuzhiyun 			pval <<= proj_end_bit;
190*4882a593Smuzhiyun 			*uval &= ~proj_mask;
191*4882a593Smuzhiyun 			*uval |= pval;
192*4882a593Smuzhiyun 		} else {
193*4882a593Smuzhiyun 			u64 pval;
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 			/* Write to pbuf, read from uval */
196*4882a593Smuzhiyun 			pval = (*uval) & proj_mask;
197*4882a593Smuzhiyun 			pval >>= proj_end_bit;
198*4882a593Smuzhiyun 			if (quirks & QUIRK_MSB_ON_THE_RIGHT)
199*4882a593Smuzhiyun 				adjust_for_msb_right_quirk(&pval,
200*4882a593Smuzhiyun 							   &box_start_bit,
201*4882a593Smuzhiyun 							   &box_end_bit,
202*4882a593Smuzhiyun 							   &box_mask);
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 			pval <<= box_end_bit;
205*4882a593Smuzhiyun 			((u8 *)pbuf)[box_addr] &= ~box_mask;
206*4882a593Smuzhiyun 			((u8 *)pbuf)[box_addr] |= pval;
207*4882a593Smuzhiyun 		}
208*4882a593Smuzhiyun 	}
209*4882a593Smuzhiyun 	return 0;
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun EXPORT_SYMBOL(packing);
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
214*4882a593Smuzhiyun MODULE_DESCRIPTION("Generic bitfield packing and unpacking");
215