This is an automated email from the ASF dual-hosted git repository.

ccollins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-newt.git

commit 4f6740563035339a1029a49155624822e9fe62af
Author: Christopher Collins <ccoll...@apache.org>
AuthorDate: Tue Nov 20 17:37:59 2018 -0800

    Larva tool
    
    This tool has the following functionality:
        * Parse a `.img` file and display it in JSON format.
        * Remove signatures from a `.img` file.
        * Add signatures to a `.img` file.
        * Split a manufacturing image into several files, one for each flash
          area.
        * Join a split manufacturing image.
---
 larva/cli/image_cmds.go | 205 ++++++++++++++++++++++++++++++++++++++++++++++++
 larva/cli/mfg_cmds.go   | 184 +++++++++++++++++++++++++++++++++++++++++++
 larva/cli/util.go       |  66 ++++++++++++++++
 larva/mfg/mfg.go        | 147 ++++++++++++++++++++++++++++++++++
 larva/mimg.go           |  84 ++++++++++++++++++++
 5 files changed, 686 insertions(+)

diff --git a/larva/cli/image_cmds.go b/larva/cli/image_cmds.go
new file mode 100644
index 0000000..ce1093e
--- /dev/null
+++ b/larva/cli/image_cmds.go
@@ -0,0 +1,205 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 cli
+
+import (
+       "encoding/hex"
+       "fmt"
+
+       log "github.com/Sirupsen/logrus"
+       "github.com/spf13/cobra"
+
+       "mynewt.apache.org/newt/artifact/image"
+       "mynewt.apache.org/newt/util"
+)
+
+func readImage(filename string) (image.Image, error) {
+       img, err := image.ReadImage(filename)
+       if err != nil {
+               return img, err
+       }
+
+       log.Debugf("Successfully read image %s", filename)
+       return img, nil
+}
+
+func writeImage(img image.Image, filename string) error {
+       if err := img.WriteToFile(filename); err != nil {
+               return err
+       }
+
+       log.Debugf("Wrote image %s", filename)
+       return nil
+}
+
+func reportDupSigs(img image.Image) {
+       m := map[string]struct{}{}
+       dups := map[string]struct{}{}
+
+       for _, tlv := range img.Tlvs {
+               if tlv.Header.Type == image.IMAGE_TLV_KEYHASH {
+                       h := hex.EncodeToString(tlv.Data)
+                       if _, ok := m[h]; ok {
+                               dups[h] = struct{}{}
+                       } else {
+                               m[h] = struct{}{}
+                       }
+               }
+       }
+
+       if len(dups) > 0 {
+               fmt.Printf("Warning: duplicate signatures detected:\n")
+               for d, _ := range dups {
+                       fmt.Printf("    %s\n", d)
+               }
+       }
+}
+
+func runShowCmd(cmd *cobra.Command, args []string) {
+       if len(args) < 1 {
+               LarvaUsage(cmd, nil)
+       }
+
+       img, err := readImage(args[0])
+       if err != nil {
+               LarvaUsage(cmd, err)
+       }
+
+       s, err := img.Json()
+       if err != nil {
+               LarvaUsage(nil, err)
+       }
+       fmt.Printf("%s\n", s)
+}
+
+func runSignCmd(cmd *cobra.Command, args []string) {
+       if len(args) < 2 {
+               LarvaUsage(cmd, nil)
+       }
+
+       inFilename := args[0]
+       outFilename, err := CalcOutFilename(inFilename)
+       if err != nil {
+               LarvaUsage(cmd, err)
+       }
+
+       img, err := readImage(inFilename)
+       if err != nil {
+               LarvaUsage(cmd, err)
+       }
+
+       keys, err := image.ReadKeys(args[1:])
+       if err != nil {
+               LarvaUsage(cmd, err)
+       }
+
+       hash, err := img.Hash()
+       if err != nil {
+               LarvaUsage(cmd, util.FmtNewtError(
+                       "Failed to read hash from specified image: %s", 
err.Error()))
+       }
+
+       tlvs, err := image.GenerateSigTlvs(keys, hash)
+       if err != nil {
+               LarvaUsage(nil, err)
+       }
+
+       img.Tlvs = append(img.Tlvs, tlvs...)
+
+       reportDupSigs(img)
+
+       if err := writeImage(img, outFilename); err != nil {
+               LarvaUsage(nil, err)
+       }
+}
+
+func runRmsigsCmd(cmd *cobra.Command, args []string) {
+       if len(args) < 1 {
+               LarvaUsage(cmd, nil)
+       }
+
+       inFilename := args[0]
+       outFilename, err := CalcOutFilename(inFilename)
+       if err != nil {
+               LarvaUsage(cmd, err)
+       }
+
+       img, err := readImage(inFilename)
+       if err != nil {
+               LarvaUsage(cmd, err)
+       }
+
+       cnt := img.RemoveTlvsIf(func(tlv image.ImageTlv) bool {
+               return tlv.Header.Type == image.IMAGE_TLV_KEYHASH ||
+                       tlv.Header.Type == image.IMAGE_TLV_RSA2048 ||
+                       tlv.Header.Type == image.IMAGE_TLV_ECDSA224 ||
+                       tlv.Header.Type == image.IMAGE_TLV_ECDSA256
+       })
+
+       log.Debugf("Removed %d existing signatures", cnt)
+
+       if err := writeImage(img, outFilename); err != nil {
+               LarvaUsage(nil, err)
+       }
+}
+
+func AddImageCommands(cmd *cobra.Command) {
+       imageCmd := &cobra.Command{
+               Use:   "image",
+               Short: "Shows and manipulates Mynewt image (.img) files",
+               Run: func(cmd *cobra.Command, args []string) {
+                       cmd.Usage()
+               },
+       }
+       cmd.AddCommand(imageCmd)
+
+       showCmd := &cobra.Command{
+               Use:   "show <img-file>",
+               Short: "Displays JSON describing a Mynewt image file",
+               Run:   runShowCmd,
+       }
+       imageCmd.AddCommand(showCmd)
+
+       signCmd := &cobra.Command{
+               Use:   "sign <img-file> <priv-key-pem> [priv-key-pem...]",
+               Short: "Appends signatures to a Mynewt image file",
+               Run:   runSignCmd,
+       }
+
+       signCmd.PersistentFlags().StringVarP(&OptOutFilename, "outfile", "o", 
"",
+               "File to write to")
+       signCmd.PersistentFlags().BoolVarP(&OptInPlace, "inplace", "i", false,
+               "Replace input file")
+
+       imageCmd.AddCommand(signCmd)
+
+       rmsigsCmd := &cobra.Command{
+               Use:   "rmsigs",
+               Short: "Removes all signatures from a Mynewt image file",
+               Run:   runRmsigsCmd,
+       }
+
+       rmsigsCmd.PersistentFlags().StringVarP(&OptOutFilename, "outfile", "o", 
"",
+               "File to write to")
+       rmsigsCmd.PersistentFlags().BoolVarP(&OptInPlace, "inplace", "i", false,
+               "Replace input file")
+
+       imageCmd.AddCommand(rmsigsCmd)
+}
diff --git a/larva/cli/mfg_cmds.go b/larva/cli/mfg_cmds.go
new file mode 100644
index 0000000..3a9e6ac
--- /dev/null
+++ b/larva/cli/mfg_cmds.go
@@ -0,0 +1,184 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 cli
+
+import (
+       "fmt"
+       "io/ioutil"
+       "os"
+
+       log "github.com/Sirupsen/logrus"
+       "github.com/spf13/cobra"
+
+       "mynewt.apache.org/newt/artifact/flash"
+       "mynewt.apache.org/newt/artifact/manifest"
+       "mynewt.apache.org/newt/larva/mfg"
+       "mynewt.apache.org/newt/util"
+)
+
+var optDeviceNum int
+
+func readManifest(filename string) (manifest.Manifest, error) {
+       man, err := manifest.ReadManifest(filename)
+       if err != nil {
+               return man, err
+       }
+
+       log.Debugf("Successfully read manifest %s", filename)
+       return man, nil
+}
+
+func readFlashAreas(manifestFilename string) ([]flash.FlashArea, error) {
+       man, err := readManifest(manifestFilename)
+       if err != nil {
+               return nil, err
+       }
+
+       areas := flash.SortFlashAreasByDevOff(man.FlashAreas)
+
+       overlaps, conflicts := flash.DetectErrors(areas)
+       if len(overlaps) > 0 || len(conflicts) > 0 {
+               return nil, util.NewNewtError(flash.ErrorText(overlaps, 
conflicts))
+       }
+
+       if err := mfg.VerifyAreas(areas, optDeviceNum); err != nil {
+               return nil, err
+       }
+
+       log.Debugf("Successfully read flash areas: %+v", areas)
+       return areas, nil
+}
+
+func createMfgMap(binDir string, areas []flash.FlashArea) (mfg.MfgMap, error) {
+       mm := mfg.MfgMap{}
+
+       for _, area := range areas {
+               filename := fmt.Sprintf("%s/%s.bin", binDir, area.Name)
+               bin, err := ioutil.ReadFile(filename)
+               if err != nil {
+                       if !os.IsNotExist(err) {
+                               return nil, util.ChildNewtError(err)
+                       }
+               } else {
+                       mm[area.Name] = bin
+               }
+       }
+
+       return mm, nil
+}
+
+func runSplitCmd(cmd *cobra.Command, args []string) {
+       if len(args) < 3 {
+               LarvaUsage(cmd, nil)
+       }
+
+       imgFilename := args[0]
+       manFilename := args[1]
+       outDir := args[2]
+
+       mfgBin, err := ioutil.ReadFile(imgFilename)
+       if err != nil {
+               LarvaUsage(cmd, util.FmtNewtError(
+                       "Failed to read manufacturing image: %s", err.Error()))
+       }
+
+       areas, err := readFlashAreas(manFilename)
+       if err != nil {
+               LarvaUsage(cmd, err)
+       }
+
+       mm, err := mfg.Split(mfgBin, optDeviceNum, areas)
+       if err != nil {
+               LarvaUsage(nil, err)
+       }
+
+       if err := os.Mkdir(outDir, os.ModePerm); err != nil {
+               LarvaUsage(nil, util.ChildNewtError(err))
+       }
+
+       for name, data := range mm {
+               filename := fmt.Sprintf("%s/%s.bin", outDir, name)
+               if err := ioutil.WriteFile(filename, data, os.ModePerm); err != 
nil {
+                       LarvaUsage(nil, util.ChildNewtError(err))
+               }
+       }
+}
+
+func runJoinCmd(cmd *cobra.Command, args []string) {
+       if len(args) < 3 {
+               LarvaUsage(cmd, nil)
+       }
+
+       binDir := args[0]
+       manFilename := args[1]
+       outFilename := args[2]
+
+       areas, err := readFlashAreas(manFilename)
+       if err != nil {
+               LarvaUsage(cmd, err)
+       }
+
+       mm, err := createMfgMap(binDir, areas)
+       if err != nil {
+               LarvaUsage(nil, err)
+       }
+
+       mfgBin, err := mfg.Join(mm, 0xff, areas)
+       if err != nil {
+               LarvaUsage(nil, err)
+       }
+
+       if err := ioutil.WriteFile(outFilename, mfgBin, os.ModePerm); err != 
nil {
+               LarvaUsage(nil, util.ChildNewtError(err))
+       }
+}
+
+func AddMfgCommands(cmd *cobra.Command) {
+       mfgCmd := &cobra.Command{
+               Use:   "mfg",
+               Short: "Manipulates Mynewt manufacturing images",
+               Run: func(cmd *cobra.Command, args []string) {
+                       cmd.Usage()
+               },
+       }
+       cmd.AddCommand(mfgCmd)
+
+       splitCmd := &cobra.Command{
+               Use:   "split <mfg-image> <manifest> <out-dir>",
+               Short: "Splits a Mynewt mfg section into several files",
+               Run:   runSplitCmd,
+       }
+
+       splitCmd.PersistentFlags().IntVarP(&optDeviceNum, "device", "d", 0,
+               "Flash device number")
+
+       mfgCmd.AddCommand(splitCmd)
+
+       joinCmd := &cobra.Command{
+               Use:   "join <bin-dir> <manifest> <out-mfg-image>",
+               Short: "Joins a split mfg section into a single file",
+               Run:   runJoinCmd,
+       }
+
+       joinCmd.PersistentFlags().IntVarP(&optDeviceNum, "device", "d", 0,
+               "Flash device number")
+
+       mfgCmd.AddCommand(joinCmd)
+}
diff --git a/larva/cli/util.go b/larva/cli/util.go
new file mode 100644
index 0000000..20c3d5e
--- /dev/null
+++ b/larva/cli/util.go
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 cli
+
+import (
+       "fmt"
+       "os"
+
+       log "github.com/Sirupsen/logrus"
+       "github.com/spf13/cobra"
+
+       "mynewt.apache.org/newt/util"
+)
+
+var OptOutFilename string
+var OptInPlace bool
+
+func LarvaUsage(cmd *cobra.Command, err error) {
+       if err != nil {
+               sErr := err.(*util.NewtError)
+               log.Debugf("%s", sErr.StackTrace)
+               fmt.Fprintf(os.Stderr, "Error: %s\n", sErr.Text)
+       }
+
+       if cmd != nil {
+               fmt.Printf("\n")
+               fmt.Printf("%s - ", cmd.Name())
+               cmd.Help()
+       }
+       os.Exit(1)
+}
+
+func CalcOutFilename(inFilename string) (string, error) {
+       if OptOutFilename != "" {
+               if OptInPlace {
+                       return "", util.FmtNewtError(
+                               "Only one of --outfile (-o) or --inplace (-i) 
options allowed")
+               }
+
+               return OptOutFilename, nil
+       }
+
+       if !OptInPlace {
+               return "", util.FmtNewtError(
+                       "--outfile (-o) or --inplace (-i) option required")
+       }
+
+       return inFilename, nil
+}
diff --git a/larva/mfg/mfg.go b/larva/mfg/mfg.go
new file mode 100644
index 0000000..0cbbbb0
--- /dev/null
+++ b/larva/mfg/mfg.go
@@ -0,0 +1,147 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 mfg
+
+import (
+       "fmt"
+       "sort"
+       "strings"
+
+       "mynewt.apache.org/newt/artifact/flash"
+       "mynewt.apache.org/newt/util"
+)
+
+type MfgMap map[string][]byte
+
+func errInvalidArea(areaName string, format string,
+       args ...interface{}) error {
+
+       suffix := fmt.Sprintf(format, args...)
+       return util.FmtNewtError("Invalid flash area \"%s\": %s", areaName, 
suffix)
+}
+
+func verifyArea(area flash.FlashArea, minOffset int) error {
+       if area.Offset < minOffset {
+               return errInvalidArea(area.Name, "invalid offset %d; expected 
>= %d",
+                       area.Offset, minOffset)
+       }
+
+       if area.Size < 0 {
+               return errInvalidArea(area.Name, "invalid size %d", area.Size)
+       }
+
+       return nil
+}
+
+// `areas` must be sorted by device ID, then by offset.
+func VerifyAreas(areas []flash.FlashArea, deviceNum int) error {
+       off := 0
+       for _, area := range areas {
+               if area.Device == deviceNum {
+                       if err := verifyArea(area, off); err != nil {
+                               return err
+                       }
+                       off += area.Size
+               }
+       }
+
+       return nil
+}
+
+func Split(mfgBin []byte, deviceNum int,
+       areas []flash.FlashArea) (MfgMap, error) {
+
+       mm := MfgMap{}
+
+       for _, area := range areas {
+               if _, ok := mm[area.Name]; ok {
+                       return nil, util.FmtNewtError(
+                               "two or more flash areas with same name: 
\"%s\"", area.Name)
+               }
+
+               if area.Device == deviceNum && area.Offset < len(mfgBin) {
+                       end := area.Offset + area.Size
+                       if end > len(mfgBin) {
+                               return nil, util.FmtNewtError(
+                                       "area \"%s\" (offset=%d size=%d) "+
+                                               "extends beyond end of 
manufacturing image",
+                                       area.Name, area.Offset, area.Size)
+                       }
+
+                       mm[area.Name] = mfgBin[area.Offset:end]
+               }
+       }
+
+       return mm, nil
+}
+
+// `areas` must be sorted by device ID, then by offset.
+func Join(mm MfgMap, eraseVal byte, areas []flash.FlashArea) ([]byte, error) {
+       // Ensure all areas in the mfg map belong to the same flash device.
+       device := -1
+       for _, area := range areas {
+               if _, ok := mm[area.Name]; ok {
+                       if device == -1 {
+                               device = area.Device
+                       } else if device != area.Device {
+                               return nil, util.FmtNewtError(
+                                       "multiple flash devices: %d != %d", 
device, area.Device)
+                       }
+               }
+       }
+
+       // Keep track of which areas we haven't seen yet.
+       unseen := map[string]struct{}{}
+       for name, _ := range mm {
+               unseen[name] = struct{}{}
+       }
+
+       joined := []byte{}
+
+       off := 0
+       for _, area := range areas {
+               bin := mm[area.Name]
+               if bin == nil {
+                       break
+               }
+               delete(unseen, area.Name)
+
+               padSize := area.Offset - off
+               for i := 0; i < padSize; i++ {
+                       joined = append(joined, 0xff)
+               }
+
+               joined = append(joined, bin...)
+       }
+
+       // Ensure we processed every area in the mfg map.
+       if len(unseen) > 0 {
+               names := []string{}
+               for name, _ := range unseen {
+                       names = append(names, name)
+               }
+               sort.Strings(names)
+
+               return nil, util.FmtNewtError(
+                       "unprocessed flash areas: %s", strings.Join(names, ", 
"))
+       }
+
+       return joined, nil
+}
diff --git a/larva/mimg.go b/larva/mimg.go
new file mode 100644
index 0000000..04cddec
--- /dev/null
+++ b/larva/mimg.go
@@ -0,0 +1,84 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 main
+
+import (
+       "fmt"
+
+       log "github.com/Sirupsen/logrus"
+       "github.com/spf13/cobra"
+
+       "mynewt.apache.org/newt/larva/cli"
+       "mynewt.apache.org/newt/util"
+)
+
+var LarvaLogLevel log.Level
+var larvaVersion = "0.0.1"
+
+func main() {
+       larvaHelpText := ""
+       larvaHelpEx := ""
+
+       logLevelStr := ""
+       larvaCmd := &cobra.Command{
+               Use:     "larva",
+               Short:   "larva is a tool to help you compose and build your 
own OS",
+               Long:    larvaHelpText,
+               Example: larvaHelpEx,
+               PersistentPreRun: func(cmd *cobra.Command, args []string) {
+                       logLevel, err := log.ParseLevel(logLevelStr)
+                       if err != nil {
+                               cli.LarvaUsage(nil, util.ChildNewtError(err))
+                       }
+                       LarvaLogLevel = logLevel
+
+                       if err := util.Init(LarvaLogLevel, "",
+                               util.VERBOSITY_DEFAULT); err != nil {
+
+                               cli.LarvaUsage(nil, err)
+                       }
+               },
+
+               Run: func(cmd *cobra.Command, args []string) {
+                       cmd.Help()
+               },
+       }
+
+       larvaCmd.PersistentFlags().StringVarP(&logLevelStr, "loglevel", "l",
+               "WARN", "Log level")
+
+       versHelpText := `Display the larva version number`
+       versHelpEx := "  larva version"
+       versCmd := &cobra.Command{
+               Use:     "version",
+               Short:   "Display the larva version number",
+               Long:    versHelpText,
+               Example: versHelpEx,
+               Run: func(cmd *cobra.Command, args []string) {
+                       fmt.Printf("%s\n", larvaVersion)
+               },
+       }
+       larvaCmd.AddCommand(versCmd)
+
+       cli.AddImageCommands(larvaCmd)
+       cli.AddMfgCommands(larvaCmd)
+
+       larvaCmd.Execute()
+}

Reply via email to