newt/imgprod/imgprod.go (303 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. */ // imgprod - Image production. package imgprod import ( "fmt" "os" "os/exec" "strconv" "strings" "github.com/apache/mynewt-artifact/flash" "github.com/apache/mynewt-artifact/image" "github.com/apache/mynewt-artifact/sec" "mynewt.apache.org/newt/newt/builder" "mynewt.apache.org/newt/newt/manifest" "mynewt.apache.org/newt/newt/newtutil" "mynewt.apache.org/newt/newt/toolchain" "mynewt.apache.org/newt/util" ) type ImageProdOpts struct { LoaderSrcFilename string LoaderDstFilename string LoaderHexFilename string AppSrcFilename string AppDstFilename string AppHexFilename string EncKeyFilename string EncKeyIndex int Sections []image.Section Version image.ImageVersion SigKeys []sec.PrivSignKey BaseAddr int HdrPad int ImagePad int DummyC *toolchain.Compiler UseLegacyTLV bool } type ProducedImage struct { Filename string Image image.Image Hash []byte FileSize int } type ProducedImageSet struct { Loader *ProducedImage App ProducedImage } // writeImageFiles writes two image artifacts: // * <name>.img // * <name>.hex func writeImageFiles(ri image.Image, imgFilename string, hexFilename string, baseAddr int, c *toolchain.Compiler) error { imgFile, err := os.OpenFile(imgFilename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666) if err != nil { return util.FmtNewtError( "can't open image file \"%s\" %s", imgFilename, err.Error()) } _, err = ri.Write(imgFile) imgFile.Close() if err != nil { return err } if err := c.ConvertBinToHex(imgFilename, hexFilename, baseAddr); err != nil { return err } return nil } func produceLoader(opts ImageProdOpts) (ProducedImage, error) { pi := ProducedImage{} igo := image.ImageCreateOpts{ SrcBinFilename: opts.LoaderSrcFilename, Sections: opts.Sections, SrcEncKeyFilename: opts.EncKeyFilename, SrcEncKeyIndex: opts.EncKeyIndex, Version: opts.Version, SigKeys: opts.SigKeys, } ri, err := image.GenerateImage(igo) if err != nil { return pi, err } hash, err := ri.Hash() if err != nil { return pi, err } fileSize, err := ri.TotalSize() if err != nil { return pi, err } if err := writeImageFiles(ri, opts.LoaderDstFilename, opts.LoaderHexFilename, opts.BaseAddr, opts.DummyC); err != nil { return pi, err } util.StatusMessage(util.VERBOSITY_DEFAULT, "Loader image successfully generated: %s\n", opts.LoaderDstFilename) pi.Filename = opts.LoaderDstFilename pi.Image = ri pi.Hash = hash pi.FileSize = fileSize return pi, nil } func produceApp(opts ImageProdOpts, loaderHash []byte) (ProducedImage, error) { pi := ProducedImage{} igo := image.ImageCreateOpts{ SrcBinFilename: opts.AppSrcFilename, Sections: opts.Sections, SrcEncKeyFilename: opts.EncKeyFilename, SrcEncKeyIndex: opts.EncKeyIndex, Version: opts.Version, SigKeys: opts.SigKeys, LoaderHash: loaderHash, HdrPad: opts.HdrPad, ImagePad: opts.ImagePad, UseLegacyTLV: opts.UseLegacyTLV, } ri, err := image.GenerateImage(igo) if err != nil { return pi, err } hash, err := ri.Hash() if err != nil { return pi, err } fileSize, err := ri.TotalSize() if err != nil { return pi, err } if err := writeImageFiles(ri, opts.AppDstFilename, opts.AppHexFilename, opts.BaseAddr, opts.DummyC); err != nil { return pi, err } util.StatusMessage(util.VERBOSITY_DEFAULT, "App image successfully generated: %s\n", opts.AppDstFilename) pi.Filename = opts.AppDstFilename pi.Image = ri pi.Hash = hash pi.FileSize = fileSize return pi, nil } // Verifies that each already-built image leaves enough room for a boot trailer // a the end of its slot. func verifyImgSizes(pset ProducedImageSet, maxSizes []int) error { errLines := []string{} slot := 0 if pset.Loader != nil { if overflow := int(pset.Loader.FileSize) - maxSizes[0]; overflow > 0 { errLines = append(errLines, fmt.Sprintf("loader overflows slot-0 by %d bytes "+ "(image=%d max=%d)", overflow, pset.Loader.FileSize, maxSizes[0])) } slot++ } if overflow := int(pset.App.FileSize) - maxSizes[slot]; overflow > 0 { errLines = append(errLines, fmt.Sprintf("app overflows slot-%d by %d bytes "+ "(image=%d max=%d)", slot, overflow, pset.App.FileSize, maxSizes[slot])) } if len(errLines) > 0 { if !newtutil.NewtForce { return util.NewNewtError(strings.Join(errLines, "; ")) } else { for _, e := range errLines { util.StatusMessage(util.VERBOSITY_QUIET, "* Warning: %s (ignoring due to force flag)\n", e) } } } return nil } func ProduceImages(opts ImageProdOpts) (ProducedImageSet, error) { pset := ProducedImageSet{} var loaderHash []byte if opts.LoaderSrcFilename != "" { pi, err := produceLoader(opts) if err != nil { return pset, err } loaderHash = pi.Hash pset.Loader = &pi } pi, err := produceApp(opts, loaderHash) if err != nil { return pset, err } pset.App = pi return pset, nil } func ProduceManifest(opts manifest.ManifestCreateOpts) error { m, err := manifest.CreateManifest(opts) if err != nil { return err } file, err := os.Create(opts.TgtBldr.AppBuilder.ManifestPath()) if err != nil { return util.FmtNewtError("Cannot create manifest file %s: %s", opts.TgtBldr.AppBuilder.ManifestPath(), err.Error()) } defer file.Close() if _, err := m.Write(file); err != nil { return err } return nil } func OptsFromTgtBldr(b *builder.TargetBuilder, ver image.ImageVersion, sigKeys []sec.PrivSignKey, encKeyFilename string, encKeyIndex int, hdrPad int, imagePad int, sections []image.Section, useLegacyTLV bool) (ImageProdOpts, error) { // This compiler is just used for converting .img files to .hex files, so // dummy paths are OK. c, err := b.NewCompiler("", "") if err != nil { return ImageProdOpts{}, err } // If there is no flash area for slot 0, default to a base address of 0. img0Area := b.BspPkg().FlashMap.Areas[flash.FLASH_AREA_NAME_IMAGE_0] baseAddr := img0Area.Offset // If there is not a cmd line override, use the BSP values // for header pad and image pad if hdrPad <= 0 { hdrPad = b.BspPkg().ImageOffset } if imagePad <= 0 { imagePad = b.BspPkg().ImagePad } opts := ImageProdOpts{ AppSrcFilename: b.AppBuilder.AppBinPath(), AppDstFilename: b.AppBuilder.AppImgPath(), AppHexFilename: b.AppBuilder.AppHexPath(), EncKeyFilename: encKeyFilename, EncKeyIndex: encKeyIndex, Version: ver, SigKeys: sigKeys, DummyC: c, BaseAddr: baseAddr, HdrPad: hdrPad, ImagePad: imagePad, Sections: sections, UseLegacyTLV: useLegacyTLV, } if b.LoaderBuilder != nil { opts.LoaderSrcFilename = b.LoaderBuilder.AppBinPath() opts.LoaderDstFilename = b.LoaderBuilder.AppImgPath() opts.LoaderHexFilename = b.LoaderBuilder.AppHexPath() } return opts, nil } func ProduceAll(t *builder.TargetBuilder, ver image.ImageVersion, sigKeys []sec.PrivSignKey, encKeyFilename string, encKeyIndex int, hdrPad int, imagePad int, sectionString string, useLegacyTLV bool) error { elfPath := t.AppBuilder.AppElfPath() cmdName := "arm-none-eabi-objdump" cmdOut, err := exec.Command(cmdName, elfPath, "-hw").Output() if err != nil { return err } var sections []image.Section section_list := strings.Split(sectionString, ",") lines := strings.Split(string(cmdOut), "\n") var imgBase int for _, line := range lines { fields := strings.Fields(strings.Replace(line, "\t", " ", -1)) if len(fields) >= 8 { _, err := strconv.ParseUint(fields[0], 16, 64) if err != nil { continue } if fields[1] == ".imghdr" { base, err := strconv.ParseInt(fields[3], 16, 32) if err != nil { continue } imgBase = int(base) } for _, section := range section_list { if fields[1] == section { offset, _ := strconv.ParseInt(fields[3], 16, 32) size, _ := strconv.ParseInt(fields[2], 16, 32) s := image.Section{Name: section, Size: int(size), Offset: int(offset)} sections = append(sections, s) } } } } // update sections offset by subtracting off start of app image for s := range sections { sections[s].Offset = sections[s].Offset - imgBase } popts, err := OptsFromTgtBldr(t, ver, sigKeys, encKeyFilename, encKeyIndex, hdrPad, imagePad, sections, useLegacyTLV) if err != nil { return err } pset, err := ProduceImages(popts) if err != nil { return err } var loaderHash []byte if pset.Loader != nil { loaderHash = pset.Loader.Hash } mopts, err := manifest.OptsForImage(t, ver, pset.App.Hash, loaderHash) if err != nil { return err } if err := ProduceManifest(mopts); err != nil { return err } if err := verifyImgSizes(pset, mopts.TgtBldr.MaxImgSizes()); err != nil { return err } return nil }