aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Yuan <andrew.yuan@jaguarmicro.com>2025-02-07 16:09:20 +0000
committerPeter Maydell <peter.maydell@linaro.org>2025-02-07 16:09:20 +0000
commit76723b8ed7b8274a4e30cdf0a9f3e7c47ebe251a (patch)
treed5d36f30b89bdf564f253d2527bc011bcd97f7fe
parent3272ddee8caf65f31c131dfca2cf1c133edd745c (diff)
downloadqemu-76723b8ed7b8274a4e30cdf0a9f3e7c47ebe251a.zip
qemu-76723b8ed7b8274a4e30cdf0a9f3e7c47ebe251a.tar.gz
qemu-76723b8ed7b8274a4e30cdf0a9f3e7c47ebe251a.tar.bz2
hw/net/cadence_gem: Fix the mask/compare/disable-mask logic
Our current handling of the mask/compare logic in the Cadence GEM ethernet device is wrong: (1) we load the same byte twice from rx_buf when creating the compare value (2) we ignore the DISABLE_MASK flag The "Cadence IP for Gigabit Ethernet MAC Part Number: IP7014 IP Rev: R1p12 - Doc Rev: 1.3 User Guide" states that if the DISABLE_MASK bit in type2_compare_x_word_1 is set, the mask_value field in type2_compare_x_word_0 is used as an additional 2 byte Compare Value. Correct these bugs: * in the !disable_mask codepath, use lduw_le_p() so we correctly load a 16-bit value for comparison * in the disable_mask codepath, we load a full 4-byte value from rx_buf for the comparison, set the compare value to the whole of the cr0 register (i.e. the concatenation of the mask and compare fields), and set mask to 0xffffffff to force a 32-bit comparison Signed-off-by: Andrew Yuan <andrew.yuan@jaguarmicro.com> Message-id: 20241219061658.805-1-andrew.yuan@jaguarmicro.com Suggested-by: Philippe Mathieu-Daudé <philmd@linaro.org> [PMM: Expand commit message and comment] Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Edgar E. Iglesias <edgar.iglesias@gmail.com> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--hw/net/cadence_gem.c26
1 files changed, 21 insertions, 5 deletions
diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c
index f744054..80fbbac 100644
--- a/hw/net/cadence_gem.c
+++ b/hw/net/cadence_gem.c
@@ -909,8 +909,8 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr,
/* Compare A, B, C */
for (j = 0; j < 3; j++) {
- uint32_t cr0, cr1, mask, compare;
- uint16_t rx_cmp;
+ uint32_t cr0, cr1, mask, compare, disable_mask;
+ uint32_t rx_cmp;
int offset;
int cr_idx = extract32(reg, R_SCREENING_TYPE2_REG0_COMPARE_A_SHIFT + j * 6,
R_SCREENING_TYPE2_REG0_COMPARE_A_LENGTH);
@@ -946,9 +946,25 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr,
break;
}
- rx_cmp = rxbuf_ptr[offset] << 8 | rxbuf_ptr[offset];
- mask = FIELD_EX32(cr0, TYPE2_COMPARE_0_WORD_0, MASK_VALUE);
- compare = FIELD_EX32(cr0, TYPE2_COMPARE_0_WORD_0, COMPARE_VALUE);
+ disable_mask =
+ FIELD_EX32(cr1, TYPE2_COMPARE_0_WORD_1, DISABLE_MASK);
+ if (disable_mask) {
+ /*
+ * If disable_mask is set, mask_value is used as an
+ * additional 2 byte Compare Value; that is equivalent
+ * to using the whole cr0 register as the comparison value.
+ * Load 32 bits of data from rx_buf, and set mask to
+ * all-ones so we compare all 32 bits.
+ */
+ rx_cmp = ldl_le_p(rxbuf_ptr + offset);
+ mask = 0xFFFFFFFF;
+ compare = cr0;
+ } else {
+ rx_cmp = lduw_le_p(rxbuf_ptr + offset);
+ mask = FIELD_EX32(cr0, TYPE2_COMPARE_0_WORD_0, MASK_VALUE);
+ compare =
+ FIELD_EX32(cr0, TYPE2_COMPARE_0_WORD_0, COMPARE_VALUE);
+ }
if ((rx_cmp & mask) == (compare & mask)) {
matched = true;