blob: 480727634ee99a6fb86f92c85922cafe91e7f70b [file] [log] [blame] [edit]
// Copyright 2025 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"bytes"
"encoding/binary"
"encoding/json"
"flag"
"fmt"
"log"
"os"
"path"
"unsafe"
"fidl/data/zbi"
)
var flags = struct {
ItemPath string
OutputPath string
DepfilePath string
}{}
func init() {
flag.StringVar(&flags.ItemPath, "item-path", "",
"Path to file containing a JSON representation of the item, including: \n"+
" * `type`(string ZBI_TYPE...)\n"+
" * `extra`(optional string ZBI_KERNEL_DRIVER....)\n"+
" * `contents`(JSON representation of the payload)")
flag.StringVar(&flags.OutputPath, "output", "",
"Path where the item, header and payload will be written to. Item is prefixed with "+
"container header so it may be combined with other ZBI entries.")
flag.StringVar(&flags.DepfilePath, "depfile", "", "Depfile for generated output file.")
}
func usage() {
msg := `Usage: ` + path.Base(os.Args[0]) + ` [flags]
ZBI item generator, used for build time generation of custom items being injected into the ZBI.
JSON representation of objects are used to generate the payload.
Flags:
`
fmt.Fprint(flag.CommandLine.Output(), msg)
flag.PrintDefaults()
}
func ItemHeader(zbiType zbi.Type, zbiExtra uint32, payloadLength uint32) zbi.Header {
return zbi.Header{
Type: zbiType,
Length: payloadLength,
Extra: zbiExtra,
Flags: zbi.FlagsVersion,
Magic: zbi.ItemMagic,
Crc32: zbi.ItemNoCrc32,
}
}
func ContainerHeader(payloadLength uint32) zbi.Header {
return zbi.Header{
Type: zbi.TypeContainer,
Length: payloadLength,
Extra: zbi.ContainerMagic,
Flags: zbi.FlagsVersion,
Magic: zbi.ItemMagic,
Crc32: zbi.ItemNoCrc32,
}
}
// Any ZBI item generator must fulfill this contract.
type ZbiItemGenerator = func(item Item) (Type zbi.Type, Extra uint32, Payload []byte, Error error)
// JSON content struct.
type Item = struct {
Type string `json:"type"`
Extra string `json:"extra"`
Contents json.RawMessage `json:"contents"`
}
func GenerateItem() error {
itemFile, err := os.ReadFile(flags.ItemPath)
if err != nil {
return err
}
var item Item
if err = json.Unmarshal(itemFile, &item); err != nil {
return err
}
var zbiType zbi.Type
var zbiExtra uint32
var payload []byte
// New ZBI items should be added here.
switch item.Type {
case "ZBI_TYPE_KERNEL_DRIVER":
zbiType = zbi.TypeKernelDriver
switch item.Extra {
case "ZBI_KERNEL_DRIVER_ARM_PSCI_CPU_SUSPEND":
var suspendStates []zbi.DcfgArmPsciCpuSuspendState
zbiExtra = uint32(zbi.KernelDriverArmPsciCpuSuspend)
payload, err = GenerateArrayPayload(suspendStates, []byte(item.Contents))
break
default:
return fmt.Errorf("Unsupported Kernel Driver: %s", item.Extra)
}
break
default:
return fmt.Errorf("Unsupported item type: %s", item.Type)
}
if err != nil {
return err
}
payloadLength := uint32(0)
if payload != nil {
payloadLength = uint32(len(payload))
}
itemHeader := ItemHeader(zbiType, zbiExtra, payloadLength)
containerHeader := ContainerHeader(payloadLength + uint32(unsafe.Sizeof(itemHeader)))
// Write file contents.
zbiFile, err := os.Create(flags.OutputPath)
if err != nil {
return err
}
defer zbiFile.Close()
if err := binary.Write(zbiFile, binary.LittleEndian, &containerHeader); err != nil {
return fmt.Errorf("Failed to write container header: %v", err)
}
if err := binary.Write(zbiFile, binary.LittleEndian, &itemHeader); err != nil {
return fmt.Errorf("Failed to write item header: %v", err)
}
if payloadLength > 0 {
if err := binary.Write(zbiFile, binary.LittleEndian, payload); err != nil {
return fmt.Errorf("Failed to write item header: %v", err)
}
}
// Now depfile
depFile, err := os.Create(flags.DepfilePath)
if err != nil {
return fmt.Errorf("Failed to create depfile (%s): %v", flags.DepfilePath, err)
}
defer depFile.Close()
_, err = fmt.Fprintf(depFile, "%s: %s\n", flags.OutputPath, flags.ItemPath)
if err != nil {
return fmt.Errorf("Failed to write depfile (%s) contents: %v", flags.DepfilePath, err)
}
return nil
}
func GenerateArrayPayload[T any](payload []T, jsonContents []byte) ([]byte, error) {
if err := json.Unmarshal(jsonContents, &payload); err != nil {
return nil, err
}
// So we can determine the size of each element.
var element T
elementSize := int(unsafe.Sizeof(element))
payloadSize := elementSize * len(payload)
buffer := bytes.NewBuffer(make([]byte, 0, payloadSize))
for _, payloadItem := range payload {
if err := binary.Write(buffer, binary.LittleEndian, &payloadItem); err != nil {
return nil, fmt.Errorf("Failed to generate binary payload: %v", err)
}
}
return buffer.Bytes(), nil
}
func main() {
flag.Parse()
if err := GenerateItem(); err != nil {
log.Fatalf("Failed to generate item: %v. Flags: %+v", err, flags)
}
}