1 # bend-osdev-acpi, example.bend: AcpiExample, a small x86 machine's tables 2 # built with this package's writers, for the tests and as a guide: an RSDP 3 # leading to an XSDT (and an RSDT), an FADT, FACS, MADT, HPET, MCFG, SRAT, 4 # SLIT, BGRT, an SSDT, and a DSDT in AML that has a PCI host bridge with 5 # _HID, _CID, _CRS and _PRT, an LPC bridge whose PIRQ routing registers are 6 # a PCI_Config field, a PCI interrupt link device whose _STA and _CRS read 7 # them (the CreateWordField pattern), an embedded controller with an 8 # EmbeddedControl region and a query method that sends a Notify, a thermal 9 # zone, a power button and a processor device. Its shape follows the 10 # examples of ACPI 6.5, sections 6.2.13 (_PRT), 6.4 (resource templates) 11 # and 11 (thermal zones). 12 import Base 13 import bend-osdev-word64@0.1.0.0/word64.bend as W 14 import bend-osdev-bytes@0.1.0.0/bytes.bend as B 15 import ./header.bend as H 16 import ./rsdp.bend as P 17 import ./rsdt.bend as S 18 import ./fadt.bend as F 19 import ./facs.bend as FS 20 import ./gas.bend as G 21 import ./madt.bend as M 22 import ./hpet.bend as Hp 23 import ./mcfg.bend as Mc 24 import ./srat.bend as Sr 25 import ./slit.bend as Sl 26 import ./bgrt.bend as Bg 27 import ./dsdt.bend as D 28 import ./acpi.bend as A 29 import ./aml_term.bend as T 30 import ./aml_resource.bend as R 31 import ./aml_host.bend as Hs 32 33 def ex.hdr(s: String, r: U32) -> H.AcpiHeader: 34 H.AcpiHeader.make(B.Octets.of_string(s), r, B.Octets.of_string("BENDOS"), B.Octets.of_string("EXAMPLE")) 35 36 def ex.i(v: U32) -> T.AmlTerm: 37 T.AmlT.int32(v) 38 39 # Physical addresses 40 def AcpiExample.rsdp_address() -> W.W64: 41 W.W64.from_u32(917520) 42 43 def AcpiExample.base() -> U32: 44 1048576 45 46 def ex.at(off: U32) -> U32: 47 U32.add(AcpiExample.base(), off) 48 49 def ex.at64(+off: U32) -> W.W64: 50 W.W64.from_u32(ex.at(off)) 51 52 # the offsets of the tables from the base 53 def ex.xsdt() -> U32: 54 0 55 56 def ex.rsdt() -> U32: 57 256 58 59 def ex.fadt() -> U32: 60 512 61 62 def ex.facs() -> U32: 63 1024 64 65 def ex.madt() -> U32: 66 1280 67 68 def ex.hpet() -> U32: 69 1536 70 71 def ex.mcfg() -> U32: 72 1664 73 74 def ex.srat() -> U32: 75 1792 76 77 def ex.slit() -> U32: 78 2048 79 80 def ex.bgrt() -> U32: 81 2176 82 83 def ex.ssdt() -> U32: 84 2304 85 86 def ex.dsdt() -> U32: 87 3072 88 89 # The DSDT 90 91 def ex.lnk.crs() -> List<&2, T.AmlTerm>: 92 [T.AmlT.name("RTLA", T.AmlT.buf(R.AmlResource.template([R.AmlRIrq{0, 24}]))), 93 T.AmlT.create_field(139, T.AmlT.nm("RTLA"), T.AmlT.one(), "IRQ0"), 94 T.AmlT.store(T.AmlT.zero(), T.AmlT.nm("IRQ0")), 95 T.AmlT.shift_left(T.AmlT.one(), T.AmlT.and(T.AmlT.nm("^^PCI0.LPCB.PIRA"), ex.i(15), T.AmlT.none()), T.AmlT.nm("IRQ0")), 96 T.AmlT.ret(T.AmlT.nm("RTLA"))] 97 98 def ex.lnka() -> T.AmlTerm: 99 T.AmlT.device("LNKA", [ 100 T.AmlT.name("_HID", T.AmlT.eisaid("PNP0C0F")), 101 T.AmlT.name("_UID", T.AmlT.one()), 102 T.AmlT.method("_STA", 0n, False{}, [T.AmlT.if_else(T.AmlT.and(T.AmlT.nm("^^PCI0.LPCB.PIRA"), ex.i(128), T.AmlT.none()), [T.AmlT.ret(ex.i(9))], [T.AmlT.ret(ex.i(11))])]), 103 T.AmlT.method("_CRS", 0n, True{}, ex.lnk.crs())]) 104 105 def ex.pci.crs() -> B.Octets: 106 R.AmlResource.template([ 107 R.AmlRAddr{16, 2, 12, 0, R.AmlRange{W.W64.zero(), W.W64.zero(), W.W64.from_u32(255), W.W64.zero(), W.W64.from_u32(256)}}, 108 R.AmlRIo{1, 3320, 3320, 1, 8}, 109 R.AmlRAddr{16, 1, 12, 3, R.AmlRange{W.W64.zero(), W.W64.zero(), W.W64.from_u32(3319), W.W64.zero(), W.W64.from_u32(3320)}}, 110 R.AmlRAddr{32, 0, 12, 1, R.AmlRange{W.W64.zero(), W.W64.from_u32(2147483648), W.W64.from_u32(4273995775), W.W64.zero(), W.W64.from_u32(2126512128)}}]) 111 112 def ex.prt() -> T.AmlTerm: 113 T.AmlT.package([ 114 T.AmlT.package([ex.i(131071), T.AmlT.zero(), T.AmlT.nm("^LNKA"), T.AmlT.zero()]), 115 T.AmlT.package([ex.i(131071), T.AmlT.one(), T.AmlT.zero(), ex.i(17)]), 116 T.AmlT.package([ex.i(2031615), T.AmlT.zero(), T.AmlT.zero(), ex.i(16)])]) 117 118 def ex.ec() -> T.AmlTerm: 119 T.AmlT.device("EC0_", [ 120 T.AmlT.name("_HID", T.AmlT.eisaid("PNP0C09")), 121 T.AmlT.name("_CRS", T.AmlT.buf(R.AmlResource.template([R.AmlRIo{1, 98, 98, 0, 1}, R.AmlRIo{1, 102, 102, 0, 1}]))), 122 T.AmlT.name("_GPE", ex.i(23)), 123 T.AmlT.region("ECOR", 3, T.AmlT.zero(), ex.i(256)), 124 T.AmlT.field("ECOR", T.AmlT.field_flags(1, True{}, 0), [T.AmlFReserved{128}, T.AmlT.fnamed("TMP0", 8), T.AmlT.fnamed("FAN0", 1), T.AmlT.fnamed("LID0", 1)]), 125 T.AmlT.method("_Q01", 0n, False{}, [T.AmlT.notify(T.AmlT.nm("\\_TZ.TZ00"), ex.i(128))])]) 126 127 def ex.lpcb() -> T.AmlTerm: 128 T.AmlT.device("LPCB", [ 129 T.AmlT.name("_ADR", ex.i(2031616)), 130 T.AmlT.region("LPCR", 2, ex.i(96), ex.i(4)), 131 T.AmlT.field("LPCR", T.AmlT.field_flags(1, False{}, 0), [T.AmlT.fnamed("PIRA", 8), T.AmlT.fnamed("PIRB", 8), T.AmlT.fnamed("PIRC", 8), T.AmlT.fnamed("PIRD", 8)]), 132 ex.ec()]) 133 134 def ex.pci0() -> T.AmlTerm: 135 T.AmlT.device("PCI0", [ 136 T.AmlT.name("_HID", T.AmlT.eisaid("PNP0A08")), 137 T.AmlT.name("_CID", T.AmlT.eisaid("PNP0A03")), 138 T.AmlT.name("_ADR", T.AmlT.zero()), 139 T.AmlT.name("_BBN", T.AmlT.zero()), 140 T.AmlT.name("_UID", T.AmlT.zero()), 141 T.AmlT.name("_CRS", T.AmlT.buf(ex.pci.crs())), 142 T.AmlT.name("_PRT", ex.prt()), 143 ex.lpcb()]) 144 145 def ex.osys() -> T.AmlTerm: 146 T.AmlT.method("OSYS", 0n, False{}, [ 147 T.AmlT.store(ex.i(2000), T.AmlT.local(0)), 148 T.AmlT.if_(T.AmlT.call("\\_OSI", [T.AmlT.str("Windows 2015")]), [T.AmlT.store(ex.i(2015), T.AmlT.local(0))]), 149 T.AmlT.if_(T.AmlT.call("\\_OSI", [T.AmlT.str("Linux")]), [T.AmlT.store(ex.i(1), T.AmlT.local(0))]), 150 T.AmlT.ret(T.AmlT.local(0))]) 151 152 # the DSDT's terms 153 def AcpiExample.dsdt_terms() -> List<&2, T.AmlTerm>: 154 [T.AmlT.scope("\\_SB", [ 155 ex.pci0(), 156 ex.lnka(), 157 T.AmlT.device("PWRB", [T.AmlT.name("_HID", T.AmlT.eisaid("PNP0C0C"))]), 158 T.AmlT.device("CPU0", [T.AmlT.name("_HID", T.AmlT.str("ACPI0007")), T.AmlT.name("_UID", T.AmlT.zero())]), 159 ex.osys(), 160 T.AmlT.method("SUM_", 1n, False{}, [ 161 T.AmlT.store(T.AmlT.zero(), T.AmlT.local(0)), 162 T.AmlT.store(T.AmlT.zero(), T.AmlT.local(1)), 163 T.AmlT.while_(T.AmlT.lless_equal(T.AmlT.local(1), T.AmlT.arg(0)), [ 164 T.AmlT.add(T.AmlT.local(0), T.AmlT.local(1), T.AmlT.local(0)), 165 T.AmlT.increment(T.AmlT.local(1))]), 166 T.AmlT.ret(T.AmlT.local(0))]), 167 T.AmlT.method("SPIN", 0n, False{}, [T.AmlT.while_(T.AmlT.one(), [T.AmlT.noop()])])]), 168 T.AmlT.scope("\\_TZ", [ 169 T.AmlT.thermal_zone("TZ00", [ 170 T.AmlT.method("_TMP", 0n, False{}, [T.AmlT.ret(T.AmlT.add(T.AmlT.multiply(T.AmlT.nm("\\_SB.PCI0.LPCB.EC0.TMP0"), ex.i(10), T.AmlT.none()), ex.i(2732), T.AmlT.none()))]), 171 T.AmlT.name("_CRT", ex.i(3732))])])] 172 173 def AcpiExample.dsdt_aml() -> B.Octets: 174 T.AmlT.encode(AcpiExample.dsdt_terms()) 175 176 def AcpiExample.dsdt() -> D.AcpiAml: 177 D.AcpiAml{ex.hdr("DSDT", 2), AcpiExample.dsdt_aml()} 178 179 # an SSDT that adds a device to a scope the DSDT defines 180 def AcpiExample.ssdt() -> D.AcpiAml: 181 D.AcpiAml{ex.hdr("SSDT", 2), T.AmlT.encode([ 182 T.AmlT.external("\\_SB.PCI0", 6, 0), 183 T.AmlT.scope("\\_SB.PCI0", [T.AmlT.device("GFX0", [T.AmlT.name("_ADR", ex.i(131072)), T.AmlT.method("_STA", 0n, False{}, [T.AmlT.ret(ex.i(15))])])])])} 184 185 # The other tables 186 187 def ex.blocks() -> F.AcpiFadtBlocks: 188 F.AcpiFadtBlocks{1536, 0, 1540, 0, 1616, 1544, 1568, 0} 189 190 def ex.lens() -> F.AcpiFadtLens: 191 F.AcpiFadtLens{4, 2, 1, 4, 16, 0, 0} 192 193 def ex.io(+port: U32, +len: U32) -> G.AcpiGas: 194 G.AcpiGas{1, U32.mul(len, 8), 0, 0, W.W64.from_u32(port)} 195 196 def AcpiExample.fadt() -> F.AcpiFadt: 197 +z = G.AcpiGas.zero() 198 +core = {F.AcpiFadtCore{ex.at(ex.facs()), ex.at(ex.dsdt()), 0, 1, 9, 178, 240, 241, 0, 0, ex.blocks(), ex.lens(), 0, 101, 1001, 0, 0, 1, 3, 13, 0, 50, 3, 34101} : F.AcpiFadtCore} 199 +x = {F.AcpiFadtX{ex.io(1536, 4), z, ex.io(1540, 2), z, ex.io(1616, 1), ex.io(1544, 4), ex.io(1568, 16), z} : F.AcpiFadtX} 200 F.AcpiFadt{ex.hdr("FACP", 6), core, Some{F.AcpiFadtExt{G.AcpiGas{1, 8, 0, 1, W.W64.from_u32(3321)}, 6, 0, 5, ex.at64(ex.facs()), ex.at64(ex.dsdt()), x}}, Some{F.AcpiFadtSleep{z, z}}, Some{W.W64.zero()}} 201 202 def AcpiExample.facs() -> FS.AcpiFacs: 203 FS.AcpiFacs{64, 305419896, 0, 0, 0, W.W64.zero(), 2, 0} 204 205 def AcpiExample.madt() -> M.AcpiMadt: 206 M.AcpiMadt{ex.hdr("APIC", 5), 4276092928, 1, {[M.AcpiMadtLapic{0, 0, 1}, M.AcpiMadtLapic{1, 1, 1}, M.AcpiMadtIoApic{2, 4273995776, 0}, M.AcpiMadtIso{0, 0, 2, 0}, M.AcpiMadtIso{0, 9, 9, 13}, M.AcpiMadtLapicNmi{255, 5, 1}, M.AcpiMadtX2apic{256, 1, 2}] : List<&2, M.AcpiMadtEntry>}} 207 208 def AcpiExample.hpet() -> Hp.AcpiHpet: 209 Hp.AcpiHpet{ex.hdr("HPET", 1), 2156306945, G.AcpiGas{0, 64, 0, 0, W.W64.from_u32(4275044352)}, 0, 14318, 0} 210 211 def AcpiExample.mcfg() -> Mc.AcpiMcfg: 212 Mc.AcpiMcfg{ex.hdr("MCFG", 1), [Mc.AcpiMcfgEntry{W.W64.from_u32(3758096384), 0, 0, 255}]} 213 214 def AcpiExample.srat() -> Sr.AcpiSrat: 215 Sr.AcpiSrat{ex.hdr("SRAT", 3), {[Sr.AcpiSratLapic{0, 0, 1, 0, 0}, Sr.AcpiSratLapic{1, 1, 1, 0, 0}, Sr.AcpiSratMemory{0, W.W64.zero(), W.W64.from_u32(2147483648), 1}, Sr.AcpiSratMemory{1, W.W64.from_parts(1, 0), W.W64.from_u32(2147483648), 1}] : List<&2, Sr.AcpiSratEntry>}} 216 217 def AcpiExample.slit() -> Sl.AcpiSlit: 218 Sl.AcpiSlit{ex.hdr("SLIT", 1), 2n, B.Octets.of_list([10, 21, 21, 10])} 219 220 def AcpiExample.bgrt() -> Bg.AcpiBgrt: 221 Bg.AcpiBgrt{ex.hdr("BGRT", 1), 1, 1, 0, W.W64.from_u32(2113929216), 100, 200} 222 223 def AcpiExample.rsdp() -> P.AcpiRsdp: 224 P.AcpiRsdp{B.Octets.of_string("BENDOS"), 2, ex.at(ex.rsdt()), Some{P.AcpiRsdpExt{36, ex.at64(ex.xsdt())}}} 225 226 def ex.listed() -> List<&2, W.W64>: 227 [ex.at64(ex.fadt()), ex.at64(ex.madt()), ex.at64(ex.hpet()), ex.at64(ex.mcfg()), ex.at64(ex.srat()), ex.at64(ex.slit()), ex.at64(ex.bgrt()), ex.at64(ex.ssdt())] 228 229 # each table as bytes, with its offset from the base 230 type AcpiExamplePiece is Data: 231 AcpiExamplePiece{offset: U32, bytes: B.Octets} 232 233 def AcpiExample.pieces() -> List<&2, AcpiExamplePiece>: 234 [AcpiExamplePiece{ex.xsdt(), S.AcpiXsdt.build(ex.hdr("XSDT", 1), ex.listed())}, 235 AcpiExamplePiece{ex.rsdt(), S.AcpiRsdt.build(ex.hdr("RSDT", 1), ex.listed())}, 236 AcpiExamplePiece{ex.fadt(), F.AcpiFadt.build(AcpiExample.fadt())}, 237 AcpiExamplePiece{ex.facs(), FS.AcpiFacs.build(AcpiExample.facs())}, 238 AcpiExamplePiece{ex.madt(), M.AcpiMadt.build(AcpiExample.madt())}, 239 AcpiExamplePiece{ex.hpet(), Hp.AcpiHpet.build(AcpiExample.hpet())}, 240 AcpiExamplePiece{ex.mcfg(), Mc.AcpiMcfg.build(AcpiExample.mcfg())}, 241 AcpiExamplePiece{ex.srat(), Sr.AcpiSrat.build(AcpiExample.srat())}, 242 AcpiExamplePiece{ex.slit(), Sl.AcpiSlit.build(AcpiExample.slit())}, 243 AcpiExamplePiece{ex.bgrt(), Bg.AcpiBgrt.build(AcpiExample.bgrt())}, 244 AcpiExamplePiece{ex.ssdt(), D.AcpiAml.build(AcpiExample.ssdt())}, 245 AcpiExamplePiece{ex.dsdt(), D.AcpiAml.build(AcpiExample.dsdt())}] 246 247 # the pieces laid out from offset at, zeros between them 248 def ex.lay(ps: List<&2, AcpiExamplePiece>, +at: Nat) -> List<&2, B.Octets>: 249 match ps: 250 case Nil{}: 251 Nil{} 252 case Con{AcpiExamplePiece{off, +o}, t}: 253 +gap = Nat.sub(U32.to_nat(off), at) 254 Con{B.Octets.zeros(gap), Con{o, ex.lay(t, Nat.add(Nat.add(at, gap), B.Octets.len(o)))}} 255 256 def AcpiExample.region() -> B.Octets: 257 B.Octets.concat(ex.lay(AcpiExample.pieces(), 0n)) 258 259 # physical memory: the RSDP at 0xE0010, the tables from 0x100000 260 def AcpiExample.memory() -> A.AcpiMemImage: 261 A.AcpiMemImage{[A.AcpiMemRegion{W.W64.from_u32(917504), B.Octets.append(B.Octets.zeros(16n), P.AcpiRsdp.build(AcpiExample.rsdp()))}, A.AcpiMemRegion{W.W64.from_u32(AcpiExample.base()), AcpiExample.region()}]} 262 263 # the host the DSDT runs against: the LPC bridge's PIRQ registers (PCI 264 # config space of device 0x1F, from offset 0x60: PIRQA routed to IRQ 11, 265 # PIRQB disabled) and the EC's temperature byte (offset 0x10: 30 degrees) 266 def AcpiExample.host() -> Hs.AmlMemHost: 267 +pci = Hs.AmlMemHost.poke(Hs.AmlMemHost.new(), 2, W.W64.add(Hs.AmlPci.address(0, 0, 31, 0, 0), W.W64.from_u32(96)), B.Octets.of_list([11, 128, 10, 5])) 268 Hs.AmlMemHost.poke(pci, 3, W.W64.from_u32(16), B.Octets.of_list([30]))