diff options
author | Maciej Żenczykowski <maze@google.com> | 2024-03-19 11:09:00 -0700 |
---|---|---|
committer | Maciej Żenczykowski <maze@google.com> | 2024-03-19 14:37:32 -0700 |
commit | 03c86c94ba3022c7b019f6a30c8f58251cc7b449 (patch) | |
tree | f67821b6a47e0666fdf4c7ac78f6d5a97570462b | |
parent | 82326fbd6b3b3cdb4b16cad70f502f9d69586fb7 (diff) | |
download | apf-03c86c94ba3022c7b019f6a30c8f58251cc7b449.tar.gz |
v5: rework LDDW/STDW for APFv6 mode to make them more useful
This makes LDDW/STDW honour firmware endianness in AFPv6+ mode,
thus making it possible to store things like APF filter age,
or APF version in APF counters without having to further
special case reading them.
It also removes the (no longer useful) indexing by OTHER_REG,
and effectively hardcodes the *4.
This makes it possible to more easily use counters as a sort of
'long-term' (ie. surviving from one packet to the next) memory.
(Reminder: the standard m[] slots are only valid for the duration
of the program.)
Note: We can no longer specify the counter to load/store via register,
but this seems pretty much impossible to sanely use anyway.
If it turns out this support is for some reason desirable,
we can easily change imm=0 to mean counter OTHER_REG,
but currently this just seems like wasted instructions...
After:
text data bss dec hex filename
3924 0 0 3924 f54 apf_interpreter.arm.o
text data bss dec hex filename
4938 0 0 4938 134a apf_interpreter.x86.o
Test: TreeHugger
Signed-off-by: Maciej Żenczykowski <maze@google.com>
Change-Id: Iccd1c5d40bf074cec8c02953182648709fd45289
-rw-r--r-- | v5/apf.h | 1 | ||||
-rw-r--r-- | v5/apf_interpreter.c | 59 | ||||
-rw-r--r-- | v5/apf_interpreter_source.c | 58 |
3 files changed, 64 insertions, 54 deletions
@@ -188,6 +188,7 @@ typedef union { // NOTE: Only APFv6+ implements R=1 'jbseq' version #define EXT_OPCODE 21 // Immediate value is one of *_EXT_OPCODE #define LDDW_OPCODE 22 // Load 4 bytes from data address (register + signed imm): "lddw R0, [5+R1]" + // LDDW/STDW in APFv6+ *mode* load/store from counter specified in imm. #define STDW_OPCODE 23 // Store 4 bytes to data address (register + signed imm): "stdw R0, [5+R1]" /* Write 1, 2 or 4 byte immediate to the output buffer and auto-increment the output buffer pointer. diff --git a/v5/apf_interpreter.c b/v5/apf_interpreter.c index 84afca8..f71db4d 100644 --- a/v5/apf_interpreter.c +++ b/v5/apf_interpreter.c @@ -260,6 +260,7 @@ typedef union { /* NOTE: Only APFv6+ implements R=1 'jbseq' version */ #define EXT_OPCODE 21 /* Immediate value is one of *_EXT_OPCODE */ #define LDDW_OPCODE 22 /* Load 4 bytes from data address (register + signed imm): "lddw R0, [5+R1]" */ + /* LDDW/STDW in APFv6+ *mode* load/store from counter specified in imm. */ #define STDW_OPCODE 23 /* Store 4 bytes to data address (register + signed imm): "stdw R0, [5+R1]" */ /* Write 1, 2 or 4 byte immediate to the output buffer and auto-increment the output buffer pointer. @@ -595,7 +596,7 @@ extern void APF_TRACE_HOOK(u32 pc, const u32* regs, const u8* program, #define ENFORCE_UNSIGNED(c) ((c)==(u32)(c)) u32 apf_version(void) { - return 20240313; + return 20240314; } typedef struct { @@ -975,34 +976,38 @@ static int do_apf_run(apf_context* ctx) { return PASS_PACKET; /* Bail out */ } break; - case LDDW_OPCODE: { - u32 offs = OTHER_REG + (u32)signed_imm; - u32 size = 4; - u32 val = 0; - /* Negative offsets wrap around the end of the address space. */ - /* This allows us to efficiently access the end of the */ - /* address space with one-byte immediates without using %=. */ - if (offs & 0x80000000) offs += ctx->ram_len; /* unsigned overflow intended */ - ASSERT_IN_DATA_BOUNDS(offs, size); - while (size--) val = (val << 8) | ctx->program[offs++]; - REG = val; - break; - } - case STDW_OPCODE: { - u32 offs = OTHER_REG + (u32)signed_imm; - u32 size = 4; - u32 val = REG; - /* Negative offsets wrap around the end of the address space. */ - /* This allows us to efficiently access the end of the */ - /* address space with one-byte immediates without using %=. */ - if (offs & 0x80000000) offs += ctx->ram_len; /* unsigned overflow intended */ - ASSERT_IN_DATA_BOUNDS(offs, size); - while (size--) { - ctx->program[offs++] = (val >> 24); - val <<= 8; + case LDDW_OPCODE: + case STDW_OPCODE: + if (ctx->v6) { + if (!imm) return PASS_PACKET; + if (imm > 0xFFFF) return PASS_PACKET; + if (imm * 4 > ctx->ram_len) return PASS_PACKET; + if (opcode == LDDW_OPCODE) { + REG = counter[-(s32)imm]; + } else { + counter[-(s32)imm] = REG; + } + } else { + u32 offs = OTHER_REG + (u32)signed_imm; + /* Negative offsets wrap around the end of the address space. */ + /* This allows us to efficiently access the end of the */ + /* address space with one-byte immediates without using %=. */ + if (offs & 0x80000000) offs += ctx->ram_len; /* unsigned overflow intended */ + u32 size = 4; + ASSERT_IN_DATA_BOUNDS(offs, size); + if (opcode == LDDW_OPCODE) { + u32 val = 0; + while (size--) val = (val << 8) | ctx->program[offs++]; + REG = val; + } else { + u32 val = REG; + while (size--) { + ctx->program[offs++] = (val >> 24); + val <<= 8; + } + } } break; - } case WRITE_OPCODE: { ASSERT_RETURN(ctx->tx_buf); ASSERT_RETURN(len_field); diff --git a/v5/apf_interpreter_source.c b/v5/apf_interpreter_source.c index 5951bfe..5523ac1 100644 --- a/v5/apf_interpreter_source.c +++ b/v5/apf_interpreter_source.c @@ -61,7 +61,7 @@ extern void APF_TRACE_HOOK(u32 pc, const u32* regs, const u8* program, #define ENFORCE_UNSIGNED(c) ((c)==(u32)(c)) u32 apf_version(void) { - return 20240313; + return 20240314; } typedef struct { @@ -441,34 +441,38 @@ static int do_apf_run(apf_context* ctx) { return PASS_PACKET; // Bail out } break; - case LDDW_OPCODE: { - u32 offs = OTHER_REG + (u32)signed_imm; - u32 size = 4; - u32 val = 0; - // Negative offsets wrap around the end of the address space. - // This allows us to efficiently access the end of the - // address space with one-byte immediates without using %=. - if (offs & 0x80000000) offs += ctx->ram_len; // unsigned overflow intended - ASSERT_IN_DATA_BOUNDS(offs, size); - while (size--) val = (val << 8) | ctx->program[offs++]; - REG = val; - break; - } - case STDW_OPCODE: { - u32 offs = OTHER_REG + (u32)signed_imm; - u32 size = 4; - u32 val = REG; - // Negative offsets wrap around the end of the address space. - // This allows us to efficiently access the end of the - // address space with one-byte immediates without using %=. - if (offs & 0x80000000) offs += ctx->ram_len; // unsigned overflow intended - ASSERT_IN_DATA_BOUNDS(offs, size); - while (size--) { - ctx->program[offs++] = (val >> 24); - val <<= 8; + case LDDW_OPCODE: + case STDW_OPCODE: + if (ctx->v6) { + if (!imm) return PASS_PACKET; + if (imm > 0xFFFF) return PASS_PACKET; + if (imm * 4 > ctx->ram_len) return PASS_PACKET; + if (opcode == LDDW_OPCODE) { + REG = counter[-(s32)imm]; + } else { + counter[-(s32)imm] = REG; + } + } else { + u32 offs = OTHER_REG + (u32)signed_imm; + // Negative offsets wrap around the end of the address space. + // This allows us to efficiently access the end of the + // address space with one-byte immediates without using %=. + if (offs & 0x80000000) offs += ctx->ram_len; // unsigned overflow intended + u32 size = 4; + ASSERT_IN_DATA_BOUNDS(offs, size); + if (opcode == LDDW_OPCODE) { + u32 val = 0; + while (size--) val = (val << 8) | ctx->program[offs++]; + REG = val; + } else { + u32 val = REG; + while (size--) { + ctx->program[offs++] = (val >> 24); + val <<= 8; + } + } } break; - } case WRITE_OPCODE: { ASSERT_RETURN(ctx->tx_buf); ASSERT_RETURN(len_field); |