| /** |
| * 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 ( |
| "encoding/hex" |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| "strings" |
| "time" |
| |
| "github.com/apache/mynewt-artifact/flash" |
| "github.com/apache/mynewt-artifact/image" |
| "github.com/apache/mynewt-artifact/manifest" |
| "github.com/apache/mynewt-artifact/mfg" |
| "github.com/apache/mynewt-artifact/sec" |
| "mynewt.apache.org/newt/newt/builder" |
| "mynewt.apache.org/newt/newt/flashmap" |
| "mynewt.apache.org/newt/newt/pkg" |
| "mynewt.apache.org/newt/newt/project" |
| "mynewt.apache.org/newt/newt/target" |
| "mynewt.apache.org/newt/newt/toolchain" |
| "mynewt.apache.org/newt/util" |
| ) |
| |
| // Current manufacturing image binary format version. |
| const MANIFEST_FORMAT = 2 |
| |
| // Represents a file copy operation. |
| type CpEntry struct { |
| From string |
| To string |
| } |
| |
| type MfgEmitTarget struct { |
| Name string |
| Offset int |
| Size int |
| IsBoot bool |
| BinPath string |
| ElfPath string |
| ManifestPath string |
| ExtraManifest map[string]interface{} |
| } |
| |
| type MfgEmitRaw struct { |
| Filename string |
| Offset int |
| Size int |
| ExtraFiles []string |
| ExtraManifest map[string]interface{} |
| } |
| |
| type MfgEmitMetaMmr struct { |
| Area flash.FlashArea |
| } |
| |
| type MfgEmitMeta struct { |
| Offset int |
| Hash bool |
| FlashMap bool |
| Mmrs []MfgEmitMetaMmr |
| } |
| |
| type MfgEmitter struct { |
| Name string |
| Ver image.ImageVersion |
| Targets []MfgEmitTarget |
| Raws []MfgEmitRaw |
| Meta *MfgEmitMeta |
| Keys []sec.PrivSignKey |
| |
| Mfg mfg.Mfg |
| Device int |
| FlashMap flashmap.FlashMap |
| BspName string |
| Compiler *toolchain.Compiler |
| } |
| |
| // Calculates the source path of a target's binary. Boot loader targets use |
| // `.bin` files; image targets use `.img`. |
| func targetSrcBinPath(t *target.Target, isBoot bool) string { |
| if isBoot { |
| return builder.AppBinPath(t.Name(), builder.BUILD_NAME_APP, |
| t.App().Name()) |
| } else { |
| return builder.AppImgPath(t.Name(), builder.BUILD_NAME_APP, |
| t.App().Name()) |
| } |
| } |
| |
| // Calculates the source path of a target's `.elf` file. |
| func targetSrcElfPath(t *target.Target) string { |
| return builder.AppElfPath(t.Name(), builder.BUILD_NAME_APP, t.App().Name()) |
| } |
| |
| // Calculates the source path of a target's manifest file. |
| func targetSrcManifestPath(t *target.Target) string { |
| return builder.ManifestPath(t.Name(), builder.BUILD_NAME_APP, |
| t.App().Name()) |
| } |
| |
| func newMfgEmitTarget(bt MfgBuildTarget) (MfgEmitTarget, error) { |
| return MfgEmitTarget{ |
| Name: bt.Target.FullName(), |
| Offset: bt.Area.Offset + bt.Offset, |
| Size: bt.Size, |
| IsBoot: bt.IsBoot, |
| BinPath: targetSrcBinPath(bt.Target, bt.IsBoot), |
| ElfPath: targetSrcElfPath(bt.Target), |
| ManifestPath: builder.ManifestPath(bt.Target.Name(), |
| builder.BUILD_NAME_APP, bt.Target.App().Name()), |
| ExtraManifest: bt.ExtraManifest, |
| }, nil |
| } |
| |
| func newMfgEmitRaw(br MfgBuildRaw) MfgEmitRaw { |
| return MfgEmitRaw{ |
| Filename: br.Filename, |
| Offset: br.Area.Offset + br.Offset, |
| Size: br.Size, |
| ExtraFiles: br.ExtraFiles, |
| ExtraManifest: br.ExtraManifest, |
| } |
| } |
| |
| func newMfgEmitMeta(bm MfgBuildMeta, metaOff int) MfgEmitMeta { |
| mmrs := []MfgEmitMetaMmr{} |
| for _, bmmr := range bm.Mmrs { |
| mmr := MfgEmitMetaMmr{ |
| Area: bmmr.Area, |
| } |
| mmrs = append(mmrs, mmr) |
| } |
| |
| return MfgEmitMeta{ |
| Offset: bm.Area.Offset + metaOff, |
| Hash: bm.Hash, |
| FlashMap: bm.FlashMap, |
| Mmrs: mmrs, |
| } |
| } |
| |
| func getCompilerFromBsp(bsp *pkg.BspPackage) (*toolchain.Compiler, error) { |
| proj := project.GetProject() |
| compilerPkg, err := proj.ResolvePackage(bsp.Repo(), bsp.CompilerName) |
| if err != nil { |
| return nil, err |
| } |
| |
| c, err := toolchain.NewCompiler(compilerPkg.BasePath(), "", |
| target.DEFAULT_BUILD_PROFILE) |
| if err != nil { |
| return nil, err |
| } |
| |
| return c, nil |
| } |
| |
| // NewMfgEmitter creates an mfg emitter from an mfg builder. |
| func NewMfgEmitter(mb MfgBuilder, name string, ver image.ImageVersion, |
| device int, keys []sec.PrivSignKey) (MfgEmitter, error) { |
| |
| me := MfgEmitter{ |
| Name: name, |
| Ver: ver, |
| Device: device, |
| Keys: keys, |
| FlashMap: mb.Bsp.FlashMap, |
| BspName: mb.Bsp.FullName(), |
| } |
| |
| c, err := getCompilerFromBsp(mb.Bsp) |
| if err != nil { |
| return me, err |
| } |
| me.Compiler = c |
| |
| m, err := mb.Build() |
| if err != nil { |
| return me, err |
| } |
| me.Mfg = m |
| |
| for _, bt := range mb.Targets { |
| et, err := newMfgEmitTarget(bt) |
| if err != nil { |
| return me, err |
| } |
| |
| me.Targets = append(me.Targets, et) |
| } |
| |
| for _, br := range mb.Raws { |
| et := newMfgEmitRaw(br) |
| me.Raws = append(me.Raws, et) |
| } |
| |
| if mb.Meta != nil { |
| mm := newMfgEmitMeta(*mb.Meta, me.Mfg.MetaOff) |
| me.Meta = &mm |
| } |
| |
| return me, nil |
| } |
| |
| func (me *MfgEmitter) calcCpEntriesTarget(mt MfgEmitTarget, idx int) []CpEntry { |
| var entries []CpEntry |
| |
| var binTo string |
| if mt.IsBoot { |
| binTo = MfgTargetBinPath(me.Name, idx) |
| } else { |
| binTo = MfgTargetImgPath(me.Name, idx) |
| } |
| |
| entry := CpEntry{ |
| From: mt.BinPath, |
| To: binTo, |
| } |
| entries = append(entries, entry) |
| |
| entry = CpEntry{ |
| From: mt.ElfPath, |
| To: MfgTargetElfPath(me.Name, idx), |
| } |
| entries = append(entries, entry) |
| |
| entry = CpEntry{ |
| From: mt.ManifestPath, |
| To: MfgTargetManifestPath(me.Name, idx), |
| } |
| entries = append(entries, entry) |
| |
| return entries |
| } |
| |
| func (me *MfgEmitter) calcCpEntriesRaw(mr MfgEmitRaw, idx int) []CpEntry { |
| var entries []CpEntry |
| |
| // Include `.bin` file. |
| entries = append(entries, |
| CpEntry{ |
| From: mr.Filename, |
| To: MfgRawBinPath(me.Name, idx), |
| }) |
| |
| // Include each extra file. |
| for _, ef := range mr.ExtraFiles { |
| entries = append(entries, CpEntry{ |
| From: ef, |
| To: MfgRawDir(me.Name, idx) + "/" + |
| filepath.Base(ef), |
| }) |
| } |
| |
| return entries |
| } |
| |
| // Calculates the necessary file copy operations for emitting an mfg image. |
| func (me *MfgEmitter) calcCpEntries() []CpEntry { |
| entries := []CpEntry{} |
| for i, mt := range me.Targets { |
| targetEntries := me.calcCpEntriesTarget(mt, i) |
| entries = append(entries, targetEntries...) |
| } |
| |
| for i, mr := range me.Raws { |
| rawEntries := me.calcCpEntriesRaw(mr, i) |
| entries = append(entries, rawEntries...) |
| } |
| |
| return entries |
| } |
| |
| func copyBinFiles(entries []CpEntry) error { |
| for _, entry := range entries { |
| if err := os.MkdirAll(filepath.Dir(entry.To), 0755); err != nil { |
| return util.ChildNewtError(err) |
| } |
| |
| util.StatusMessage(util.VERBOSITY_VERBOSE, "copying file %s --> %s\n", |
| entry.From, entry.To) |
| |
| if err := util.CopyFile(entry.From, entry.To); err != nil { |
| return err |
| } |
| } |
| |
| return nil |
| } |
| |
| func (me *MfgEmitter) createSigs() ([]manifest.MfgManifestSig, error) { |
| hashBytes, err := me.Mfg.Hash(0xff) |
| if err != nil { |
| return nil, err |
| } |
| |
| var sigs []manifest.MfgManifestSig |
| for _, k := range me.Keys { |
| sig, err := image.GenerateSig(k, hashBytes) |
| if err != nil { |
| return nil, err |
| } |
| |
| pubKey, err := k.PubBytes() |
| if err != nil { |
| return nil, err |
| } |
| keyHash := sec.RawKeyHash(pubKey) |
| |
| sigs = append(sigs, manifest.MfgManifestSig{ |
| Key: hex.EncodeToString(keyHash), |
| Sig: hex.EncodeToString(sig.Data), |
| }) |
| } |
| |
| return sigs, nil |
| } |
| |
| func (me *MfgEmitter) convertHexImages() ([]string, error) { |
| dstPaths := []string{} |
| for i, mt := range me.Targets { |
| var binPath, hexPath string |
| if mt.IsBoot { |
| binPath = MfgTargetBinPath(me.Name, i) |
| } else { |
| binPath = MfgTargetImgPath(me.Name, i) |
| } |
| hexPath = MfgTargetHexPath(me.Name, i) |
| |
| err := me.Compiler.ConvertBinToHex(binPath, hexPath, mt.Offset) |
| if err != nil { |
| return dstPaths, err |
| } |
| dstPaths = append(dstPaths, hexPath) |
| } |
| return dstPaths, nil |
| } |
| |
| // emitManifest generates an mfg manifest. |
| func (me *MfgEmitter) emitManifest() ([]byte, error) { |
| hashBytes, err := me.Mfg.Hash(0xff) |
| if err != nil { |
| return nil, err |
| } |
| |
| sigs, err := me.createSigs() |
| if err != nil { |
| return nil, err |
| } |
| |
| mm := manifest.MfgManifest{ |
| Name: me.Name, |
| BuildTime: time.Now().Format(time.RFC3339), |
| Format: MANIFEST_FORMAT, |
| MfgHash: hex.EncodeToString(hashBytes), |
| Version: me.Ver.String(), |
| Device: me.Device, |
| BinPath: mfg.MFG_BIN_IMG_FILENAME, |
| HexPath: mfg.MFG_HEX_IMG_FILENAME, |
| Signatures: sigs, |
| FlashAreas: me.FlashMap.SortedAreas(), |
| Bsp: me.BspName, |
| EraseVal: 0xff, |
| } |
| |
| for i, t := range me.Targets { |
| mmt := manifest.MfgManifestTarget{ |
| Name: t.Name, |
| ManifestPath: MfgTargetManifestPath(me.Name, i), |
| Offset: t.Offset, |
| Size: t.Size, |
| Extra: t.ExtraManifest, |
| } |
| |
| if t.IsBoot { |
| mmt.BinPath = MfgTargetBinPath(me.Name, i) |
| } else { |
| mmt.ImagePath = MfgTargetImgPath(me.Name, i) |
| } |
| mmt.HexPath = MfgTargetHexPath(me.Name, i) |
| |
| mm.Targets = append(mm.Targets, mmt) |
| } |
| |
| basePath := project.GetProject().BasePath |
| |
| for i, r := range me.Raws { |
| mmr := manifest.MfgManifestRaw{ |
| Filename: strings.TrimPrefix(r.Filename, basePath+"/"), |
| Offset: r.Offset, |
| Size: r.Size, |
| BinPath: MfgRawBinPath(me.Name, i), |
| Extra: r.ExtraManifest, |
| } |
| |
| mm.Raws = append(mm.Raws, mmr) |
| } |
| |
| if me.Meta != nil { |
| mmm := manifest.MfgManifestMeta{ |
| EndOffset: me.Mfg.MetaOff + int(me.Mfg.Meta.Footer.Size), |
| Size: int(me.Mfg.Meta.Footer.Size), |
| } |
| |
| mmm.Hash = me.Meta.Hash |
| mmm.FlashMap = me.Meta.FlashMap |
| |
| for _, mmr := range me.Meta.Mmrs { |
| mmm.Mmrs = append(mmm.Mmrs, manifest.MfgManifestMetaMmr{ |
| Area: mmr.Area.Name, |
| Device: mmr.Area.Device, |
| EndOffset: mmr.Area.Offset + mmr.Area.Size, |
| }) |
| } |
| |
| mm.Meta = &mmm |
| } |
| |
| return mm.MarshalJson() |
| } |
| |
| // @return [source-paths], [dest-paths], error |
| func (me *MfgEmitter) Emit() ([]string, []string, error) { |
| if err := me.Mfg.RefillHash(0xff); err != nil { |
| return nil, nil, err |
| } |
| |
| mbin, err := me.Mfg.Bytes(0xff) |
| if err != nil { |
| return nil, nil, err |
| } |
| |
| cpEntries := me.calcCpEntries() |
| if err := copyBinFiles(cpEntries); err != nil { |
| return nil, nil, err |
| } |
| |
| dstPaths, err := me.convertHexImages() |
| if err != nil { |
| return nil, nil, err |
| } |
| |
| // Write mfgimg.bin |
| binPath := MfgBinPath(me.Name) |
| if err := os.MkdirAll(filepath.Dir(binPath), 0755); err != nil { |
| return nil, nil, util.ChildNewtError(err) |
| } |
| if err := ioutil.WriteFile(binPath, mbin, 0644); err != nil { |
| return nil, nil, err |
| } |
| |
| // Write mfgimg.hex |
| hexPath := MfgHexPath(me.Name) |
| if err := me.Compiler.ConvertBinToHex(binPath, hexPath, 0); err != nil { |
| return nil, nil, err |
| } |
| |
| // Write manifest. |
| manifest, err := me.emitManifest() |
| if err != nil { |
| return nil, nil, err |
| } |
| |
| manifestPath := MfgManifestPath(me.Name) |
| if err := ioutil.WriteFile(manifestPath, manifest, 0644); err != nil { |
| return nil, nil, util.FmtNewtError( |
| "Failed to write mfg manifest file: %s", err.Error()) |
| } |
| |
| srcPaths := []string{} |
| dstPaths = append(dstPaths, binPath, hexPath, manifestPath) |
| |
| for _, entry := range cpEntries { |
| srcPaths = append(srcPaths, entry.From) |
| dstPaths = append(dstPaths, entry.To) |
| } |
| |
| return srcPaths, dstPaths, nil |
| } |