1 # bend-osdev-acpi, aml.bend: AML definition blocks loaded into a namespace, 2 # and objects evaluated there (ACPI 6.5, sections 5.4, Definition Block 3 # Encoding, and 5.5, Using the ACPI Control Method Source Language). 4 # 5 # A system is the namespace, the operation-region host, the integer width 6 # (64 bits unless the DSDT's revision is below 2) and the options. Loading a 7 # DSDT or SSDT runs its top-level terms (Name, Method, Device, Scope, ...) 8 # into the namespace; evaluating a path runs a method with arguments, or 9 # reads any other object. Both take a step bound (aml_interp.bend) and 10 # answer an AmlOutcome: the result (AcpiAmlStepLimit when the bound ran 11 # out), the namespace and host after, the steps used, the Notify calls and 12 # the values stored to Debug. 13 import Base 14 import bend-osdev-word64@0.1.1.1/word64.bend as W 15 import bend-osdev-bytes@0.1.1.1/bytes.bend as B 16 import ./error.bend as E 17 import ./dsdt.bend as D 18 import ./aml_value.bend as V 19 import ./aml_ns.bend as S 20 import ./aml_host.bend as Hs 21 import ./aml_interp.bend as I 22 23 type AmlSystem<-H: Data> is Data: 24 AmlSystem{opts: I.AmlOptions, wide: Bool, ns: S.AmlNs, host: H} 25 26 type AmlOutcome<-H: Data> is Data: 27 AmlOutcome{result: Result<&2, &2, E.AcpiError, V.AmlValue>, ns: S.AmlNs, host: H, steps: Nat, notes: List<&2, I.AmlNote>, debug: List<&2, V.AmlValue>} 28 29 def AmlOutcome.result(-H: Data, o: AmlOutcome) -> Result<&2, &2, E.AcpiError, V.AmlValue>: 30 match o: 31 case AmlOutcome{r, n, h, s, nt, d}: 32 r 33 34 def AmlOutcome.steps(-H: Data, o: AmlOutcome) -> Nat: 35 match o: 36 case AmlOutcome{r, n, h, s, nt, d}: 37 s 38 39 def AmlOutcome.ns(-H: Data, o: AmlOutcome) -> S.AmlNs: 40 match o: 41 case AmlOutcome{r, n, h, s, nt, d}: 42 n 43 44 def AmlOutcome.host(-H: Data, o: AmlOutcome) -> H: 45 match o: 46 case AmlOutcome{r, n, h, s, nt, d}: 47 h 48 49 def AmlOutcome.notes(-H: Data, o: AmlOutcome) -> List<&2, I.AmlNote>: 50 match o: 51 case AmlOutcome{r, n, h, s, nt, d}: 52 nt 53 54 def AmlOutcome.debug(-H: Data, o: AmlOutcome) -> List<&2, V.AmlValue>: 55 match o: 56 case AmlOutcome{r, n, h, s, nt, d}: 57 d 58 59 # a system with the initial namespace (\_SB_, \_OSI, ...) and this host 60 def AmlSystem.new(-H: Data, opts: I.AmlOptions, +wide: Bool, host: H) -> AmlSystem: 61 match opts: 62 case I.AmlOptions{+osi, +os, +rev}: 63 AmlSystem{I.AmlOptions{osi, os, rev}, wide, S.AmlNs.initial(os, rev), host} 64 65 def AmlSystem.ns(-H: Data, s: AmlSystem) -> S.AmlNs: 66 match s: 67 case AmlSystem{o, w, n, h}: 68 n 69 70 def AmlSystem.host(-H: Data, s: AmlSystem) -> H: 71 match s: 72 case AmlSystem{o, w, n, h}: 73 h 74 75 def AmlSystem.wide(-H: Data, s: AmlSystem) -> Bool: 76 match s: 77 case AmlSystem{o, w, n, h}: 78 w 79 80 # the system after an outcome: its namespace and host 81 def AmlSystem.after(-H: Data, s: AmlSystem, o: AmlOutcome) -> AmlSystem: 82 match s o: 83 case AmlSystem{op, w, n, h} AmlOutcome{r, n2, h2, st, nt, d}: 84 AmlSystem{op, w, n2, h2} 85 86 def aml.rev.n(xs: List<&2, I.AmlNote>, acc: List<&2, I.AmlNote>) -> List<&2, I.AmlNote>: 87 match xs: 88 case Nil{}: 89 acc 90 case Con{x, t}: 91 aml.rev.n(t, Con{x, acc}) 92 93 def aml.rev.v(xs: List<&2, V.AmlValue>, acc: List<&2, V.AmlValue>) -> List<&2, V.AmlValue>: 94 match xs: 95 case Nil{}: 96 acc 97 case Con{x, t}: 98 aml.rev.v(t, Con{x, acc}) 99 100 def aml.result(md: I.AmlMode) -> Result<&2, &2, E.AcpiError, V.AmlValue>: 101 match md: 102 case I.AmlMDone{v}: 103 Done{v} 104 case I.AmlMFail{e}: 105 Fail{e} 106 case _: 107 Fail{E.AcpiAmlStepLimit{}} 108 109 # what a run came to 110 def AmlOutcome.of_run(-H: Data, r: I.AmlRun) -> AmlOutcome: 111 match r: 112 case I.AmlRun{I.AmlMachine{md, c, fs, I.AmlWorld{ctx, ns, h, I.AmlEnv{wd, o, nts, d, cl}}}, s}: 113 AmlOutcome{aml.result(md), ns, h, s, aml.rev.n(nts, Nil{}), aml.rev.v(d, Nil{})} 114 115 def aml.env(+wide: Bool, opts: I.AmlOptions) -> I.AmlEnv: 116 I.AmlEnv{wide, opts, Nil{}, Nil{}, W.W64.zero()} 117 118 def aml.ctx(+p: String, args: List<&2, V.AmlValue>) -> I.AmlCtx: 119 I.AmlCtx{p, args, I.in.locals(), Nil{}} 120 121 # a machine at the start of a definition block, at the root 122 def AmlSystem.loader(-H: Data, s: AmlSystem, aml: B.Octets) -> I.AmlMachine: 123 match s: 124 case AmlSystem{o, +wd, n, h}: 125 +code = B.Octets.to_list(aml) 126 I.AmlMachine{I.AmlMExec{}, I.AmlCur{code, B.len.fast(code)}, [I.AmlFTop{}], I.AmlWorld{aml.ctx("\\", Nil{}), n, h, aml.env(wd, o)}} 127 128 def aml.read(-H: Data, r: Result<&2, &2, E.AcpiError, I.AmlRead>, +w: I.AmlWorld) -> I.AmlMachine: 129 match r: 130 case Fail{e}: 131 I.AmlMachine{I.AmlMFail{e}, I.in.no.cur(), [I.AmlFTop{}], w} 132 case Done{I.AmlRead{w2, v}}: 133 I.AmlMachine{I.AmlMDone{v}, I.in.no.cur(), [I.AmlFTop{}], w2} 134 135 def aml.start(~H: Data, ~io: H -> Hs.AmlHostOp -> Result<&2, &2, E.AcpiError, Hs.AmlHostAns>, o: Maybe<&2, S.AmlObject>, +p: String, args: List<&2, V.AmlValue>, +w: I.AmlWorld) -> I.AmlMachine: 136 match o: 137 case Some{S.AmlOMethod{n, f, +body}}: 138 +code = B.Octets.to_list(body) 139 I.AmlMachine{I.AmlMExec{}, I.AmlCur{code, B.len.fast(code)}, [I.AmlFTop{}], I.in.with_ctx(H, w, aml.ctx(p, args))} 140 case _: 141 aml.read(H, I.in.read_obj(~H, ~io, w, p, o), w) 142 143 # arguments as the method sees them: Integers cut to 32 bits in a 32-bit 144 # definition block 145 def aml.fit.args(+wide: Bool, xs: List<&2, V.AmlValue>) -> List<&2, V.AmlValue>: 146 match xs: 147 case Nil{}: 148 Nil{} 149 case Con{V.AmlInt{x}, t}: 150 Con{V.AmlInt{V.AmlInt.fit(wide, x)}, aml.fit.args(wide, t)} 151 case Con{v, t}: 152 Con{v, aml.fit.args(wide, t)} 153 154 # a machine about to evaluate the object at path p (a method is called 155 # with args; any other object is read before the first step) 156 def AmlSystem.caller(~H: Data, ~io: H -> Hs.AmlHostOp -> Result<&2, &2, E.AcpiError, Hs.AmlHostAns>, s: AmlSystem, +p: String, args: List<&2, V.AmlValue>) -> I.AmlMachine: 157 match s: 158 case AmlSystem{o, +wd, +n, h}: 159 +q = I.in.unalias(n, p) 160 aml.start(~H, ~io, S.AmlNs.get(n, q), q, aml.fit.args(wd, args), I.AmlWorld{aml.ctx("\\", Nil{}), n, h, aml.env(wd, o)}) 161 162 # a definition block run into the system's namespace, in at most n steps 163 def Aml.load(~H: Data, ~io: H -> Hs.AmlHostOp -> Result<&2, &2, E.AcpiError, Hs.AmlHostAns>, s: AmlSystem, aml: B.Octets, n: Nat) -> AmlOutcome: 164 AmlOutcome.of_run(H, I.AmlM.run(~H, ~io, n, AmlSystem.loader(H, s, aml))) 165 166 # the object at path p evaluated (a method called with args), in at most n 167 # steps 168 def Aml.eval(~H: Data, ~io: H -> Hs.AmlHostOp -> Result<&2, &2, E.AcpiError, Hs.AmlHostAns>, s: AmlSystem, +p: String, args: List<&2, V.AmlValue>, n: Nat) -> AmlOutcome: 169 AmlOutcome.of_run(H, I.AmlM.run(~H, ~io, n, AmlSystem.caller(~H, ~io, s, p, args))) 170 171 def aml.boot.chk(-H: Data, r: Result<&2, &2, E.AcpiError, V.AmlValue>, s: AmlSystem, o: AmlOutcome) -> Result<&2, &2, E.AcpiError, AmlSystem>: 172 match r: 173 case Fail{e}: 174 Fail{e} 175 case Done{v}: 176 Done{AmlSystem.after(H, s, o)} 177 178 def aml.boot.res(-H: Data, s: AmlSystem, +o: AmlOutcome) -> Result<&2, &2, E.AcpiError, AmlSystem>: 179 aml.boot.chk(H, AmlOutcome.result(H, o), s, o) 180 181 def aml.boot.one(~H: Data, ~io: H -> Hs.AmlHostOp -> Result<&2, &2, E.AcpiError, Hs.AmlHostAns>, t: D.AcpiAml, +n: Nat, r: Result<&2, &2, E.AcpiError, AmlSystem>) -> Result<&2, &2, E.AcpiError, AmlSystem>: 182 match r: 183 case Fail{e}: 184 Fail{e} 185 case Done{+s}: 186 aml.boot.res(H, s, Aml.load(~H, ~io, s, D.AcpiAml.aml(t), n)) 187 188 def aml.boot.go(~H: Data, ~io: H -> Hs.AmlHostOp -> Result<&2, &2, E.AcpiError, Hs.AmlHostAns>, ts: List<&2, D.AcpiAml>, +n: Nat, r: Result<&2, &2, E.AcpiError, AmlSystem>) -> Result<&2, &2, E.AcpiError, AmlSystem>: 189 match ts: 190 case Nil{}: 191 r 192 case Con{t, rest}: 193 aml.boot.go(~H, ~io, rest, n, aml.boot.one(~H, ~io, t, n, r)) 194 195 # the namespace of a machine's tables: the DSDT, then each SSDT, each 196 # loaded in at most n steps; the integer width is the DSDT's (the first 197 # table's) 198 def Aml.boot(~H: Data, ~io: H -> Hs.AmlHostOp -> Result<&2, &2, E.AcpiError, Hs.AmlHostAns>, opts: I.AmlOptions, host: H, ts: List<&2, D.AcpiAml>, +n: Nat) -> Result<&2, &2, E.AcpiError, AmlSystem>: 199 match ts: 200 case Nil{}: 201 Done{AmlSystem.new(H, opts, True{}, host)} 202 case Con{+t, rest}: 203 aml.boot.go(~H, ~io, Con{t, rest}, n, Done{AmlSystem.new(H, opts, D.AcpiAml.wide(t), host)}) 204 205 # The defaults: the in-memory host 206 207 def Aml.mem_system(+wide: Bool) -> AmlSystem: 208 AmlSystem.new(Hs.AmlMemHost, I.AmlOptions.default(), wide, Hs.AmlMemHost.new()) 209 210 def Aml.load_mem(s: AmlSystem, aml: B.Octets, n: Nat) -> AmlOutcome: 211 Aml.load(~Hs.AmlMemHost, ~Hs.AmlMemHost.io, s, aml, n) 212 213 def Aml.eval_mem(s: AmlSystem, +p: String, args: List<&2, V.AmlValue>, n: Nat) -> AmlOutcome: 214 Aml.eval(~Hs.AmlMemHost, ~Hs.AmlMemHost.io, s, p, args, n)