newt/mfg/emit.go (387 lines of code) (raw):

/** * 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().FullName()) } else { return builder.AppImgPath(t.Name(), builder.BUILD_NAME_APP, t.App().FullName()) } } // 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().FullName()) } // 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().FullName()) } 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().FullName()), 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, nil) 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 }