newt/flashmap/flashmap.go (264 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 flashmap import ( "bytes" "fmt" "io" "io/ioutil" "os" "path/filepath" "strings" log "github.com/sirupsen/logrus" "github.com/spf13/cast" "github.com/apache/mynewt-artifact/flash" "mynewt.apache.org/newt/newt/newtutil" "mynewt.apache.org/newt/util" ) const HEADER_PATH = "sysflash/sysflash.h" const C_VAR_NAME = "sysflash_map_dflt" const C_VAR_COMMENT = `/** * This flash map definition is used for two purposes: * 1. To locate the meta area, which contains the true flash map definition. * 2. As a fallback in case the meta area cannot be read from flash. */ ` type FlashMap struct { PkgName string Areas map[string]flash.FlashArea Overlaps [][]flash.FlashArea IdConflicts [][]flash.FlashArea } func newFlashMap() FlashMap { return FlashMap{ Areas: map[string]flash.FlashArea{}, Overlaps: [][]flash.FlashArea{}, } } func flashAreaErr(areaName string, format string, args ...interface{}) error { return util.NewNewtError( "failure while parsing flash area \"" + areaName + "\": " + fmt.Sprintf(format, args...)) } func parseSize(val string) (int, error) { lower := strings.ToLower(val) multiplier := 1 if strings.HasSuffix(lower, "kb") { multiplier = 1024 lower = strings.TrimSuffix(lower, "kb") } num, err := util.AtoiNoOct(lower) if err != nil { return 0, err } return num * multiplier, nil } func parseFlashArea( name string, ymlFields map[string]interface{}) (flash.FlashArea, error) { area := flash.FlashArea{ Name: name, } idPresent := false devicePresent := false offsetPresent := false sizePresent := false var isSystem bool area.Id, isSystem = flash.SYSTEM_AREA_NAME_ID_MAP[name] var err error fields := cast.ToStringMapString(ymlFields) for k, v := range fields { switch k { case "user_id": if isSystem { return area, flashAreaErr(name, "system areas cannot specify a user ID") } userId, err := util.AtoiNoOct(v) if err != nil { return area, flashAreaErr(name, "invalid user id: %s", v) } area.Id = userId + flash.AREA_USER_ID_MIN idPresent = true case "device": area.Device, err = util.AtoiNoOct(v) if err != nil { return area, flashAreaErr(name, "invalid device: %s", v) } devicePresent = true case "offset": area.Offset, err = util.AtoiNoOct(v) if err != nil { return area, flashAreaErr(name, "invalid offset: %s", v) } offsetPresent = true case "size": area.Size, err = parseSize(v) if err != nil { return area, flashAreaErr(name, err.Error()) } sizePresent = true default: util.StatusMessage(util.VERBOSITY_QUIET, "Warning: flash area \"%s\" contains unrecognized field: %s", name, k) } } if !isSystem && !idPresent { return area, flashAreaErr(name, "required field \"user_id\" missing") } if !devicePresent { return area, flashAreaErr(name, "required field \"device\" missing") } if !offsetPresent { return area, flashAreaErr(name, "required field \"offset\" missing") } if !sizePresent { return area, flashAreaErr(name, "required field \"size\" missing") } return area, nil } func (flashMap FlashMap) unSortedAreas() []flash.FlashArea { areas := make([]flash.FlashArea, 0, len(flashMap.Areas)) for _, area := range flashMap.Areas { areas = append(areas, area) } return areas } func (flashMap FlashMap) SortedAreas() []flash.FlashArea { areas := flashMap.unSortedAreas() return flash.SortFlashAreasById(areas) } func areasDistinct(a flash.FlashArea, b flash.FlashArea) bool { var lo flash.FlashArea var hi flash.FlashArea if a.Offset < b.Offset { lo = a hi = b } else { lo = b hi = a } return lo.Device != hi.Device || lo.Offset+lo.Size <= hi.Offset } func (flashMap *FlashMap) detectOverlaps() { flashMap.Overlaps, flashMap.IdConflicts = flash.DetectErrors(flashMap.unSortedAreas()) } func (flashMap FlashMap) ErrorText() string { return flash.ErrorText(flashMap.Overlaps, flashMap.IdConflicts) } func Read(ymlFlashMap map[string]interface{}, pkgName string) (FlashMap, error) { flashMap := newFlashMap() flashMap.PkgName = pkgName ymlAreas := ymlFlashMap["areas"] if ymlAreas == nil { return flashMap, util.NewNewtError( "\"areas\" mapping missing from flash map definition") } areaMap := cast.ToStringMap(ymlAreas) for k, v := range areaMap { if _, ok := flashMap.Areas[k]; ok { return flashMap, flashAreaErr(k, "name conflict") } ymlArea := cast.ToStringMap(v) area, err := parseFlashArea(k, ymlArea) if err != nil { return flashMap, flashAreaErr(k, err.Error()) } flashMap.Areas[k] = area } flashMap.detectOverlaps() return flashMap, nil } func flashMapVarDecl(fm FlashMap) string { return fmt.Sprintf("const struct flash_area %s[FLASH_AREA_COUNT]", C_VAR_NAME) } func writeFlashAreaHeader(w io.Writer, area flash.FlashArea) { fmt.Fprintf(w, "#define %-40s %d\n", area.Name, area.Id) fmt.Fprintf(w, "#define %-40s %d\n", area.Name+"_DEVICE", area.Device) fmt.Fprintf(w, "#define %-40s 0x%08x\n", area.Name+"_OFFSET", area.Offset) fmt.Fprintf(w, "#define %-40s %d\n", area.Name+"_SIZE", area.Size) fmt.Fprintf(w, "\n") } func writeFlashMapHeader(w io.Writer, fm FlashMap) { fmt.Fprintf(w, newtutil.GeneratedPreamble()) fmt.Fprintf(w, "#ifndef H_MYNEWT_SYSFLASH_\n") fmt.Fprintf(w, "#define H_MYNEWT_SYSFLASH_\n") fmt.Fprintf(w, "\n") fmt.Fprintf(w, "#include \"flash_map/flash_map.h\"\n") fmt.Fprintf(w, "\n") fmt.Fprintf(w, "#define FLASH_AREA_COUNT %d\n", len(fm.Areas)) fmt.Fprintf(w, "\n") fmt.Fprintf(w, "%s", C_VAR_COMMENT) fmt.Fprintf(w, "extern %s;\n", flashMapVarDecl(fm)) fmt.Fprintf(w, "\n") fmt.Fprintf(w, "/* Flash map was defined in %s */\n", fm.PkgName) fmt.Fprintf(w, "\n") for _, area := range fm.SortedAreas() { writeFlashAreaHeader(w, area) } fmt.Fprintf(w, "#endif\n") } func sizeComment(size int) string { if size%1024 != 0 { return "" } return fmt.Sprintf(" /* %d kB */", size/1024) } func writeFlashAreaSrc(w io.Writer, area flash.FlashArea) { fmt.Fprintf(w, " {\n") fmt.Fprintf(w, " .fa_id = %s,\n", area.Name) fmt.Fprintf(w, " .fa_device_id = %s_DEVICE,\n", area.Name) fmt.Fprintf(w, " .fa_off = %s_OFFSET,\n", area.Name) fmt.Fprintf(w, " .fa_size = %s_SIZE, %s\n", area.Name, sizeComment(area.Size)) fmt.Fprintf(w, " },\n") } func writeFlashMapSrc(w io.Writer, fm FlashMap) { fmt.Fprintf(w, newtutil.GeneratedPreamble()) fmt.Fprintf(w, "#include \"%s\"\n", HEADER_PATH) fmt.Fprintf(w, "\n") fmt.Fprintf(w, "%s", C_VAR_COMMENT) fmt.Fprintf(w, "%s = {", flashMapVarDecl(fm)) for _, area := range fm.SortedAreas() { fmt.Fprintf(w, "\n") writeFlashAreaSrc(w, area) } fmt.Fprintf(w, "};\n") } func ensureFlashMapWrittenGen(path string, contents []byte) error { writeReqd, err := util.FileContentsChanged(path, contents) if err != nil { return err } if !writeReqd { log.Debugf("flash map unchanged; not writing file (%s).", path) return nil } log.Debugf("flash map changed; writing file (%s).", path) if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { return util.NewNewtError(err.Error()) } if err := ioutil.WriteFile(path, contents, 0644); err != nil { return util.NewNewtError(err.Error()) } return nil } func EnsureFlashMapWritten( fm FlashMap, srcDir string, includeDir string, targetName string) error { buf := bytes.Buffer{} writeFlashMapSrc(&buf, fm) if err := ensureFlashMapWrittenGen( fmt.Sprintf("%s/%s-sysflash.c", srcDir, targetName), buf.Bytes()); err != nil { return err } buf = bytes.Buffer{} writeFlashMapHeader(&buf, fm) if err := ensureFlashMapWrittenGen( includeDir+"/"+HEADER_PATH, buf.Bytes()); err != nil { return err } return nil }