First commit

Change-Id: Iab8dca19bca1ef367b1cced4b345554fd32154df
diff --git a/src/main/java/org/opencord/fabric/tofino/PipeconfLoader.java b/src/main/java/org/opencord/fabric/tofino/PipeconfLoader.java
new file mode 100644
index 0000000..f6b60e9
--- /dev/null
+++ b/src/main/java/org/opencord/fabric/tofino/PipeconfLoader.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opencord.fabric.tofino;
+
+import org.onosproject.core.CoreService;
+import org.onosproject.net.pi.model.DefaultPiPipeconf;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.model.PiPipeconf.ExtensionType;
+import org.onosproject.net.pi.model.PiPipeconfId;
+import org.onosproject.net.pi.service.PiPipeconfService;
+import org.onosproject.pipelines.fabric.FabricPipeconfService;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static java.lang.String.format;
+import static org.osgi.framework.wiring.BundleWiring.LISTRESOURCES_RECURSE;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Component responsible of registering Tofino-specific versions
+ * of the fabric pipeconf at app activation.
+ */
+@Component(immediate = true)
+public class PipeconfLoader {
+
+    private static final String APP_NAME = "org.opencord.fabric-tofino";
+
+    private static Logger log = getLogger(PipeconfLoader.class);
+
+    private static final String BASE_PIPECONF_ID = "org.opencord";
+    private static final String P4C_OUT_PATH = "/p4c-out";
+    // p4c-out/<profile>/<platform>
+    private static final String P4C_RES_BASE_PATH = P4C_OUT_PATH + "/%s/%s/%s/";
+    private static final String SEP = File.separator;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    private PiPipeconfService pipeconfService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    private FabricPipeconfService fabricPipeconfService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    private CoreService coreService;
+
+    private Collection<PiPipeconf> pipeconfs;
+
+    private static final String TOFINO = "tofino";
+    private static final String P4INFO_TXT = "p4info.txt";
+    private static final String CPU_PORT_TXT = "cpu_port.txt";
+    private static final String TOFINO_BIN = "pipe/tofino.bin";
+    private static final String TOFINO_CTX_JSON = "pipe/context.json";
+
+
+    @Activate
+    public void activate() {
+        coreService.registerApplication(APP_NAME);
+        // Registers all pipeconf at component activation.
+        pipeconfs = buildAllPipeconfs();
+        pipeconfs.forEach(pipeconfService::register);
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        pipeconfs.stream()
+                .map(PiPipeconf::id)
+                .forEach(pipeconfService::unregister);
+        pipeconfs = null;
+        log.info("Stopped");
+    }
+
+    private Collection<PiPipeconf> buildAllPipeconfs() {
+        return FrameworkUtil
+                .getBundle(this.getClass())
+                .adapt(BundleWiring.class)
+                // List all resource files in /p4c-out
+                .listResources(P4C_OUT_PATH, "*", LISTRESOURCES_RECURSE)
+                .stream()
+                // Filter only directories
+                .filter(name -> name.endsWith(SEP))
+                // Derive profile, target, and platform and build pipeconf.
+                .map(this::buildPipeconfFromPath)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+    }
+
+    private PiPipeconf buildPipeconfFromPath(String path) {
+        String[] pieces = path.split(SEP);
+        // We expect a path of 4 elements, e.g.
+        // p4c-out/<profile>/<target>/<platform>
+        if (pieces.length != 4) {
+            return null;
+        }
+        String profile = pieces[1];
+        String target = pieces[2];
+        String platform = pieces[3];
+
+        if (TOFINO.equals(target)) {
+            try {
+                return tofinoPipeconf(profile, platform);
+            } catch (FileNotFoundException e) {
+                log.warn("Unable to build pipeconf at {} because file is missing: {}",
+                        path, e.getMessage());
+                return null;
+            }
+        }
+
+        log.warn("Unknown target '{}', skipping pipeconf build at '{}'...",
+                target, path);
+        return null;
+    }
+
+    private PiPipeconf tofinoPipeconf(String profile, String platform)
+            throws FileNotFoundException {
+        final URL tofinoBinUrl = this.getClass().getResource(format(
+                P4C_RES_BASE_PATH + TOFINO_BIN, profile, TOFINO, platform));
+        final URL contextJsonUrl = this.getClass().getResource(format(
+                P4C_RES_BASE_PATH + TOFINO_CTX_JSON, profile, TOFINO, platform));
+        final URL p4InfoUrl = this.getClass().getResource(format(
+                P4C_RES_BASE_PATH + P4INFO_TXT, profile, TOFINO, platform));
+        final URL cpuPortUrl = this.getClass().getResource(format(
+                P4C_RES_BASE_PATH + CPU_PORT_TXT, profile, TOFINO, platform));
+
+        checkFileExists(tofinoBinUrl, TOFINO_BIN);
+        checkFileExists(contextJsonUrl, TOFINO_CTX_JSON);
+        checkFileExists(p4InfoUrl, P4INFO_TXT);
+        checkFileExists(cpuPortUrl, CPU_PORT_TXT);
+
+        final DefaultPiPipeconf.Builder builder = DefaultPiPipeconf.builder()
+                .withId(new PiPipeconfId(format(
+                        "%s.%s.tofino.%s", BASE_PIPECONF_ID, profile, platform)))
+                .addExtension(ExtensionType.TOFINO_BIN, tofinoBinUrl)
+                .addExtension(ExtensionType.TOFINO_CONTEXT_JSON, contextJsonUrl);
+
+        return fabricPipeconfService.buildFabricPipeconf(
+                builder, profile, p4InfoUrl, cpuPortUrl);
+    }
+
+    private void checkFileExists(URL url, String name)
+            throws FileNotFoundException {
+        if (url == null) {
+            throw new FileNotFoundException(name);
+        }
+    }
+}
diff --git a/src/main/java/org/opencord/fabric/tofino/package-info.java b/src/main/java/org/opencord/fabric/tofino/package-info.java
new file mode 100644
index 0000000..2e69823
--- /dev/null
+++ b/src/main/java/org/opencord/fabric/tofino/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Fabric-tofino pipeconf loader app.
+ */
+package org.opencord.fabric.tofino;
\ No newline at end of file
diff --git a/src/main/p4/fabric-tofino.p4 b/src/main/p4/fabric-tofino.p4
new file mode 100755
index 0000000..7b81449
--- /dev/null
+++ b/src/main/p4/fabric-tofino.p4
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _PKT_OUT_HDR_ANNOT @not_extracted_in_egress
+
+#define _BOOL bit<1>
+#define _TRUE 1w1
+#define _FALSE 1w0
+
+#ifdef WITH_INT_TRANSIT
+#define _PRE_INGRESS fabric_metadata.int_meta.ig_tstamp = (bit<32>) standard_metadata.ingress_global_timestamp;
+#define _PRE_EGRESS fabric_metadata.int_meta.eg_tstamp = (bit<32>) standard_metadata.egress_global_timestamp;
+#endif // WITH_INT_TRANSIT
+
+#define _INT_INIT_METADATA \
+    hdr.int_switch_id.switch_id = switch_id;\
+    hdr.int_port_ids.ingress_port_id = (bit<16>) smeta.ingress_port;\
+    hdr.int_port_ids.egress_port_id = (bit<16>) smeta.egress_port;\
+    hdr.int_ingress_tstamp.ingress_tstamp = fmeta.int_meta.ig_tstamp;\
+    hdr.int_egress_tstamp.egress_tstamp = fmeta.int_meta.eg_tstamp;\
+    hdr.int_hop_latency.hop_latency = (bit<32>) smeta.egress_global_timestamp - (bit<32>) smeta.ingress_global_timestamp;\
+    hdr.int_q_occupancy.q_id = 8w0;\
+    hdr.int_q_occupancy.q_occupancy = (bit<24>) smeta.deq_qdepth;\
+    hdr.int_q_congestion.q_id = 8w0;\
+    hdr.int_q_congestion.q_congestion = 24w0;\
+    hdr.int_egress_tx_util.egress_port_tx_util = 32w0;\
+
+#define _INT_METADATA_ACTIONS \
+    action int_set_header_0() { hdr.int_switch_id.setValid(); }\
+    action int_set_header_1() { hdr.int_port_ids.setValid(); }\
+    action int_set_header_2() { hdr.int_hop_latency.setValid(); }\
+    action int_set_header_3() { hdr.int_q_occupancy.setValid(); }\
+    action int_set_header_4() { hdr.int_ingress_tstamp.setValid(); }\
+    action int_set_header_5() { hdr.int_egress_tstamp.setValid(); }\
+    action int_set_header_6() { hdr.int_q_congestion.setValid(); }\
+    action int_set_header_7() { hdr.int_egress_tx_util.setValid(); }\
+
+#define __TABLE_SIZE__
+#define BNG_MAX_SUBSC 1024
+#define BNG_MAX_NET_PER_SUBSC 4
+#define BNG_MAX_SUBSC_NET BNG_MAX_NET_PER_SUBSC * BNG_MAX_SUBSC
+#ifdef WITH_BNG
+    #define PORT_VLAN_TABLE_SIZE BNG_MAX_SUBSC + 2048
+#else
+    #define PORT_VLAN_TABLE_SIZE 2048
+#endif // WITH_BNG
+#define FWD_CLASSIFIER_TABLE_SIZE 128
+#define BRIDGING_TABLE_SIZE 2048
+#define MPLS_TABLE_SIZE 2048
+#ifdef WITH_BNG
+    #define ROUTING_V4_TABLE_SIZE BNG_MAX_SUBSC_NET + 1024
+#else
+    #define ROUTING_V4_TABLE_SIZE 30000
+#endif // WITH_BNG
+#define ACL_TABLE_SIZE 2048
+// Depends on number of unique next_id expected
+#define NEXT_VLAN_TABLE_SIZE 2048
+#define XCONNECT_NEXT_TABLE_SIZE 4096
+#define SIMPLE_NEXT_TABLE_SIZE 2048
+#define HASHED_NEXT_TABLE_SIZE 2048
+// Max size of ECMP groups
+#define HASHED_SELECTOR_MAX_GROUP_SIZE 16
+// Ideally HASHED_NEXT_TABLE_SIZE * HASHED_SELECTOR_MAX_GROUP_SIZE
+#define HASHED_ACT_PROFILE_SIZE 32w32768
+#define MULTICAST_NEXT_TABLE_SIZE 2048
+#define EGRESS_VLAN_TABLE_SIZE 2048
+
+#define _ROUTING_V4_TABLE_ANNOT @alpm(1)
+
+#include "fabric.p4"
diff --git a/src/main/p4/tofino-compile.sh b/src/main/p4/tofino-compile.sh
new file mode 100755
index 0000000..cab4c0c
--- /dev/null
+++ b/src/main/p4/tofino-compile.sh
@@ -0,0 +1,82 @@
+#!/usr/bin/env bash
+# Copyright 2019-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+MAVERICKS_CPU_PORT=320
+MONTARA_CPU_PORT=192
+
+if [ -z "$ONOS_ROOT" ]; then
+  echo "Error: ONOS_ROOT is not set"
+  exit 1
+fi
+
+P4_SRC_DIR=${ONOS_ROOT}/pipelines/fabric/impl/src/main/resources
+
+if [ ! -d "${P4_SRC_DIR}" ]; then
+  echo "Error: unable to locate fabric P4 sources at ${P4_SRC_DIR}"
+fi
+
+set -e
+
+PROFILE=$1
+OTHER_PP_FLAGS=$2
+
+# PWD is the directory where this script is called from (should be the root of
+# this repo).
+P4C_OUT=${PWD}/p4c-out/${PROFILE}
+
+# DIR is this file directory.
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+# Where the compiler output should be placed to be included in the pipeconf.
+DEST_DIR=${DIR}/../resources/p4c-out/${PROFILE}/tofino
+
+# If SDE_DOCKER_IMG env is set, use containerized version of the compiler
+if [ -z "${SDE_DOCKER_IMG}" ]; then
+  P4C_CMD="bf-p4c"
+else
+  P4C_CMD="docker run --rm -v ${P4C_OUT}:${P4C_OUT} -v ${P4_SRC_DIR}:${P4_SRC_DIR} -v ${DIR}:${DIR} -w ${DIR} ${SDE_DOCKER_IMG} bf-p4c"
+fi
+
+SDE_VER=$( ${P4C_CMD} --version | cut -d' ' -f2 )
+
+# shellcheck disable=SC2086
+function do_p4c() {
+  pltf="$1_sde_${SDE_VER//./_}"
+  cpu_port=$2
+  echo "*** Compiling profile '${PROFILE}' for ${pltf} platform..."
+  echo "*** Output in ${P4C_OUT}/${pltf}"
+  pp_flags="-DCPU_PORT=${cpu_port}"
+  mkdir -p ${P4C_OUT}/${pltf}
+  (
+    set -x
+    $P4C_CMD --arch v1model -g \
+      -o ${P4C_OUT}/${pltf} -I ${P4_SRC_DIR} \
+      ${pp_flags} ${OTHER_PP_FLAGS} \
+      --p4runtime-files ${P4C_OUT}/${pltf}/p4info.txt \
+      ${DIR}/fabric-tofino.p4
+  )
+
+  # Copy only the relevant files to the pipeconf resources
+  mkdir -p ${DEST_DIR}/${pltf}/pipe
+  cp ${P4C_OUT}/${pltf}/p4info.txt ${DEST_DIR}/${pltf}
+  cp ${P4C_OUT}/${pltf}/pipe/context.json ${DEST_DIR}/${pltf}/pipe
+  cp ${P4C_OUT}/${pltf}/pipe/tofino.bin ${DEST_DIR}/${pltf}/pipe
+  echo "${cpu_port}" > ${DEST_DIR}/${pltf}/cpu_port.txt
+
+  echo
+}
+
+do_p4c "mavericks" ${MAVERICKS_CPU_PORT}
+do_p4c "montara" ${MONTARA_CPU_PORT}