@@ -9,27 +9,6 @@
#include <linux/types.h>
#include <linux/bitrev.h>
-static int get_le_offset(int offset)
-{
- int closest_multiple_of_4;
-
- closest_multiple_of_4 = (offset / 4) * 4;
- offset -= closest_multiple_of_4;
- return closest_multiple_of_4 + (3 - offset);
-}
-
-static int get_reverse_lsw32_offset(int offset, size_t len)
-{
- int closest_multiple_of_4;
- int word_index;
-
- word_index = offset / 4;
- closest_multiple_of_4 = word_index * 4;
- offset -= closest_multiple_of_4;
- word_index = (len / 4) - word_index - 1;
- return word_index * 4 + offset;
-}
-
static void adjust_for_msb_right_quirk(u64 *to_write, int *box_start_bit,
int *box_end_bit, u8 *box_mask)
{
@@ -47,6 +26,48 @@ static void adjust_for_msb_right_quirk(u64 *to_write, int *box_start_bit,
*box_end_bit = new_box_end_bit;
}
+/**
+ * calculate_box_addr - Determine physical location of byte in buffer
+ * @box: Index of byte within buffer seen as a logical big-endian big number
+ * @len: Size of buffer in bytes
+ * @quirks: mask of QUIRK_LSW32_IS_FIRST and QUIRK_LITTLE_ENDIAN
+ *
+ * Function interprets the buffer as a @len byte sized big number, and returns
+ * the physical offset of the @box logical octet within it. Internally, it
+ * treats the big number as groups of 4 bytes. If @len is not a multiple of 4,
+ * the last group may be shorter.
+ *
+ * @QUIRK_LSW32_IS_FIRST gives the ordering of groups of 4 octets relative to
+ * each other. If set, the most significant group of 4 octets is last in the
+ * buffer (and may be truncated if @len is not a multiple of 4).
+ *
+ * @QUIRK_LITTLE_ENDIAN gives the ordering of bytes within each group of 4.
+ * If set, the most significant byte is last in the group. If @len takes the
+ * form of 4k+3, the last group will only be able to represent 24 bits, and its
+ * most significant octet is byte 2.
+ *
+ * Return: the physical offset into the buffer corresponding to the logical box.
+ */
+static int calculate_box_addr(int box, size_t len, u8 quirks)
+{
+ size_t offset_of_group, offset_in_group, this_group = box / 4;
+ size_t group_size;
+
+ if (quirks & QUIRK_LSW32_IS_FIRST)
+ offset_of_group = this_group * 4;
+ else
+ offset_of_group = len - ((this_group + 1) * 4);
+
+ group_size = min(4, len - offset_of_group);
+
+ if (quirks & QUIRK_LITTLE_ENDIAN)
+ offset_in_group = box - this_group * 4;
+ else
+ offset_in_group = group_size - (box - this_group * 4) - 1;
+
+ return offset_of_group + offset_in_group;
+}
+
/**
* packing - Convert numbers (currently u64) between a packed and an unpacked
* format. Unpacked means laid out in memory in the CPU's native
@@ -157,12 +178,7 @@ int packing(void *pbuf, u64 *uval, int startbit, int endbit, size_t pbuflen,
* effective addressing inside the pbuf (so it's not
* logical any longer).
*/
- box_addr = pbuflen - box - 1;
- if (quirks & QUIRK_LITTLE_ENDIAN)
- box_addr = get_le_offset(box_addr);
- if (quirks & QUIRK_LSW32_IS_FIRST)
- box_addr = get_reverse_lsw32_offset(box_addr,
- pbuflen);
+ box_addr = calculate_box_addr(box, pbuflen, quirks);
if (op == UNPACK) {
u64 pval;
@@ -151,6 +151,77 @@ the more significant 4-byte word.
We always think of our offsets as if there were no quirk, and we translate
them afterwards, before accessing the memory region.
+Note on buffer lengths not multiple of 4
+----------------------------------------
+
+To deal with memory layout quirks where groups of 4 bytes are laid out "little
+endian" relative to each other, but "big endian" within the group itself, the
+concept of groups of 4 bytes is intrinsic to the packing API (not to be
+confused with the memory access, which is performed byte by byte, though).
+
+With buffer lengths not multiple of 4, this means one group will be incomplete.
+Depending on the quirks, this may lead to discontinuities in the bit fields
+accessible through the buffer. The packing API assumes discontinuities were not
+the intention of the memory layout, so it avoids them by effectively logically
+shortening the most significant group of 4 octets to the number of octets
+actually available.
+
+Example with a 31 byte sized buffer given below. Physical buffer offsets are
+implicit, and increase from left to right within a group, and from top to
+bottom within a column.
+
+No quirks:
+
+::
+
+ 31 29 28 | Group 7 (most significant)
+ 27 26 25 24 | Group 6
+ 23 22 21 20 | Group 5
+ 19 18 17 16 | Group 4
+ 15 14 13 12 | Group 3
+ 11 10 9 8 | Group 2
+ 7 6 5 4 | Group 1
+ 3 2 1 0 | Group 0 (least significant)
+
+QUIRK_LSW32_IS_FIRST:
+
+::
+
+ 3 2 1 0 | Group 0 (least significant)
+ 7 6 5 4 | Group 1
+ 11 10 9 8 | Group 2
+ 15 14 13 12 | Group 3
+ 19 18 17 16 | Group 4
+ 23 22 21 20 | Group 5
+ 27 26 25 24 | Group 6
+ 30 29 28 | Group 7 (most significant)
+
+QUIRK_LITTLE_ENDIAN:
+
+::
+
+ 30 28 29 | Group 7 (most significant)
+ 24 25 26 27 | Group 6
+ 20 21 22 23 | Group 5
+ 16 17 18 19 | Group 4
+ 12 13 14 15 | Group 3
+ 8 9 10 11 | Group 2
+ 4 5 6 7 | Group 1
+ 0 1 2 3 | Group 0 (least significant)
+
+QUIRK_LITTLE_ENDIAN | QUIRK_LSW32_IS_FIRST:
+
+::
+
+ 0 1 2 3 | Group 0 (least significant)
+ 4 5 6 7 | Group 1
+ 8 9 10 11 | Group 2
+ 12 13 14 15 | Group 3
+ 16 17 18 19 | Group 4
+ 20 21 22 23 | Group 5
+ 24 25 26 27 | Group 6
+ 28 29 30 | Group 7 (most significant)
+
Intended use
------------