xref: /aosp_15_r20/external/coreboot/util/spd_tools/src/spd_gen/spd_gen.go (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2package main
3
4import (
5	"encoding/json"
6	"fmt"
7	"io/ioutil"
8	"log"
9	"os"
10	"path/filepath"
11	"reflect"
12	"regexp"
13	"sort"
14	"strings"
15)
16
17/* ------------------------------------------------------------------------------------------ */
18/*                                     Program-defined types                                  */
19/* ------------------------------------------------------------------------------------------ */
20type memParts struct {
21	MemParts []memPart `json:"parts"`
22}
23
24type memPart struct {
25	Name    string
26	Attribs interface{}
27	SPDId   int
28}
29
30type memTech interface {
31	/*
32	 * Returns the set -> platform mapping for the memory technology. Platforms with the
33	 * same SPD requirements should be grouped together into a single set.
34	 */
35	getSetMap() map[int][]int
36
37	/*
38	 * Takes the name and attributes of a part, as read from the memory_parts JSON file.
39	 * Validates the attributes, returning an error if any attribute has an invalid value.
40	 * Stores the name and attributes internally to be used later.
41	 */
42	addNewPart(string, interface{}) error
43
44	/*
45	 * Takes the name of a part and a set number.
46	 * Retrieves the part's attributes which were stored by addNewPart(). Updates them by
47	 * setting any optional attributes which weren't specified in the JSON file to their
48	 * default values.
49	 * Returns these updated attributes.
50	 */
51	getSPDAttribs(string, int) (interface{}, error)
52
53	/*
54	 * Returns the size of an SPD file for this memory technology.
55	 */
56	getSPDLen() int
57
58	/*
59	 * Takes an SPD byte index and the attributes of a part.
60	 * Returns the value which that SPD byte should be set to based on the attributes.
61	 */
62	getSPDByte(int, interface{}) byte
63}
64
65/* ------------------------------------------------------------------------------------------ */
66/*                                         Constants                                          */
67/* ------------------------------------------------------------------------------------------ */
68
69const (
70	PlatformTGL = iota
71	PlatformADL
72	PlatformJSL
73	PlatformPCO
74	PlatformCZN
75	PlatformMDN
76	PlatformMTL
77	PlatformPHX
78	PlatformMax
79)
80
81const (
82	SPDManifestFileName      = "parts_spd_manifest.generated.txt"
83	PlatformManifestFileName = "platforms_manifest.generated.txt"
84)
85
86/* ------------------------------------------------------------------------------------------ */
87/*                                    Global variables                                        */
88/* ------------------------------------------------------------------------------------------ */
89
90var platformNames = map[int]string{
91	PlatformTGL: "TGL",
92	PlatformADL: "ADL",
93	PlatformJSL: "JSL",
94	PlatformPCO: "PCO",
95	PlatformCZN: "CZN",
96	PlatformMDN: "MDN",
97	PlatformMTL: "MTL",
98	PlatformPHX: "PHX",
99}
100
101var memTechMap = map[string]memTech{
102	"lp4x": lp4x{},
103	"ddr4": ddr4{},
104	"lp5":  lp5{},
105}
106
107/* ------------------------------------------------------------------------------------------ */
108/*                                Conversion Helper Functions                                 */
109/* ------------------------------------------------------------------------------------------ */
110
111func convNsToPs(timeNs int) int {
112	return timeNs * 1000
113}
114
115func convMtbToPs(mtb int) int {
116	return mtb * 125
117}
118
119func convPsToMtb(timePs int) int {
120	return divRoundUp(timePs, 125)
121}
122
123func convPsToMtbByte(timePs int) byte {
124	return byte(convPsToMtb(timePs) & 0xff)
125}
126
127func convPsToFtbByte(timePs int) byte {
128	mtb := convPsToMtb(timePs)
129	ftb := timePs - convMtbToPs(mtb)
130
131	return byte(ftb)
132}
133
134func convNsToMtb(timeNs int) int {
135	return convPsToMtb(convNsToPs(timeNs))
136}
137
138func convNsToMtbByte(timeNs int) byte {
139	return convPsToMtbByte(convNsToPs(timeNs))
140}
141
142func convNsToFtbByte(timeNs int) byte {
143	return convPsToFtbByte(convNsToPs(timeNs))
144}
145
146func divRoundUp(dividend int, divisor int) int {
147	return (dividend + divisor - 1) / divisor
148}
149
150/* ------------------------------------------------------------------------------------------ */
151/*                                        Functions                                           */
152/* ------------------------------------------------------------------------------------------ */
153
154func findIndex(dedupedAttribs []interface{}, newSPDAttribs interface{}) int {
155	for i := 0; i < len(dedupedAttribs); i++ {
156		if reflect.DeepEqual(dedupedAttribs[i], newSPDAttribs) {
157			return i
158		}
159	}
160
161	return -1
162}
163
164func readMemParts(memPartsFilePath string) (memParts, error) {
165	var memParts memParts
166
167	dataBytes, err := ioutil.ReadFile(memPartsFilePath)
168	if err != nil {
169		return memParts, err
170	}
171
172	// Strip comments from json file
173	re := regexp.MustCompile(`(?m)^\s*//.*`)
174	dataBytes = re.ReplaceAll(dataBytes, []byte(""))
175
176	if err := json.Unmarshal(dataBytes, &memParts); err != nil {
177		return memParts, err
178	}
179
180	return memParts, nil
181}
182
183func createSPD(memAttribs interface{}, t memTech) string {
184	var s string
185
186	for i := 0; i < t.getSPDLen(); i++ {
187		var b byte = 0
188		if memAttribs != nil {
189			b = t.getSPDByte(i, memAttribs)
190		}
191
192		if (i+1)%16 == 0 {
193			s += fmt.Sprintf("%02X\n", b)
194		} else {
195			s += fmt.Sprintf("%02X ", b)
196		}
197	}
198
199	return s
200}
201
202func writeSPD(memAttribs interface{}, SPDId int, SPDSetDirName string, t memTech) {
203	s := createSPD(memAttribs, t)
204	SPDFileName := fmt.Sprintf("spd-%d.hex", SPDId)
205	ioutil.WriteFile(filepath.Join(SPDSetDirName, SPDFileName), []byte(s), 0644)
206}
207
208func writeEmptySPD(SPDSetDirName string, t memTech) {
209	s := createSPD(nil, t)
210	SPDFileName := "spd-empty.hex"
211	ioutil.WriteFile(filepath.Join(SPDSetDirName, SPDFileName), []byte(s), 0644)
212}
213
214func getGeneratedString() string {
215	return fmt.Sprintf("# Generated by:\n# %s\n\n", strings.Join(os.Args[0:], " "))
216}
217
218func writeSPDManifest(memPartArray []memPart, SPDSetDirName string) {
219	var s string
220
221	s += getGeneratedString()
222	for i := 0; i < len(memPartArray); i++ {
223		s += fmt.Sprintf("%s,spd-%d.hex\n", memPartArray[i].Name, memPartArray[i].SPDId)
224	}
225
226	ioutil.WriteFile(filepath.Join(SPDSetDirName, SPDManifestFileName), []byte(s), 0644)
227}
228
229func writeSetMap(setMap map[int][]int, SPDDirName string) {
230	var s string
231
232	s += getGeneratedString()
233
234	var setNumbers []int
235	for k, _ := range setMap {
236		setNumbers = append(setNumbers, k)
237	}
238	sort.Ints(setNumbers)
239
240	for _, num := range setNumbers {
241		for _, item := range setMap[num] {
242			s += fmt.Sprintf("%s,set-%d\n", platformNames[item], num)
243		}
244	}
245
246	ioutil.WriteFile(filepath.Join(SPDDirName, PlatformManifestFileName), []byte(s), 0644)
247}
248
249func usage() {
250	fmt.Printf("\nUsage: %s <mem_parts_list_json> <mem_technology>\n\n", os.Args[0])
251	fmt.Printf("   where,\n")
252	fmt.Printf("   mem_parts_list_json = JSON File containing list of memory parts and attributes\n")
253	fmt.Printf("   mem_technology = Memory technology for which to generate SPDs\n")
254	fmt.Printf("                    supported technologies: %v\n\n\n",
255		reflect.ValueOf(memTechMap).MapKeys())
256}
257
258func main() {
259	if len(os.Args) != 3 {
260		usage()
261		log.Fatal("Incorrect number of arguments")
262	}
263
264	var t memTech
265	memPartsFilePath, memTechnology := os.Args[1], os.Args[2]
266
267	t, ok := memTechMap[strings.ToLower(memTechnology)]
268	if !ok {
269		log.Fatal("Unsupported memory technology ", memTechnology)
270	}
271
272	SPDDir, err := filepath.Abs(filepath.Dir(memPartsFilePath))
273	if err != nil {
274		log.Fatal(err)
275	}
276
277	memParts, err := readMemParts(memPartsFilePath)
278	if err != nil {
279		log.Fatal(err)
280	}
281
282	memPartExists := make(map[string]bool)
283	for i := 0; i < len(memParts.MemParts); i++ {
284		if memPartExists[memParts.MemParts[i].Name] {
285			log.Fatalf("%s is duplicated in mem_parts_list_json", memParts.MemParts[i].Name)
286		}
287		memPartExists[memParts.MemParts[i].Name] = true
288
289		if err := t.addNewPart(memParts.MemParts[i].Name, memParts.MemParts[i].Attribs); err != nil {
290			log.Fatal(err)
291		}
292	}
293
294	setMap := t.getSetMap()
295
296	for i := 0; i < len(setMap); i++ {
297		var dedupedAttribs []interface{}
298
299		for j := 0; j < len(memParts.MemParts); j++ {
300			spdAttribs, _ := t.getSPDAttribs(memParts.MemParts[j].Name, i)
301			index := -1
302
303			if index = findIndex(dedupedAttribs, spdAttribs); index == -1 {
304				dedupedAttribs = append(dedupedAttribs, spdAttribs)
305				index = len(dedupedAttribs) - 1
306			}
307
308			memParts.MemParts[j].SPDId = index + 1
309		}
310
311		SPDSetDir := fmt.Sprintf("%s/set-%d", SPDDir, i)
312		os.MkdirAll(SPDSetDir, os.ModePerm)
313
314		for j := 0; j < len(dedupedAttribs); j++ {
315			writeSPD(dedupedAttribs[j], j+1, SPDSetDir, t)
316		}
317
318		writeEmptySPD(SPDSetDir, t)
319
320		writeSPDManifest(memParts.MemParts, SPDSetDir)
321	}
322
323	writeSetMap(setMap, SPDDir)
324}
325