1/* SPDX-License-Identifier: GPL-2.0-or-later */ 2 3package main 4 5import ( 6 "encoding/csv" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "log" 11 "os" 12 "path/filepath" 13 "strconv" 14 "strings" 15) 16 17/* 18 * This program allocates DRAM strap IDs for different parts that are being used by the variant. 19 * 20 * It expects the following inputs: 21 * Name of the SoC platform, e.g. TGL. 22 * Memory technology used by the variant, e.g. lp4x. 23 * Path to Makefile directory. Makefile.mk generated by this program is placed in this 24 * location. 25 * Text file containing a list of memory part names used by the board. Each line in the file 26 * is expected to have one memory part name. 27 */ 28const ( 29 SPDManifestFileName = "parts_spd_manifest.generated.txt" 30 PlatformsManifestFileName = "platforms_manifest.generated.txt" 31 SPDEmptyFileName = "spd-empty.hex" 32 MakefileName = "Makefile.mk" 33 DRAMIdFileName = "dram_id.generated.txt" 34 MaxMemoryId = 15 35) 36 37var supportedPlatforms = [...]string{ 38 "TGL", 39 "ADL", 40 "JSL", 41 "PCO", 42 "CZN", 43 "MDN", 44 "MTL", 45 "PHX", 46} 47 48var supportedMemTechs = [...]string{ 49 "lp4x", 50 "ddr4", 51 "lp5", 52} 53 54func usage() { 55 fmt.Printf("\nUsage: %s <platform> <mem_technology> <makefile_dir> <mem_parts_used_file>\n\n", os.Args[0]) 56 fmt.Printf(" where,\n") 57 fmt.Printf(" platform = SoC platform which the board is based on\n") 58 fmt.Printf(" supported platforms: %v\n", supportedPlatforms) 59 fmt.Printf(" mem_technology = Memory technology used by the board\n") 60 fmt.Printf(" supported technologies: %v\n", supportedMemTechs) 61 fmt.Printf(" makefile_dir = Directory path where generated Makefile.mk should be placed\n") 62 fmt.Printf(" mem_parts_used_file = CSV file containing list of memory parts used by the board and optional fixed ids\n\n\n") 63} 64 65func checkArgs(platform string, memTech string, makefileDir string, memPartsUsedFile string) error { 66 supported := false 67 for _, p := range supportedPlatforms { 68 if strings.ToUpper(platform) == p { 69 supported = true 70 break 71 } 72 } 73 if !supported { 74 return fmt.Errorf("Platform %s is not supported", platform) 75 } 76 77 supported = false 78 for _, m := range supportedMemTechs { 79 if strings.ToLower(memTech) == m { 80 supported = true 81 break 82 } 83 } 84 if !supported { 85 return fmt.Errorf("Memory technology %s is not supported", memTech) 86 } 87 88 if _, err := os.Stat(makefileDir); err != nil { 89 return fmt.Errorf("Invalid makefile_dir %s: %v", makefileDir, err) 90 } 91 92 if _, err := os.Stat(memPartsUsedFile); err != nil { 93 return fmt.Errorf("Invalid mem_parts_used_file %s: %v", memPartsUsedFile, err) 94 } 95 96 return nil 97} 98 99type mappingType int 100 101const ( 102 Auto mappingType = iota 103 Fixed 104 Exclusive 105) 106 107type usedPart struct { 108 partName string 109 index int 110 mapping mappingType 111 SPDOverride string 112} 113 114func readPlatformsManifest(memTech string) (map[string]string, error) { 115 manifestFilePath := filepath.Join("spd", strings.ToLower(memTech), PlatformsManifestFileName) 116 f, err := os.Open(manifestFilePath) 117 if err != nil { 118 return nil, err 119 } 120 defer f.Close() 121 r := csv.NewReader(f) 122 r.Comment = '#' 123 124 platformToSetMap := make(map[string]string) 125 126 for { 127 fields, err := r.Read() 128 129 if err == io.EOF { 130 break 131 } 132 133 if err != nil { 134 return nil, err 135 } 136 137 if len(fields) != 2 { 138 return nil, fmt.Errorf("Platforms manifest file is incorrectly formatted: %s", manifestFilePath) 139 } 140 141 platformToSetMap[fields[0]] = fields[1] 142 } 143 144 return platformToSetMap, nil 145} 146 147func getSPDDir(platform string, memTech string) (string, error) { 148 platformToSetMap, err := readPlatformsManifest(memTech) 149 if err != nil { 150 return "", err 151 } 152 153 setName, ok := platformToSetMap[strings.ToUpper(platform)] 154 if !ok { 155 return "", fmt.Errorf("Platform %s does not support memory technology %s", platform, memTech) 156 } 157 158 return filepath.Join("spd", strings.ToLower(memTech), setName), nil 159} 160 161/* 162 * Read input file CSV that contains list of memory part names used by the variant 163 * and an optional assigned id. 164 */ 165func readParts(memPartsUsedFileName string) ([]usedPart, error) { 166 f, err := os.Open(memPartsUsedFileName) 167 if err != nil { 168 return nil, err 169 } 170 defer f.Close() 171 r := csv.NewReader(f) 172 r.FieldsPerRecord = -1 // Allow variable length records 173 r.TrimLeadingSpace = true 174 r.Comment = '#' 175 176 parts := []usedPart{} 177 178 for { 179 fields, err := r.Read() 180 181 if err == io.EOF { 182 break 183 } 184 185 if err != nil { 186 return nil, err 187 } 188 189 if len(fields) == 1 { 190 parts = append(parts, usedPart{fields[0], -1, Auto, ""}) 191 } else { 192 var mapping = Auto 193 var assignedId = -1 194 var err error = nil 195 var spdOverride string = "" 196 197 // Second column, ID override 198 if len(fields) >= 2 { 199 if len(fields[1]) >= 2 && fields[1][0] == '*' { 200 // Exclusive mapping 201 mapping = Exclusive 202 assignedId, err = strconv.Atoi(fields[1][1:]) 203 } else if fields[1] != "" { 204 // Fixed mapping 205 mapping = Fixed 206 assignedId, err = strconv.Atoi(fields[1]) 207 } 208 } 209 210 // Third column, SPD file override 211 if len(fields) >= 3 { 212 if len(fields[2]) == 0 { 213 err = fmt.Errorf("mem_parts_used_file file is incorrectly formatted, SPD file column is empty") 214 } else { 215 spdOverride = fields[2] 216 } 217 } 218 219 if err != nil { 220 return nil, err 221 } 222 223 if assignedId > MaxMemoryId { 224 return nil, fmt.Errorf("Out of bounds assigned id %d for part %s", assignedId, fields[0]) 225 } 226 227 parts = append(parts, usedPart{fields[0], assignedId, mapping, spdOverride}) 228 } 229 } 230 231 return parts, nil 232} 233 234/* 235 * Read SPD manifest file(CSV) generated by gen_spd program and generate two maps: 236 * 1. Part to SPD Map : This maps global memory part name to generated SPD file name 237 * 2. SPD to Index Map: This generates a map of deduplicated SPD file names to index assigned to 238 * that SPD. This function sets the index for all SPDs to -1. This index gets 239 * updated as part of genPartIdInfo() depending upon the SPDs actually used 240 * by the variant. 241 */ 242func readSPDManifest(SPDDirName string) (map[string]string, map[string]int, error) { 243 f, err := os.Open(filepath.Join(SPDDirName, SPDManifestFileName)) 244 if err != nil { 245 return nil, nil, err 246 } 247 defer f.Close() 248 r := csv.NewReader(f) 249 r.Comment = '#' 250 251 partToSPDMap := make(map[string]string) 252 SPDToIndexMap := make(map[string]int) 253 254 for { 255 fields, err := r.Read() 256 257 if err == io.EOF { 258 break 259 } 260 261 if err != nil { 262 return nil, nil, err 263 } 264 265 if len(fields) != 2 { 266 return nil, nil, fmt.Errorf("CSV file is incorrectly formatted") 267 } 268 269 partToSPDMap[fields[0]] = fields[1] 270 SPDToIndexMap[fields[1]] = -1 271 } 272 273 return partToSPDMap, SPDToIndexMap, nil 274} 275 276/* Print information about memory part used by variant and ID assigned to it. */ 277func appendPartIdInfo(s *string, partName string, index int) { 278 *s += fmt.Sprintf("%-30s %d (%04b)\n", partName, index, int64(index)) 279} 280 281type partIds struct { 282 SPDFileName string 283 memParts string 284} 285 286func getFileHeader() string { 287 return `# SPDX-License-Identifier: GPL-2.0-or-later 288# This is an auto-generated file. Do not edit!! 289# Generated by: 290` + fmt.Sprintf("# %s\n\n", strings.Join(os.Args[0:], " ")) 291} 292 293/* 294 * For each part used by the variant, check if the SPD (as per the manifest) already has an ID 295 * assigned to it. If yes, then add the part name to the list of memory parts supported by the 296 * SPD entry. If not, then assign the next ID to the SPD file and add the part name to the 297 * list of memory parts supported by the SPD entry. 298 * 299 * Returns list of partIds that contains spdFileName and supported memory parts for each 300 * assigned ID. 301 */ 302func genPartIdInfo(parts []usedPart, partToSPDMap map[string]string, SPDToIndexMap map[string]int, makefileDirName string) ([]partIds, error) { 303 partIdList := []partIds{} 304 assignedMapping := []mappingType{} 305 var s string 306 307 // Assign parts with fixed ids first 308 for _, p := range parts { 309 if p.index == -1 { 310 continue 311 } 312 313 if p.partName == "" { 314 return nil, fmt.Errorf("Invalid part entry") 315 } 316 317 SPDFileName, ok := partToSPDMap[p.partName] 318 if !ok { 319 return nil, fmt.Errorf("Failed to find part %s in SPD Manifest. Please add the part to global part list and regenerate SPD Manifest", p.partName) 320 } 321 322 // Extend partIdList and assignedMapping with empty entries if needed 323 for i := len(partIdList) - 1; i < p.index; i++ { 324 partIdList = append(partIdList, partIds{}) 325 assignedMapping = append(assignedMapping, Auto) 326 } 327 328 // Only allow parts with the same index if they share the same SPD 329 assignedSPD := partIdList[p.index].SPDFileName 330 if assignedSPD != "" && assignedSPD != partToSPDMap[p.partName] { 331 return nil, fmt.Errorf("ID %d is already assigned to %s, conflicting with %s(%s)", p.index, assignedSPD, p.partName, SPDFileName) 332 } 333 334 mapping := assignedMapping[p.index] 335 if (mapping == Fixed && p.mapping == Exclusive) || (mapping == Exclusive && p.mapping == Fixed) { 336 return nil, fmt.Errorf("Exclusive/non-exclusive conflict in assigning %s to ID %d", p.partName, p.index) 337 } else { 338 assignedMapping[p.index] = p.mapping 339 } 340 341 if partIdList[p.index].memParts == "" { 342 partIdList[p.index] = partIds{SPDFileName: SPDFileName, memParts: p.partName} 343 } else { 344 partIdList[p.index].memParts += ", " + p.partName 345 } 346 347 // SPDToIndexMap should point to first assigned index in the used part list 348 // Exclusive entries don't update the map because they're not valid for auto assigning 349 if SPDToIndexMap[SPDFileName] < 0 && p.mapping != Exclusive { 350 SPDToIndexMap[SPDFileName] = p.index 351 } 352 } 353 354 s += fmt.Sprintf("%-30s %s\n", "DRAM Part Name", "ID to assign") 355 356 // Assign parts with no fixed id 357 for _, p := range parts { 358 if p.partName == "" { 359 return nil, fmt.Errorf("Invalid part entry") 360 } 361 362 // Add assigned parts to dram id file in the order they appear 363 if p.index != -1 { 364 appendPartIdInfo(&s, p.partName, p.index) 365 continue 366 } 367 368 SPDFileName, ok := partToSPDMap[p.partName] 369 if !ok { 370 return nil, fmt.Errorf("Failed to find part ", p.partName, " in SPD Manifest. Please add the part to global part list and regenerate SPD Manifest") 371 } 372 373 index := SPDToIndexMap[SPDFileName] 374 // Only Exclusive mappings don't allow automatic assigning of parts 375 if index != -1 && assignedMapping[index] != Exclusive { 376 partIdList[index].memParts += ", " + p.partName 377 appendPartIdInfo(&s, p.partName, index) 378 continue 379 } 380 381 // Find first empty index 382 for i, partId := range partIdList { 383 if partId.SPDFileName == "" { 384 index = i 385 break 386 } 387 } 388 389 // Append new entry 390 if index == -1 { 391 index = len(partIdList) 392 if index > MaxMemoryId { 393 return nil, fmt.Errorf("Maximum part ID %d exceeded.", MaxMemoryId) 394 } 395 partIdList = append(partIdList, partIds{}) 396 assignedMapping = append(assignedMapping, Auto) 397 } 398 399 SPDToIndexMap[SPDFileName] = index 400 appendPartIdInfo(&s, p.partName, index) 401 partIdList[index] = partIds{SPDFileName: SPDFileName, memParts: p.partName} 402 } 403 404 fmt.Printf("%s", s) 405 406 s = getFileHeader() + s 407 err := ioutil.WriteFile(filepath.Join(makefileDirName, DRAMIdFileName), []byte(s), 0644) 408 409 return partIdList, err 410} 411 412/* 413 * This function generates Makefile.mk under the variant directory path and adds assigned SPDs 414 * to SPD_SOURCES. 415 */ 416func genMakefile(partIdList []partIds, makefileDirName string, SPDDir string, partsDir string) error { 417 s := getFileHeader() 418 s += fmt.Sprintf("SPD_SOURCES =\n") 419 420 for i := 0; i < len(partIdList); i++ { 421 if partIdList[i].SPDFileName == "" { 422 s += fmt.Sprintf("SPD_SOURCES += %v ", filepath.Join(SPDDir, SPDEmptyFileName)) 423 s += fmt.Sprintf(" # ID = %d(0b%04b)\n", i, int64(i)) 424 } else { 425 SPDFileName := partIdList[i].SPDFileName 426 path := filepath.Join(partsDir, SPDFileName) 427 428 // Check if the file exists in the directory of the parts file 429 if _, err := os.Stat(path); err != nil { 430 // File doesn't exist, check spd directory 431 path = filepath.Join(SPDDir, SPDFileName) 432 if _, err = os.Stat(path); err != nil { 433 return fmt.Errorf("Failed to write Makefile, SPD file '%s' doesn't exist", SPDFileName) 434 } 435 } 436 s += fmt.Sprintf("SPD_SOURCES += %v ", path) 437 s += fmt.Sprintf(" # ID = %d(0b%04b) ", i, int64(i)) 438 s += fmt.Sprintf(" Parts = %04s\n", partIdList[i].memParts) 439 } 440 } 441 442 return ioutil.WriteFile(filepath.Join(makefileDirName, MakefileName), []byte(s), 0644) 443} 444 445func main() { 446 if len(os.Args) != 5 { 447 usage() 448 log.Fatal("Incorrect number of arguments") 449 } 450 451 platform, memTech, makefileDir, memPartsUsedFile := os.Args[1], os.Args[2], os.Args[3], os.Args[4] 452 453 err := checkArgs(platform, memTech, makefileDir, memPartsUsedFile) 454 if err != nil { 455 log.Fatal(err) 456 } 457 458 SPDDir, err := getSPDDir(platform, memTech) 459 if err != nil { 460 log.Fatal(err) 461 } 462 463 partToSPDMap, SPDToIndexMap, err := readSPDManifest(SPDDir) 464 if err != nil { 465 log.Fatal(err) 466 } 467 468 parts, err := readParts(memPartsUsedFile) 469 if err != nil { 470 log.Fatal(err) 471 } 472 473 // Update our SPD maps with part specific overrides 474 for _, p := range parts { 475 if p.SPDOverride != "" { 476 partToSPDMap[p.partName] = p.SPDOverride 477 SPDToIndexMap[p.SPDOverride] = -1 478 } 479 } 480 481 partIdList, err := genPartIdInfo(parts, partToSPDMap, SPDToIndexMap, makefileDir) 482 if err != nil { 483 log.Fatal(err) 484 } 485 486 if err := genMakefile(partIdList, makefileDir, SPDDir, filepath.Dir(memPartsUsedFile)); err != nil { 487 log.Fatal(err) 488 } 489} 490