From 9db795ea69caeafe46d0ec0e8604f723e63b2370 Mon Sep 17 00:00:00 2001 From: Christopher Meis Date: Mon, 11 Sep 2023 15:56:50 +0200 Subject: [PATCH] Add ACPI/AML Generation Signed-off-by: Christopher Meis --- acpi/aml.go | 639 +++++++++++++++++++++++++++++++++++++++++++++++ acpi/aml_test.go | 49 ++++ acpi/const.go | 84 +++++++ acpi/dsdt.go | 31 +++ acpi/fadt.go | 129 ++++++++++ acpi/header.go | 61 +++++ acpi/madt.go | 115 +++++++++ acpi/mcfg.go | 61 +++++ acpi/viot.go | 80 ++++++ acpi/xsdt.go | 56 +++++ 10 files changed, 1305 insertions(+) create mode 100644 acpi/aml.go create mode 100644 acpi/aml_test.go create mode 100644 acpi/const.go create mode 100644 acpi/dsdt.go create mode 100644 acpi/fadt.go create mode 100644 acpi/header.go create mode 100644 acpi/madt.go create mode 100644 acpi/mcfg.go create mode 100644 acpi/viot.go create mode 100644 acpi/xsdt.go diff --git a/acpi/aml.go b/acpi/aml.go new file mode 100644 index 0000000..9a61166 --- /dev/null +++ b/acpi/aml.go @@ -0,0 +1,639 @@ +package acpi + +import ( + "bytes" + "encoding/binary" + "strconv" + "strings" +) + +type AMLOp uint8 + +const ( + OpZero AMLOp = 0x00 + OpOne AMLOp = 0x01 + + OpName AMLOp = 0x08 + OpBytePrefix AMLOp = 0x0A + OpWordPrefix AMLOp = 0x0B + OpDWordPrefix AMLOp = 0x0C + OpString AMLOp = 0x0D + OpQWordPrefix AMLOp = 0x0E + OpScope AMLOp = 0x10 + OpBuffer AMLOp = 0x11 + OpPackage AMLOp = 0x12 + OpVarPackage AMLOp = 0x13 + OpMethod AMLOp = 0x14 + OpDualNamePrefix AMLOp = 0x2E + OpMultiNamePrefix AMLOp = 0x2F + + OpNameCharBase AMLOp = 0x40 + + OpExtPrefix AMLOp = 0x5b + OpMutex AMLOp = 0x01 + OpCreateFile AMLOp = 0x13 + OpAcquire AMLOp = 0x23 + OpRelease AMLOp = 0x27 + OpRegionOp AMLOp = 0x80 + OpFile AMLOp = 0x81 + OpDevice AMLOp = 0x82 + OpPowerSource AMLOp = 0x84 + + OpLocal AMLOp = 0x60 + OpArg AMLOp = 0x68 + OpStore AMLOp = 0x70 + OpDerefof AMLOp = 0x83 + OpNotify AMLOp = 0x86 + OpSizeOf AMLOp = 0x87 + + OpObjectType AMLOp = 0x8E + OpLNot AMLOp = 0x92 + OpLEqual AMLOp = 0x93 + OpLGreater AMLOp = 0x94 + OpLLess AMLOp = 0x95 + OpToBuffer AMLOp = 0x96 + OpToInteger AMLOp = 0x99 + + OpMid AMLOp = 0x9E + OpIf AMLOp = 0xA0 + OpElse AMLOp = 0xA1 + OpWhile AMLOp = 0xA2 + OpReturn AMLOp = 0xA4 + OpOnes AMLOp = 0xFF + + IOPortDesc AMLOp = 0x47 + EndTag AMLOp = 0x79 + Mem32FixedDesc AMLOp = 0x86 + DWordAddressSpaceDesc AMLOp = 0x87 + WordAddressSpaceDesc AMLOp = 0x88 + ExtIRQDesc AMLOp = 0x89 + QWordAddressSpaceDesc AMLOp = 0x8A +) + +type BinaryAMLOp uint8 + +const ( + OpAdd BinaryAMLOp = 0x72 + OpConcat BinaryAMLOp = 0x73 + OpSubstract BinaryAMLOp = 0x74 + OpMultiply BinaryAMLOp = 0x77 + OpShiftLeft BinaryAMLOp = 0x79 + OpShiftRight BinaryAMLOp = 0x7A + OpAND BinaryAMLOp = 0x7B + OpNAND BinaryAMLOp = 0x7C + OpOR BinaryAMLOp = 0x7D + OpNOR BinaryAMLOp = 0x7E + OpXOR BinaryAMLOp = 0x7F + + OpConcatRes BinaryAMLOp = 0x84 + OpMod BinaryAMLOp = 0x85 + OpIndex BinaryAMLOp = 0x88 + OpCreateDWFile BinaryAMLOp = 0x8A + OpCreateQWFile BinaryAMLOp = 0x8F + OpToString BinaryAMLOp = 0x9C +) + +type AML struct { + buf bytes.Buffer +} + +func NewAML() *AML { + return &AML{ + buf: bytes.Buffer{}, + } +} + +func (a *AML) ToBytes() []byte { + return a.buf.Bytes() +} + +func (a *AML) Zero() *AML { + a.buf.WriteByte(byte(OpZero)) + + return a +} + +func (a *AML) One() *AML { + a.buf.WriteByte(byte(OpOne)) + + return a +} + +func (a *AML) Ones() *AML { + a.buf.WriteByte(byte(OpOnes)) + + return a +} + +func (a *AML) Path(str string) *AML { + if strings.HasPrefix(str, "\\") { + a.buf.WriteByte('\\') + + str = strings.Trim(str, "\\") + } + + strs := strings.Split(str, ".") + + for _, substring := range strs { + if len(substring) > 4 { + return nil + } + + a.buf.WriteString(substring) + } + + return a +} + +func (a *AML) Bytes(b byte) *AML { + a.buf.WriteByte(byte(OpBytePrefix)) + a.buf.WriteByte(b) + + return a +} + +func (a *AML) Word(w uint16) *AML { + a.buf.WriteByte(byte(OpWordPrefix)) + + data := make([]byte, 2) + + binary.LittleEndian.PutUint16(data, w) + + a.buf.Write(data) + + return a +} + +func (a *AML) DWord(dw uint32) *AML { + a.buf.WriteByte(byte(OpDWordPrefix)) + + data := make([]byte, 4) + + binary.LittleEndian.PutUint32(data, dw) + + a.buf.Write(data) + + return a +} + +func (a *AML) QWord(qw uint64) *AML { + a.buf.WriteByte(byte(OpQWordPrefix)) + + data := make([]byte, 8) + + binary.LittleEndian.PutUint64(data, qw) + + a.buf.Write(data) + + return a +} + +func (a *AML) Name(path string, inner *AML) *AML { + a.buf.WriteByte(byte(OpName)) + a.Path(path) + a.buf.Write(inner.ToBytes()) + + return a +} + +func (a *AML) EISAName(str string) *AML { + if len(str) != 7 { + return nil + } + + var eisaid uint32 + eisaid |= (uint32(str[0]) - 'A' + 1&0x1F) << 10 + eisaid |= (uint32(str[1]) - 'A' + 1&0x1F) << 5 + eisaid |= (uint32(str[2]) - 'A' + 1&0x1F) + + n1, err := strconv.ParseUint(str[3:], 16, 32) + if err != nil { + return nil + } + + eisaid |= (uint32(n1) << 16) + + data := make([]byte, 4) + + binary.LittleEndian.PutUint32(data, eisaid) + + a.buf.Write(data) + + return a +} + +func (a *AML) String(str string) *AML { + a.buf.WriteByte(byte(OpString)) + + for _, substr := range str { + a.buf.WriteByte(byte(substr)) + } + + a.buf.WriteByte(0x0) + + return a +} + +const ( + pkgLen1 = 63 + pkgLen2 = 4096 + pkgLen3 = 1048573 +) + +func CalcPkgLength(length uint32, includepkg bool) []byte { + var lenlen uint32 + + if length < pkgLen1 { // nolint:gocritic + lenlen = 1 + } else if length < pkgLen2 { + lenlen = 2 + } else if length < pkgLen3 { + lenlen = 3 + } else { + lenlen = 4 + } + + ret := make([]byte, lenlen) + + if includepkg { + length += lenlen + } + + switch lenlen { + case 1: + ret[0] = uint8(length) + case 2: + ret[0] = (uint8(1) << 6) | uint8(length&0xf) + ret[1] = uint8(length >> 4) + case 3: + ret[0] = (uint8(2) << 6) | uint8(length&0xf) + ret[1] = uint8(length >> 4) + ret[2] = uint8(length >> 12) + case 4: + ret[0] = (uint8(3) << 6) | uint8(length&0xf) + ret[1] = uint8(length >> 4) + ret[2] = uint8(length >> 12) + ret[3] = uint8(length >> 20) + } + + return ret +} + +func (a *AML) ResourceTemplate(inner *AML) *AML { + var buf1, buf2 bytes.Buffer + + buf1.Write(inner.ToBytes()) + buf1.WriteByte(byte(EndTag)) + buf1.WriteByte(0x0) + + dlenb := make([]byte, 4) + dlen := uint32(buf1.Len() + len(dlenb)) + + binary.LittleEndian.PutUint32(dlenb, dlen) + + pkglen := CalcPkgLength(dlen+4, true) + + buf2.Write(pkglen) + buf2.Write(dlenb) + buf2.Write(buf1.Bytes()) + a.buf.WriteByte(byte(OpBuffer)) + a.buf.Write(buf2.Bytes()) + + return a +} + +func (a *AML) Memory32Fixed(base, length uint32, rw bool) *AML { + readwrite := uint8(0) + + a.buf.WriteByte(byte(Mem32FixedDesc)) + a.buf.WriteByte(0x09) + a.buf.WriteByte(0x0) + + if rw { + readwrite = 1 + } + + a.buf.WriteByte(readwrite) + a.DWord(base) + a.DWord(length) + + return a +} + +func (a *AML) IO(min, max uint16, align, length uint8) *AML { + a.buf.WriteByte(byte(IOPortDesc)) + a.buf.WriteByte(0x1) + a.Word(min) + a.Word(max) + a.buf.WriteByte(align) + a.buf.WriteByte(length) + + return a +} + +func (a *AML) Interrupt(consumer, edgetrig, activelow, shared bool, number uint32) *AML { + flags := uint8(0) + + if consumer { + flags = 0x1 + } + + if edgetrig { + flags |= 1 << 1 + } + + if activelow { + flags |= 1 << 2 + } + + if shared { + flags |= 1 << 3 + } + + a.buf.WriteByte(byte(ExtIRQDesc)) + a.Word(0x6) + a.buf.WriteByte(flags) + a.buf.WriteByte(1) + a.DWord(number) + + return a +} + +func (a *AML) Device(path string, children *AML) *AML { + aml := NewAML() + aml.Path(path) + + aml.buf.Write(children.ToBytes()) + + amllen := uint32(aml.buf.Len()) + + pkglen := CalcPkgLength(amllen, true) + + a.buf.WriteByte(byte(OpExtPrefix)) + a.buf.WriteByte(byte(OpDevice)) + a.buf.Write(pkglen) + a.buf.Write(aml.ToBytes()) + + return a +} + +func (a *AML) Method(path string, args uint8, serialize bool, children *AML) *AML { + amlbuf := NewAML() + + amlbuf.Path(path) + + flags := args & 0x7 + + if serialize { + flags |= 1 << 3 + } + + amlbuf.buf.WriteByte(flags) + amlbuf.buf.Write(children.ToBytes()) + + datlen := uint32(amlbuf.buf.Len()) + + pkglen := CalcPkgLength(datlen, true) + + a.buf.WriteByte(byte(OpMethod)) + a.buf.Write(pkglen) + a.buf.Write(amlbuf.ToBytes()) + + return a +} + +const ( + FieldAccessTypeAny uint8 = 0 + iota + FieldAccessTypeByte + FieldAccessTypeWord + FieldAccessTypeDWord + FieldAccessTypeQWord + FieldAccessTypeBuffer + + FieldUpdateRulePreserve uint8 = 0 + FieldUpdateRuleWriteAsOnes uint8 = 1 + FieldUpdateRuleWriteAsZeros uint8 = 2 + + FieldEntryTypeNamed uint8 = 0 + FieldEntryTypeReserved uint8 = 1 +) + +type FieldEntry interface { + Name() string + Length() uint32 +} + +type FieldEntryNamed struct { + name string + length uint32 +} + +func NewFieldEntryNamed(name string, l uint32) FieldEntryNamed { + return FieldEntryNamed{name: name, length: l} +} + +func (f FieldEntryNamed) Name() string { + return f.name +} + +func (f FieldEntryNamed) Length() uint32 { + return f.length +} + +type FieldEntryReserved struct { + length uint32 +} + +func NewFieldEntryReserved(l uint32) FieldEntryReserved { + return FieldEntryReserved{length: l} +} + +func (f FieldEntryReserved) Name() string { + return "reserved" // Give it a undesirable name just in case. +} + +func (f FieldEntryReserved) Length() uint32 { + return f.length +} + +func (a *AML) Field(path string, accessType uint8, lockrule bool, updaterule uint8, entries ...FieldEntry) *AML { + amlbuf := NewAML() + + amlbuf.Path(path) + + flags := accessType | updaterule<<5 + + if lockrule { + flags |= 1 << 4 + } + + amlbuf.buf.WriteByte(flags) + + for _, entry := range entries { + switch e := entry.(type) { + case *FieldEntryNamed: + amlbuf.buf.Write([]byte(e.Name())) + amlbuf.buf.Write(CalcPkgLength(e.Length(), false)) + case *FieldEntryReserved: + amlbuf.buf.WriteByte(0x0) + amlbuf.buf.Write(CalcPkgLength(e.Length(), false)) + } + } + + pkglen := CalcPkgLength(uint32(amlbuf.buf.Len()), true) + + a.buf.WriteByte(byte(OpExtPrefix)) + a.buf.WriteByte(byte(OpFile)) + a.buf.Write(pkglen) + a.buf.Write(amlbuf.ToBytes()) + + return a +} + +const ( + OpRegionSpaceSysMem uint8 = 0 + iota + OpRegionSpaceSysIO + OpRegionSpacePCIConf + OpRegionSpaceEmbControl + OpRegionSpaceSMBus + OpRegionSpaceSysCMOS + OpRegionSpacePCIBarTarget + OpRegionSpaceIPMI + OpRegionSpaceGPIO + OpRegionSpaceGenSerialBus +) + +func (a *AML) OpRegion(path string, space uint8, offset *AML, length *AML) *AML { + a.buf.WriteByte(byte(OpExtPrefix)) + a.buf.WriteByte(byte(OpRegionOp)) + a.Path(path) + a.buf.WriteByte(space) + a.buf.Write(offset.ToBytes()) + a.buf.Write(length.ToBytes()) + + return a +} + +func (a *AML) Store(name *AML, value *AML) *AML { + a.buf.WriteByte(byte(OpStore)) + a.buf.Write(name.ToBytes()) + a.buf.Write(value.ToBytes()) + + return a +} + +func (a *AML) Mutex(path string, syncLevel uint8) *AML { + a.buf.WriteByte(byte(OpExtPrefix)) + a.buf.WriteByte(byte(OpMutex)) + a.Path(path) + a.buf.WriteByte(syncLevel) + + return a +} + +func (a *AML) Acquire(path string, timeout uint16) *AML { + a.buf.WriteByte(byte(OpExtPrefix)) + a.buf.WriteByte(byte(OpAcquire)) + a.Path(path) + a.Word(timeout) + + return a +} + +func (a *AML) Release(path string) *AML { + a.buf.WriteByte(byte(OpExtPrefix)) + a.buf.WriteByte(byte(OpRelease)) + a.Path(path) + + return a +} + +func (a *AML) MethodCall(path string, args *AML) *AML { + a.Path(path) + a.buf.Write(args.ToBytes()) + + return a +} + +func (a *AML) Return(op AML) *AML { + a.buf.WriteByte(byte(OpReturn)) + a.buf.Write(op.ToBytes()) + + return a +} + +func (a *AML) BinaryOp(op BinaryAMLOp, operandA *AML, operandB *AML, target *AML) *AML { + a.buf.WriteByte(byte(op)) + a.buf.Write(operandA.ToBytes()) + a.buf.Write(operandB.ToBytes()) + a.buf.Write(target.ToBytes()) + + return a +} + +const ( + TypeAddressSpaceMemory uint8 = 0 + TypeAddressSpaceIO uint8 = 1 + TypeAddressSpaceBusnumber uint8 = 2 + + TFlagNotCachable uint8 = 0 + TFlagReadWrite uint8 = 1 + TFlagCachable uint8 = 2 + TFlagWriteCombining uint8 = 3 + TFlagPrefetchable uint8 = 4 +) + +func (a *AML) AddressSpace64(addrtype uint8, min, max uint64, tflags uint8, translation []byte) *AML { + a.buf.WriteByte(byte(QWordAddressSpaceDesc)) + + length := 43 + + blen := make([]byte, 2) + + binary.LittleEndian.PutUint16(blen, uint16(length)) + a.buf.Write(blen) + + a.buf.WriteByte(addrtype) + + genflags := uint8(1<<2 | 1<<3) + + a.buf.WriteByte(genflags) + + a.buf.WriteByte(tflags) + + a.QWord(0x0) + a.QWord(min) + a.QWord(max) + a.QWord(0x0) + a.QWord(max - min + 1) + + return a +} + +func (a *AML) BufferTerm() *AML { return a } + +func (a *AML) BufferData() *AML { return a } + +func (a *AML) Package() *AML { return a } + +func (a *AML) If() *AML { return a } + +func (a *AML) Else() *AML { return a } + +func (a *AML) Arg(arg uint8) *AML { + a.buf.WriteByte(uint8(OpArg) + arg) + + return a +} + +func (a *AML) Local() *AML { return a } + +func (a *AML) Scope() *AML { return a } + +func (a *AML) Notify() *AML { return a } + +func (a *AML) While() *AML { return a } + +func (a *AML) CreateField() *AML { return a } + +func (a *AML) Mid() *AML { return a } diff --git a/acpi/aml_test.go b/acpi/aml_test.go new file mode 100644 index 0000000..a8f43f7 --- /dev/null +++ b/acpi/aml_test.go @@ -0,0 +1,49 @@ +package acpi_test + +import ( + "bytes" + "testing" + + "github.com/bobuhiro11/gokvm/acpi" +) + +func TestCalcPkgLength(t *testing.T) { + t.Parallel() + + for _, tt := range []struct { + name string + size uint32 + exp []byte + err error + }{ + { + name: "1ByteSize", + size: 62, + exp: []byte{63}, + }, + { + name: "2ByteSize", + size: 64, + exp: []byte{1<<6 | (66 & 0xf), 66 >> 4}, + }, + { + name: "3ByteSize", + size: 4096, + exp: []byte{2<<6 | (4099 & 0xf), 0, 1}, + }, + { + name: "4ByteSize", + size: 536870912, + exp: []byte{3<<6 | (536870916 & 0xf), 0, 0, 0}, + }, + } { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + val := acpi.CalcPkgLength(tt.size, true) + if !bytes.Equal(val, tt.exp) { + t.Fatalf("byte not match. Have: 0x%x, want: 0x%x", val, tt.exp) + } + }) + } +} diff --git a/acpi/const.go b/acpi/const.go new file mode 100644 index 0000000..739e5ba --- /dev/null +++ b/acpi/const.go @@ -0,0 +1,84 @@ +package acpi + +type Signature string + +func (s Signature) ToBytes() [4]byte { + var ret [4]byte + + for i := 0; i < 3; i++ { + ret[i] = s[i] + } + + return ret +} + +const ( + SigAEST Signature = "AEST" + SigAPIC Signature = "APIC" + SigBDAT Signature = "BDAT" + SigBERT Signature = "BERT" + SigBGRT Signature = "BGRT" + SigBOOT Signature = "BOOT" + SigCDIT Signature = "CDIT" + SigCEDT Signature = "CEDT" + SigCPEP Signature = "CPEP" + SigCRAT Signature = "CRAT" + SigCSRT Signature = "CSRT" + SigDBGP Signature = "DBGP" + SigDBG2 Signature = "DBG2" + SigDMAR Signature = "DMAR" + SigDRTM Signature = "DRTM" + SigDSDT Signature = "DSDT" + SigECDT Signature = "ECDT" + SigETDT Signature = "ETDT" + SigEINJ Signature = "EINJ" + SigERST Signature = "ERST" + SigFACP Signature = "FACP" + SigFACS Signature = "FACS" + SigFPDT Signature = "FPDT" + SigGTDT Signature = "GTDT" + SigHPET Signature = "HPET" + SigHEST Signature = "HEST" + SigIBFT Signature = "IBFT" + SigIORT Signature = "IORT" + SigIVRS Signature = "IVRS" + SigLPIT Signature = "LPIT" + SigMCFG Signature = "MCFG" + SigMCHI Signature = "MCHI" + SigMPAM Signature = "MPAM" + SigMSCT Signature = "MSCT" + SigMSDM Signature = "MSDM" + SigMPST Signature = "MPST" + SigNFIT Signature = "NFIT" + SigOEMx Signature = "OEMx" + SigPCCT Signature = "PCCT" + SigPHAT Signature = "PHAT" + SigPMTT Signature = "PMTT" + SigPRMT Signature = "PRMT" + SigPSDT Signature = "PSDT" + SigRASF Signature = "RASF" + SigRGRT Signature = "RGRT" + SigRSDT Signature = "RSDT" + SigSBST Signature = "SBST" + SigSDEI Signature = "SDEI" + SigSDEV Signature = "SDEV" + SigSLIC Signature = "SLIC" + SigSLIT Signature = "SLIT" + SigSPCR Signature = "SPCR" + SigSPMI Signature = "SPMI" + SigSRAT Signature = "SRAT" + SigSSDT Signature = "SSDT" + SigSTAO Signature = "STAO" + SigSVKL Signature = "SVKL" + SigTCPA Signature = "TCPA" + SigTPM2 Signature = "TPM2" + SigUEFI Signature = "UEFI" + SigVIOT Signature = "VIOT" + SigWAET Signature = "WAET" + SigWDAT Signature = "WDAT" + SigWDRT Signature = "WDRT" + SigWDBT Signature = "WDBT" + SigWSMT Signature = "WSMT" + SigXENV Signature = "XENV" + SigXSDT Signature = "XSDT" +) diff --git a/acpi/dsdt.go b/acpi/dsdt.go new file mode 100644 index 0000000..9e0a5ee --- /dev/null +++ b/acpi/dsdt.go @@ -0,0 +1,31 @@ +package acpi + +import ( + "bytes" + "encoding/binary" +) + +type DSDT struct { + Header + *AML +} + +func NewDSDT(oemid, oemtableid string) DSDT { + h := newHeader(SigDSDT, 36, 6, oemid, oemtableid) + a := NewAML() + + return DSDT{h, a} +} + +func (d *DSDT) ToBytes() ([]byte, error) { + var buf bytes.Buffer + if err := binary.Write(&buf, binary.LittleEndian, d.Header); err != nil { + return nil, err + } + + if _, err := buf.Write(d.AML.ToBytes()); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} diff --git a/acpi/fadt.go b/acpi/fadt.go new file mode 100644 index 0000000..fcfeb6d --- /dev/null +++ b/acpi/fadt.go @@ -0,0 +1,129 @@ +package acpi + +import ( + "bytes" + "encoding/binary" +) + +type FADTFeatureFlag uint32 + +const ( + WBINVD FADTFeatureFlag = 1<<0 + iota + WBINVDFlush + ProcC1 + PLvL2Up + PwrButton + SleepButton + FixRTC + RTCS4 + TmrValExt + DCKCap + ResetRegSup + SealedCase + Headless + CPUSwSleep + PCIExpWak + UsePlatformClock + S4RTCSTSValid + RemotePowerOnCapable + ForceAPICCluterModel + ForceAPICPhysicalDestMode + HwReducedACPI + LowPowerS0IdleCapable +) + +type FADT struct { + Header + FirmwareCTRL uint32 + DSDTAddr uint32 + _ uint8 + PrefPMProfile uint8 + SCIInt uint16 + SMICmd uint32 + ACPIEnable uint8 + ACPIDisable uint8 + S4BIOSReq uint8 + PStateCnt uint8 + PM1aEvtBlk uint32 + PM1bEvtBlk uint32 + PM1aCntBlk uint32 + PM1bCntBlk uint32 + PM2CntBlk uint32 + PMTmrBlk uint32 + GPE0Blk uint32 + GPE1Blk uint32 + PM1EvtLen uint8 + PM1CntLen uint8 + PM2CntLen uint8 + PMTmrLen uint8 + GPE0BlkLen uint8 + GPE1BlkLen uint8 + GPE1Base uint8 + CstCnt uint8 + PLvL2Lat uint16 + PLvL3Lat uint16 + FlushSize uint16 + FlushStride uint16 + DutyOffset uint8 + DutyWidth uint8 + DayALRM uint8 + MonALRM uint8 + Century uint8 + IAPCBootArch uint16 + _ uint8 + FADTFeatureFlag + ResetReg [12]uint8 + ResetValue uint8 + ARMBootArch uint16 + MinorVersion uint8 + XFirmwareCntl uint64 + XDSDT uint64 + XPM1aEvtBlk [12]uint8 + XPM1bEvtBlk [12]uint8 + XPM1aCntBlk [12]uint8 + XPM1bCntBlk [12]uint8 + XPM2CntBlk [12]uint8 + XPMTmrBlk [12]uint8 + XGPE0Blk [12]uint8 + XGPE1Blk [12]uint8 + SleepCtlReg [12]uint8 + SleepStatReg [12]uint8 + HyperVendorID [8]uint8 +} + +func NewFADT(oemid, oemtableid, creatorid string) FADT { + h := newHeader(SigFACP, 276, 6, oemid, oemtableid) + + return FADT{ + Header: h, + } +} + +func (f *FADT) ToBytes() ([]byte, error) { + var buf bytes.Buffer + + if err := binary.Write(&buf, binary.LittleEndian, f); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func (f *FADT) Checksum() error { + f.Header.Checksum = 0 + + data, err := f.ToBytes() + if err != nil { + return err + } + + cks := uint8(0) + + for _, b := range data { + cks += b + } + + f.Header.Checksum = cks + + return nil +} diff --git a/acpi/header.go b/acpi/header.go new file mode 100644 index 0000000..b586b79 --- /dev/null +++ b/acpi/header.go @@ -0,0 +1,61 @@ +package acpi + +type Header struct { + Signature [4]byte + Length uint32 + Rev uint8 + Checksum uint8 + OEMId [6]byte + OEMTableID [8]byte + OEMRev uint32 + CreatorID [4]byte + CreatorRev uint32 +} + +func convertOEMID(oemID string) [6]byte { + var id [6]byte + + for i := 0; i < 6; i++ { + id[i] = oemID[i] + } + + return id +} + +func convertOEMTableID(oemTableID string) [8]byte { + var id [8]byte + + for i := 0; i < 8; i++ { + id[i] = oemTableID[i] + } + + return id +} + +func convertCreatorID(creatorID string) [4]byte { + var id [4]byte + + for i := 0; i < 4; i++ { + id[i] = creatorID[i] + } + + return id +} + +func newHeader(sig Signature, length uint32, rev uint8, oemID, oemTableID string) Header { + creatorID := "GACT" // Go ACPI Tables. + + oid := convertOEMID(oemID) + otid := convertOEMTableID(oemTableID) + cid := convertCreatorID(creatorID) + + return Header{ + Signature: sig.ToBytes(), + Length: length, + Rev: rev, + OEMId: oid, + OEMTableID: otid, + CreatorID: cid, + CreatorRev: 1, + } +} diff --git a/acpi/madt.go b/acpi/madt.go new file mode 100644 index 0000000..e2ab205 --- /dev/null +++ b/acpi/madt.go @@ -0,0 +1,115 @@ +package acpi + +import ( + "bytes" + "encoding/binary" +) + +const ( + TypeLocalAPIC uint8 = 0 + iota + TypeIOAPIC + TypeInterruptSourceOverride +) + +type APIC interface { + Len() uint8 + ToBytes() ([]byte, error) +} + +type LocalAPIC struct { + Type uint8 + Length uint8 + ProcessorID uint8 + APICId uint8 + Flags uint32 +} + +func (l *LocalAPIC) Len() uint8 { + return l.Length +} + +func (l *LocalAPIC) ToBytes() ([]byte, error) { + var buf bytes.Buffer + + if err := binary.Write(&buf, binary.LittleEndian, l); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type IOAPIC struct { + Type uint8 + Length uint8 + IOAPICID uint8 + _ uint8 + APICAddress uint32 + GSIBase uint32 +} + +func (i *IOAPIC) Len() uint8 { + return i.Length +} + +func (i *IOAPIC) ToBytes() ([]byte, error) { + var buf bytes.Buffer + + if err := binary.Write(&buf, binary.LittleEndian, i); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type InterruptSourceOverride struct { + Type uint8 + Length uint8 + Bus uint8 + Source uint8 + GSI uint32 + Flags uint16 +} + +func (i *InterruptSourceOverride) Len() uint8 { + return i.Length +} + +func (i *InterruptSourceOverride) ToBytes() ([]byte, error) { + var buf bytes.Buffer + + if err := binary.Write(&buf, binary.LittleEndian, i); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type MADT struct { + Header + APICS []APIC +} + +func (m *MADT) AddAPIC(apic APIC) { + m.APICS = append(m.APICS, apic) +} + +func (m *MADT) ToBytes() ([]byte, error) { + var buf bytes.Buffer + + if err := binary.Write(&buf, binary.LittleEndian, m.Header); err != nil { + return nil, err + } + + for _, apic := range m.APICS { + data, err := apic.ToBytes() + if err != nil { + return nil, err + } + + if _, err := buf.Write(data); err != nil { + return nil, err + } + } + + return buf.Bytes(), nil +} diff --git a/acpi/mcfg.go b/acpi/mcfg.go new file mode 100644 index 0000000..da11421 --- /dev/null +++ b/acpi/mcfg.go @@ -0,0 +1,61 @@ +package acpi + +import ( + "bytes" + "encoding/binary" +) + +type PCISegment struct { + BaseAddress uint64 + Segment uint16 + Start uint8 + End uint8 + _ uint32 +} + +func (p *PCISegment) ToBytes() ([]byte, error) { + var buf bytes.Buffer + + if err := binary.Write(&buf, binary.LittleEndian, p); err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type MCFG struct { + Header + _ [8]byte + Segments []PCISegment +} + +func NewMCFG(oemid, oemtableid, creatorid string) MCFG { + h := newHeader(SigMCFG, 36, 1, oemid, oemtableid) + + return MCFG{Header: h} +} + +func (m *MCFG) AddSegment(seg PCISegment) { + m.Segments = append(m.Segments, seg) +} + +func (m *MCFG) ToBytes() ([]byte, error) { + var buf bytes.Buffer + + if err := binary.Write(&buf, binary.LittleEndian, m.Header); err != nil { + return nil, err + } + + for _, seg := range m.Segments { + data, err := seg.ToBytes() + if err != nil { + return nil, err + } + + if _, err := buf.Write(data); err != nil { + return nil, err + } + } + + return buf.Bytes(), nil +} diff --git a/acpi/viot.go b/acpi/viot.go new file mode 100644 index 0000000..c943aa5 --- /dev/null +++ b/acpi/viot.go @@ -0,0 +1,80 @@ +package acpi + +import ( + "bytes" + "encoding/binary" +) + +type VIOTNode interface { + ToBytes() ([]byte, error) +} + +type ViotVirtualPCINode struct { + Type uint8 + _ uint8 + Length uint16 + PCISegment uint16 + PCIBDFNumber uint16 + _ uint64 +} + +func (v *ViotVirtualPCINode) ToBytes() ([]byte, error) { + var buf bytes.Buffer + + return buf.Bytes(), nil +} + +type ViotPCIRangeNode struct { + Type uint8 + _ uint8 + Length uint16 + EndpointStart uint32 + PCISegmentStart uint16 + PCISegmentEnd uint16 + PCIBDFStart uint16 + PCIBDFEnd uint16 + OutputNode uint16 + _ uint64 +} + +func (v *ViotPCIRangeNode) ToBytes() ([]byte, error) { + var buf bytes.Buffer + + return buf.Bytes(), nil +} + +type VIOT struct { + Header + Nodes []VIOTNode +} + +func NewVIOT(oemid, oemtableid, creatorid string) VIOT { + h := newHeader(SigVIOT, 36, 1, oemid, oemtableid) + + return VIOT{Header: h} +} + +func (v *VIOT) AddNode(node VIOTNode) { + v.Nodes = append(v.Nodes, node) +} + +func (v *VIOT) ToBytes() ([]byte, error) { + var buf bytes.Buffer + + if err := binary.Write(&buf, binary.LittleEndian, v.Header); err != nil { + return nil, err + } + + for _, node := range v.Nodes { + data, err := node.ToBytes() + if err != nil { + return nil, err + } + + if _, err := buf.Write(data); err != nil { + return nil, err + } + } + + return buf.Bytes(), nil +} diff --git a/acpi/xsdt.go b/acpi/xsdt.go new file mode 100644 index 0000000..20a9df7 --- /dev/null +++ b/acpi/xsdt.go @@ -0,0 +1,56 @@ +package acpi + +import ( + "bytes" + "encoding/binary" +) + +type XSDT struct { + Header + Entries []uint64 +} + +func NewXSDT(oemid, oemtableid, creatorid string) XSDT { + h := newHeader(SigXSDT, 36, 1, oemid, oemtableid) + + return XSDT{Header: h} +} + +func (x *XSDT) ToBytes() ([]byte, error) { + var buf bytes.Buffer + + if err := binary.Write(&buf, binary.LittleEndian, x.Header); err != nil { + return nil, err + } + + for _, addr := range x.Entries { + if err := binary.Write(&buf, binary.LittleEndian, addr); err != nil { + return nil, err + } + } + + return buf.Bytes(), nil +} + +func (x *XSDT) AddEntry(entry uint64) { + x.Entries = append(x.Entries, entry) +} + +func (x *XSDT) Checksum() error { + x.Header.Checksum = 0 + + data, err := x.ToBytes() + if err != nil { + return err + } + + cks := uint8(0) + + for _, b := range data { + cks += b + } + + x.Header.Checksum = cks + + return nil +}