You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2019/01/04 18:21:13 UTC
[mynewt-newt] 08/17: Larva tool
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 <cc...@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()
+}