From: Andy Zhou Date: Wed, 6 Mar 2013 00:27:55 +0000 (-0800) Subject: nicira-ext: Add Nicira actions NXAST_STACK_PUSH and NXAST_STACK_POP. X-Git-Tag: sliver-openvswitch-1.10.90-1~10^2~102 X-Git-Url: http://git.onelab.eu/?p=sliver-openvswitch.git;a=commitdiff_plain;h=bd85dac14ed7fa42d5804592fd4e903826d9e899 nicira-ext: Add Nicira actions NXAST_STACK_PUSH and NXAST_STACK_POP. The Push action takes a single parameter. Any source allowed by NXAST_REG_MOVE is allowed to be pushed onto the stack. When the source is a bit field, its value will be right shifted to bit zero before being pushed onto the stack. The remaining bits will be set to zero. The Pop action also takes a single parameter. Any destination allowed by NXAST_REG_MOVE can be used as the destination of the action. The value, in case of a bit field, will be taken from top of the stack, starting from bit zero. The stack size is not limited. The initial 8KB is statically allocated to efficiently handle most common use cases. When more stack space is required, the stack can grow using malloc(). Signed-off-by: Andy Zhou Signed-off-by: Ben Pfaff --- diff --git a/NEWS b/NEWS index 7fb093269..235327821 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,8 @@ post-v1.10.0 - OpenFlow: * The "dec_mpls_ttl" and "set_mpls_ttl" actions from OpenFlow 1.1 and later are now implemented. + * New "stack" extension for use in actions, to push and pop from + NXM fields. v1.10.0 - xx xxx xxxx diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h index cdb19520c..f9b0af45d 100644 --- a/include/openflow/nicira-ext.h +++ b/include/openflow/nicira-ext.h @@ -308,6 +308,8 @@ enum nx_action_subtype { NXAST_POP_MPLS, /* struct nx_action_pop_mpls */ NXAST_SET_MPLS_TTL, /* struct nx_action_ttl */ NXAST_DEC_MPLS_TTL, /* struct nx_action_header */ + NXAST_STACK_PUSH, /* struct nx_action_stack */ + NXAST_STACK_POP, /* struct nx_action_stack */ }; /* Header for Nicira-defined actions. */ @@ -562,6 +564,23 @@ struct nx_action_reg_load { }; OFP_ASSERT(sizeof(struct nx_action_reg_load) == 24); +/* Action structure for NXAST_STACK_PUSH and NXAST_STACK_POP. + * + * Pushes (or pops) field[offset: offset + n_bits] to (or from) + * top of the stack. + */ +struct nx_action_stack { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* Length is 16. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_REG_PUSH or NXAST_REG_POP. */ + ovs_be16 offset; /* Bit offset into the field. */ + ovs_be32 field; /* The field used for push or pop. */ + ovs_be16 n_bits; /* (n_bits + 1) bits of the field. */ + uint8_t zero[6]; /* Reserved, must be zero. */ +}; +OFP_ASSERT(sizeof(struct nx_action_stack) == 24); + /* Action structure for NXAST_NOTE. * * This action has no effect. It is variable length. The switch does not diff --git a/lib/nx-match.c b/lib/nx-match.c index e5545dea6..6efc94df3 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -1312,3 +1312,149 @@ nxm_reg_load(const struct mf_subfield *dst, uint64_t src_data, sizeof src_data_be * 8); mf_write_subfield_flow(dst, &src_subvalue, flow); } + +/* nxm_parse_stack_action, works for both push() and pop(). */ +void +nxm_parse_stack_action(struct ofpact_stack *stack_action, const char *s) +{ + s = mf_parse_subfield(&stack_action->subfield, s); + if (*s != '\0') { + ovs_fatal(0, "%s: trailing garbage following push or pop", s); + } +} + +void +nxm_format_stack_push(const struct ofpact_stack *push, struct ds *s) +{ + ds_put_cstr(s, "push:"); + mf_format_subfield(&push->subfield, s); +} + +void +nxm_format_stack_pop(const struct ofpact_stack *pop, struct ds *s) +{ + ds_put_cstr(s, "pop:"); + mf_format_subfield(&pop->subfield, s); +} + +/* Common set for both push and pop actions. */ +static void +stack_action_from_openflow__(const struct nx_action_stack *nasp, + struct ofpact_stack *stack_action) +{ + stack_action->subfield.field = mf_from_nxm_header(ntohl(nasp->field)); + stack_action->subfield.ofs = ntohs(nasp->offset); + stack_action->subfield.n_bits = ntohs(nasp->n_bits); +} + +static void +nxm_stack_to_nxast__(const struct ofpact_stack *stack_action, + struct nx_action_stack *nasp) +{ + nasp->offset = htons(stack_action->subfield.ofs); + nasp->n_bits = htons(stack_action->subfield.n_bits); + nasp->field = htonl(stack_action->subfield.field->nxm_header); +} + +enum ofperr +nxm_stack_push_from_openflow(const struct nx_action_stack *nasp, + struct ofpbuf *ofpacts) +{ + struct ofpact_stack *push; + + push = ofpact_put_STACK_PUSH(ofpacts); + stack_action_from_openflow__(nasp, push); + + return nxm_stack_push_check(push, NULL); +} + +enum ofperr +nxm_stack_pop_from_openflow(const struct nx_action_stack *nasp, + struct ofpbuf *ofpacts) +{ + struct ofpact_stack *pop; + + pop = ofpact_put_STACK_POP(ofpacts); + stack_action_from_openflow__(nasp, pop); + + return nxm_stack_pop_check(pop, NULL); +} + +enum ofperr +nxm_stack_push_check(const struct ofpact_stack *push, + const struct flow *flow) +{ + return mf_check_src(&push->subfield, flow); +} + +enum ofperr +nxm_stack_pop_check(const struct ofpact_stack *pop, + const struct flow *flow) +{ + return mf_check_dst(&pop->subfield, flow); +} + +void +nxm_stack_push_to_nxast(const struct ofpact_stack *stack, + struct ofpbuf *openflow) +{ + nxm_stack_to_nxast__(stack, ofputil_put_NXAST_STACK_PUSH(openflow)); +} + +void +nxm_stack_pop_to_nxast(const struct ofpact_stack *stack, + struct ofpbuf *openflow) +{ + nxm_stack_to_nxast__(stack, ofputil_put_NXAST_STACK_POP(openflow)); +} + +/* nxm_execute_stack_push(), nxm_execute_stack_pop(). */ +static void +nx_stack_push(struct ofpbuf *stack, union mf_subvalue *v) +{ + ofpbuf_put(stack, v, sizeof *v); +} + +static union mf_subvalue * +nx_stack_pop(struct ofpbuf *stack) +{ + union mf_subvalue *v = NULL; + + if (stack->size) { + stack->size -= sizeof *v; + v = (union mf_subvalue *) ofpbuf_tail(stack); + } + + return v; +} + +void +nxm_execute_stack_push(const struct ofpact_stack *push, + const struct flow *flow, struct ofpbuf *stack) +{ + union mf_subvalue dst_value; + + mf_read_subfield(&push->subfield, flow, &dst_value); + nx_stack_push(stack, &dst_value); +} + +void +nxm_execute_stack_pop(const struct ofpact_stack *pop, + struct flow *flow, struct ofpbuf *stack) +{ + union mf_subvalue *src_value; + + src_value = nx_stack_pop(stack); + + /* Only pop if stack is not empty. Otherwise, give warning. */ + if (src_value) { + mf_write_subfield_flow(&pop->subfield, src_value, flow); + } else { + if (!VLOG_DROP_WARN(&rl)) { + char *flow_str = flow_to_string(flow); + VLOG_WARN_RL(&rl, "Failed to pop from an empty stack. On flow \n" + " %s", flow_str); + free(flow_str); + } + } +} diff --git a/lib/nx-match.h b/lib/nx-match.h index 6a57297cb..7d316d800 100644 --- a/lib/nx-match.h +++ b/lib/nx-match.h @@ -29,10 +29,12 @@ struct match; struct mf_subfield; struct ofpact_reg_move; struct ofpact_reg_load; +struct ofpact_stack; struct ofpbuf; struct nx_action_reg_load; struct nx_action_reg_move; + /* Nicira Extended Match (NXM) flexible flow match helper functions. * * See include/openflow/nicira-ext.h for NXM specification. @@ -83,6 +85,30 @@ void nxm_execute_reg_load(const struct ofpact_reg_load *, struct flow *); void nxm_reg_load(const struct mf_subfield *, uint64_t src_data, struct flow *); +void nxm_parse_stack_action(struct ofpact_stack *, const char *); + +void nxm_format_stack_push(const struct ofpact_stack *, struct ds *); +void nxm_format_stack_pop(const struct ofpact_stack *, struct ds *); + +enum ofperr nxm_stack_push_from_openflow(const struct nx_action_stack *, + struct ofpbuf *ofpacts); +enum ofperr nxm_stack_pop_from_openflow(const struct nx_action_stack *, + struct ofpbuf *ofpacts); +enum ofperr nxm_stack_push_check(const struct ofpact_stack *, + const struct flow *); +enum ofperr nxm_stack_pop_check(const struct ofpact_stack *, + const struct flow *); + +void nxm_stack_push_to_nxast(const struct ofpact_stack *, + struct ofpbuf *openflow); +void nxm_stack_pop_to_nxast(const struct ofpact_stack *, + struct ofpbuf *openflow); + +void nxm_execute_stack_push(const struct ofpact_stack *, + const struct flow *, struct ofpbuf *); +void nxm_execute_stack_pop(const struct ofpact_stack *, + struct flow *, struct ofpbuf *); + int nxm_field_bytes(uint32_t header); int nxm_field_bits(uint32_t header); diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index 6dabc5a49..d405d2d4e 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -339,6 +339,16 @@ ofpact_from_nxast(const union ofp_action *a, enum ofputil_action_code code, (const struct nx_action_reg_load *) a, out); break; + case OFPUTIL_NXAST_STACK_PUSH: + error = nxm_stack_push_from_openflow( + (const struct nx_action_stack *) a, out); + break; + + case OFPUTIL_NXAST_STACK_POP: + error = nxm_stack_pop_from_openflow( + (const struct nx_action_stack *) a, out); + break; + case OFPUTIL_NXAST_NOTE: nan = (const struct nx_action_note *) a; note_from_openflow(nan, out); @@ -1155,6 +1165,12 @@ ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports, return nxm_reg_load_check(ofpact_get_REG_LOAD(a), flow); } + case OFPACT_STACK_PUSH: + return nxm_stack_push_check(ofpact_get_STACK_PUSH(a), flow); + + case OFPACT_STACK_POP: + return nxm_stack_pop_check(ofpact_get_STACK_POP(a), flow); + case OFPACT_DEC_TTL: case OFPACT_SET_MPLS_TTL: case OFPACT_DEC_MPLS_TTL: @@ -1401,6 +1417,14 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out) nxm_reg_load_to_nxast(ofpact_get_REG_LOAD(a), out); break; + case OFPACT_STACK_PUSH: + nxm_stack_push_to_nxast(ofpact_get_STACK_PUSH(a), out); + break; + + case OFPACT_STACK_POP: + nxm_stack_pop_to_nxast(ofpact_get_STACK_POP(a), out); + break; + case OFPACT_DEC_TTL: ofpact_dec_ttl_to_nxast(ofpact_get_DEC_TTL(a), out); break; @@ -1580,6 +1604,8 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out) case OFPACT_BUNDLE: case OFPACT_REG_MOVE: case OFPACT_REG_LOAD: + case OFPACT_STACK_PUSH: + case OFPACT_STACK_POP: case OFPACT_DEC_TTL: case OFPACT_SET_MPLS_TTL: case OFPACT_DEC_MPLS_TTL: @@ -1748,6 +1774,8 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out) case OFPACT_BUNDLE: case OFPACT_REG_MOVE: case OFPACT_REG_LOAD: + case OFPACT_STACK_PUSH: + case OFPACT_STACK_POP: case OFPACT_SET_TUNNEL: case OFPACT_POP_QUEUE: case OFPACT_FIN_TIMEOUT: @@ -1867,6 +1895,8 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, uint16_t port) case OFPACT_SET_L4_DST_PORT: case OFPACT_REG_MOVE: case OFPACT_REG_LOAD: + case OFPACT_STACK_PUSH: + case OFPACT_STACK_POP: case OFPACT_DEC_TTL: case OFPACT_SET_MPLS_TTL: case OFPACT_DEC_MPLS_TTL: @@ -2088,6 +2118,14 @@ ofpact_format(const struct ofpact *a, struct ds *s) nxm_format_reg_load(ofpact_get_REG_LOAD(a), s); break; + case OFPACT_STACK_PUSH: + nxm_format_stack_push(ofpact_get_STACK_PUSH(a), s); + break; + + case OFPACT_STACK_POP: + nxm_format_stack_pop(ofpact_get_STACK_POP(a), s); + break; + case OFPACT_DEC_TTL: print_dec_ttl(ofpact_get_DEC_TTL(a), s); break; diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h index 2d934f98c..0189c8aee 100644 --- a/lib/ofp-actions.h +++ b/lib/ofp-actions.h @@ -70,6 +70,8 @@ DEFINE_OFPACT(SET_L4_DST_PORT, ofpact_l4_port, ofpact) \ DEFINE_OFPACT(REG_MOVE, ofpact_reg_move, ofpact) \ DEFINE_OFPACT(REG_LOAD, ofpact_reg_load, ofpact) \ + DEFINE_OFPACT(STACK_PUSH, ofpact_stack, ofpact) \ + DEFINE_OFPACT(STACK_POP, ofpact_stack, ofpact) \ DEFINE_OFPACT(DEC_TTL, ofpact_cnt_ids, cnt_ids) \ DEFINE_OFPACT(SET_MPLS_TTL, ofpact_mpls_ttl, ofpact) \ DEFINE_OFPACT(DEC_MPLS_TTL, ofpact_null, ofpact) \ @@ -304,6 +306,14 @@ struct ofpact_reg_move { struct mf_subfield dst; }; +/* OFPACT_STACK_PUSH. + * + * Used for NXAST_STACK_PUSH and NXAST_STACK_POP. */ +struct ofpact_stack { + struct ofpact ofpact; + struct mf_subfield subfield; +}; + /* OFPACT_REG_LOAD. * * Used for NXAST_REG_LOAD, OFPAT12_SET_FIELD. */ diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index f1802048a..e8abc9f6d 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -591,6 +591,12 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow, ofpact_put_POP_MPLS(ofpacts)->ethertype = htons(str_to_u16(arg, "pop_mpls")); break; + case OFPUTIL_NXAST_STACK_PUSH: + nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg); + break; + case OFPUTIL_NXAST_STACK_POP: + nxm_parse_stack_action(ofpact_put_STACK_POP(ofpacts), arg); + break; } } diff --git a/lib/ofp-util.def b/lib/ofp-util.def index 439d34ed5..b7dde4832 100644 --- a/lib/ofp-util.def +++ b/lib/ofp-util.def @@ -50,6 +50,8 @@ NXAST_ACTION(NXAST_SET_QUEUE, nx_action_set_queue, 0, "set_queue") NXAST_ACTION(NXAST_POP_QUEUE, nx_action_pop_queue, 0, "pop_queue") NXAST_ACTION(NXAST_REG_MOVE, nx_action_reg_move, 0, "move") NXAST_ACTION(NXAST_REG_LOAD, nx_action_reg_load, 0, "load") +NXAST_ACTION(NXAST_STACK_PUSH, nx_action_stack, 0, "push") +NXAST_ACTION(NXAST_STACK_POP, nx_action_stack, 0, "pop") NXAST_ACTION(NXAST_NOTE, nx_action_note, 1, "note") NXAST_ACTION(NXAST_SET_TUNNEL64, nx_action_set_tunnel64, 0, "set_tunnel64") NXAST_ACTION(NXAST_MULTIPATH, nx_action_multipath, 0, "multipath") diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index f37d8406d..10e4e23bc 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -216,6 +216,11 @@ struct action_xlate_ctx { * this flow when actions change header fields. */ struct flow flow; + /* stack for the push and pop actions. + * Each stack element is of the type "union mf_subvalue". */ + struct ofpbuf stack; + union mf_subvalue init_stack[1024 / sizeof(union mf_subvalue)]; + /* The packet corresponding to 'flow', or a null pointer if we are * revalidating without a packet to refer to. */ const struct ofpbuf *packet; @@ -6404,6 +6409,16 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, nxm_execute_reg_load(ofpact_get_REG_LOAD(a), &ctx->flow); break; + case OFPACT_STACK_PUSH: + nxm_execute_stack_push(ofpact_get_STACK_PUSH(a), &ctx->flow, + &ctx->stack); + break; + + case OFPACT_STACK_POP: + nxm_execute_stack_pop(ofpact_get_STACK_POP(a), &ctx->flow, + &ctx->stack); + break; + case OFPACT_PUSH_MPLS: execute_mpls_push_action(ctx, ofpact_get_PUSH_MPLS(a)->ethertype); break; @@ -6573,6 +6588,8 @@ xlate_actions(struct action_xlate_ctx *ctx, ctx->table_id = 0; ctx->exit = false; + ofpbuf_use_stub(&ctx->stack, ctx->init_stack, sizeof ctx->init_stack); + if (ctx->ofproto->has_mirrors || hit_resubmit_limit) { /* Do this conditionally because the copy is expensive enough that it * shows up in profiles. */ @@ -6657,6 +6674,8 @@ xlate_actions(struct action_xlate_ctx *ctx, } fix_sflow_action(ctx); } + + ofpbuf_uninit(&ctx->stack); } /* Translates the 'ofpacts_len' bytes of "struct ofpact"s starting at 'ofpacts' diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index b795f5618..7915792cd 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -45,6 +45,7 @@ in_port=12 actions=load:0x10->NXM_NX_REG0[[]],load:0x11->NXM_NX_REG1[[]],load:0x in_port=13 actions=load:0x13->NXM_NX_REG3[[]],load:0x14->NXM_NX_REG4[[]],load:0x15->NXM_NX_REG5[[]] in_port=14 actions=load:0x16->NXM_NX_REG6[[]],load:0x17->NXM_NX_REG7[[]] in_port=15,reg0=0x10,reg1=0x11,reg2=0x12,reg3=0x13,reg4=0x14,reg5=0x15,reg6=0x16,reg7=0x17 actions=output:33 + ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) @@ -54,6 +55,25 @@ AT_CHECK([tail -1 stdout], [0], OVS_VSWITCHD_STOP AT_CLEANUP +AT_SETUP([ofproto-dpif - push-pop]) +OVS_VSWITCHD_START +ADD_OF_PORTS([br0], [20], [21], [22], [33], [90]) +AT_DATA([flows.txt], [dnl +in_port=90 actions=load:20->NXM_NX_REG0[[0..7]],load:21->NXM_NX_REG1[[0..7]],load:22->NXM_NX_REG2[[0..7]], load:33->NXM_NX_REG3[[0..7]], push:NXM_NX_REG0[[]], push:NXM_NX_REG1[[0..7]],push:NXM_NX_REG2[[0..15]], push:NXM_NX_REG3[[]], resubmit:2, resubmit:3, resubmit:4, resubmit:5 +in_port=2 actions=pop:NXM_NX_REG0[[0..7]],output:NXM_NX_REG0[[]] +in_port=3 actions=pop:NXM_NX_REG1[[0..7]],output:NXM_NX_REG1[[]] +in_port=4 actions=pop:NXM_NX_REG2[[0..15]],output:NXM_NX_REG2[[]] +in_port=5 actions=pop:NXM_NX_REG3[[]],output:NXM_NX_REG3[[]] + +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) +AT_CHECK([tail -1 stdout], [0], + [Datapath actions: 33,22,21,20 +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + AT_SETUP([ofproto-dpif - output]) OVS_VSWITCHD_START ADD_OF_PORTS([br0], [1], [9], [10], [11], [55], [66], [77], [88]) diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at index 7395fd14e..075f2e492 100644 --- a/tests/ovs-ofctl.at +++ b/tests/ovs-ofctl.at @@ -243,6 +243,7 @@ tun_id=0x1234,cookie=0x5678,actions=flood actions=drop reg0=123,actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:55->NXM_NX_REG2[0..31],move:NXM_NX_REG0[0..31]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[] actions=move:OXM_OF_ETH_DST[]->OXM_OF_ETH_SRC[] +actions=push:NXM_NX_REG0[0..31],pop:NXM_NX_REG0[] vlan_tci=0x1123/0x1fff,actions=drop ]]) AT_CHECK([ovs-ofctl -F nxm -mmm parse-flows flows.txt], [0], [stdout], [stderr]) @@ -271,6 +272,7 @@ NXT_FLOW_MOD: ADD NXM_NX_TUN_ID(0000000000001234) cookie:0x5678 actions=FLOOD NXT_FLOW_MOD: ADD actions=drop NXT_FLOW_MOD: ADD NXM_NX_REG0(0000007b) actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:0x37->NXM_NX_REG2[],move:NXM_NX_REG0[]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[] NXT_FLOW_MOD: ADD actions=move:NXM_OF_ETH_DST[]->NXM_OF_ETH_SRC[] +NXT_FLOW_MOD: ADD actions=push:NXM_NX_REG0[],pop:NXM_NX_REG0[] NXT_FLOW_MOD: ADD NXM_OF_VLAN_TCI_W(1123/1fff) actions=drop ]]) AT_CLEANUP diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index 609df9f2f..e7d9cfb54 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -1063,6 +1063,23 @@ in field \fIdst\fR. Example: \fBload:55\->NXM_NX_REG2[0..5]\fR loads value 55 (bit pattern \fB110111\fR) into bits 0 through 5, inclusive, in register 2. . +.IP "\fBpush:\fIsrc\fB[\fIstart\fB..\fIend\fB]" +Pushes \fIstart\fR to \fIend\fR bits inclusive, in fields +on top of the stack. +.IP +Example: \fBpush:NXM_NX_REG2[0..5]\fR push the value stored in register +2 bits 0 through 5, inclusive, on to the internal stack. +. +.IP "\fBpop:\fIdst\fB[\fIstart\fB..\fIend\fB]" +Pops from the top of the stack, retrieves the \fIstart\fR to \fIend\fR bits +inclusive, from the value popped and store them into the corresponding +bits in \fIdst\fR. +. +.IP +Example: \fBpop:NXM_NX_REG2[0..5]\fR pops the value from top of the stack. +Set register 2 bits 0 through 5, inclusive, based on bits 0 through 5 from the +value just popped. +. .IP "\fBset_field:\fIvalue\fB\->\fIdst" Writes the literal \fIvalue\fR into the field \fIdst\fR, which should be specified as a name used for matching. (This is similar to