1 # bend-osdev-acpi, aml_host.bend: how AML reaches hardware. Operation 2 # regions (ACPI 6.5, chapter 19, OperationRegion; 5.5.2.4, Access to 3 # Operation Regions) are read and written through a host, a template 4 # argument of the interpreter: 5 # 6 # ~H: Data, ~io: H -> AmlHostOp -> Result<&2, &2, AcpiError, AmlHostAns> 7 # 8 # io(h, AmlHRead{a}) answers the value read and the host after the read 9 # (a read may have effects); io(h, AmlHWrite{a, v}) answers the host after 10 # the write (and v). An access names its address space (0 SystemMemory, 11 # 1 SystemIO, 2 PCI_Config, 3 EmbeddedControl, 4 SMBus, 5 SystemCMOS, 12 # 6 PciBarTarget, 7 IPMI, 8 GeneralPurposeIO, 9 GenericSerialBus, 10 PCC, 13 # 0x80 and up OEM), its address and its width in bits (8, 16, 32 or 64). 14 # 15 # A PCI_Config address is laid out as in a Generic Address Structure 16 # (5.2.3.2: offset in bits 0-15, function in 16-31, device in 32-47), with 17 # the bus in bits 48-55 and the low byte of the segment in bits 56-63 18 # (AmlPci.address); the interpreter takes the device and function from the 19 # region's device's _ADR, the bus from _BBN and the segment from _SEG of the 20 # nearest enclosing device that has them (6.1.1, 6.5.5, 6.5.6). 21 # 22 # AmlMemHost, the default, is pure memory: one byte map per address space, 23 # zero where nothing was written, and a log of every access. 24 import Base 25 import bend-osdev-word64@0.1.1.1/word64.bend as W 26 import bend-osdev-bytes@0.1.1.1/bytes.bend as B 27 import ./error.bend as E 28 29 type AmlAccess is Data: 30 AmlAccess{space: U32, address: W.W64, width: Nat} 31 32 type AmlHostOp is Data: 33 AmlHRead{a: AmlAccess} 34 AmlHWrite{a: AmlAccess, v: W.W64} 35 36 type AmlHostAns<-H: Data> is Data: 37 AmlHostAns{host: H, value: W.W64} 38 39 def AmlPci.address(seg: U32, bus: U32, dev: U32, fun: U32, off: U32) -> W.W64: 40 W.W64.from_parts(U32.or(U32.shln(U32.and(seg, 255), 24n), U32.or(U32.shln(U32.and(bus, 255), 16n), U32.and(dev, 65535))), U32.or(U32.shln(U32.and(fun, 65535), 16n), U32.and(off, 65535))) 41 42 # The in-memory host 43 44 type AmlMemHost is Data: 45 AmlMemHost{bytes: Map<&2, U32>, log: List<&2, AmlHostOp>} 46 47 def AmlMemHost.new() -> AmlMemHost: 48 AmlMemHost{Map.new(&2, U32), Nil{}} 49 50 def hst.key(+space: U32, +a: W.W64) -> String: 51 U32.show(space) ++ ":" ++ W.W64.show_hex(a) 52 53 def hst.snd(r: Map<&2, U32> & U32) -> U32: 54 (m, v) = r 55 v 56 57 def hst.byte(+m: Map<&2, U32>, +space: U32, +a: W.W64) -> U32: 58 hst.snd(Map.get(U32, 0, m, hst.key(space, a))) 59 60 # n bytes from address a, little-endian, into acc from bit sh 61 def hst.rd(n: Nat, +m: Map<&2, U32>, +space: U32, +a: W.W64, +sh: Nat, acc: W.W64) -> W.W64: 62 match n: 63 case 0n: 64 acc 65 case 1n+p: 66 hst.rd(p, m, space, W.W64.add(a, W.W64.one()), Nat.add(sh, 8n), W.W64.or(acc, W.W64.shl(W.W64.from_u32(hst.byte(m, space, a)), sh))) 67 68 def hst.wr(n: Nat, m: Map<&2, U32>, +space: U32, +a: W.W64, +v: W.W64) -> Map<&2, U32>: 69 match n: 70 case 0n: 71 m 72 case 1n+p: 73 hst.wr(p, Map.set(&2, U32, m, hst.key(space, a), U32.and(W.W64.lo(v), 255)), space, W.W64.add(a, W.W64.one()), W.W64.shr(v, 8n)) 74 75 def hst.go(h: AmlMemHost, op: AmlHostOp) -> Result<&2, &2, E.AcpiError, AmlHostAns>: 76 match h op: 77 case AmlMemHost{+m, log} AmlHRead{AmlAccess{+s, +a, +w}}: 78 Done{AmlHostAns{AmlMemHost{m, Con{AmlHRead{AmlAccess{s, a, w}}, log}}, hst.rd(Nat.div(w, 8n), m, s, a, 0n, W.W64.zero())}} 79 case AmlMemHost{m, log} AmlHWrite{AmlAccess{+s, +a, +w}, +v}: 80 Done{AmlHostAns{AmlMemHost{hst.wr(Nat.div(w, 8n), m, s, a, v), Con{AmlHWrite{AmlAccess{s, a, w}, v}, log}}, v}} 81 82 # the host interface for AmlMemHost (plain parameters, so that it fits the 83 # template type ~io) 84 def AmlMemHost.io(h: AmlMemHost, op: AmlHostOp) -> Result<&2, &2, E.AcpiError, AmlHostAns>: 85 hst.go(h, op) 86 87 def hst.load(xs: List<&2, U32>, m: Map<&2, U32>, +space: U32, +a: W.W64) -> Map<&2, U32>: 88 match xs: 89 case Nil{}: 90 m 91 case Con{x, t}: 92 hst.load(t, Map.set(&2, U32, m, hst.key(space, a), x), space, W.W64.add(a, W.W64.one())) 93 94 # the host with bytes o at address a of a space (no access logged) 95 def AmlMemHost.poke(h: AmlMemHost, +space: U32, +a: W.W64, o: B.Octets) -> AmlMemHost: 96 match h: 97 case AmlMemHost{m, log}: 98 AmlMemHost{hst.load(B.Octets.to_list(o), m, space, a), log} 99 100 # the byte at an address of a space 101 def AmlMemHost.peek(h: AmlMemHost, +space: U32, +a: W.W64) -> U32: 102 match h: 103 case AmlMemHost{+m, log}: 104 hst.byte(m, space, a) 105 106 def hst.rev(xs: List<&2, AmlHostOp>, acc: List<&2, AmlHostOp>) -> List<&2, AmlHostOp>: 107 match xs: 108 case Nil{}: 109 acc 110 case Con{x, t}: 111 hst.rev(t, Con{x, acc}) 112 113 # every access so far, oldest first 114 def AmlMemHost.log(h: AmlMemHost) -> List<&2, AmlHostOp>: 115 match h: 116 case AmlMemHost{m, log}: 117 hst.rev(log, Nil{}) 118 119 def AmlHostOp.show(op: AmlHostOp) -> String: 120 match op: 121 case AmlHRead{AmlAccess{s, a, w}}: 122 "read " ++ U32.show(s) ++ ":" ++ W.W64.show_hex(a) ++ "/" ++ Nat.show(w) 123 case AmlHWrite{AmlAccess{s, a, w}, v}: 124 "write " ++ U32.show(s) ++ ":" ++ W.W64.show_hex(a) ++ "/" ++ Nat.show(w) ++ " " ++ W.W64.show_hex(v) 125 126 def hst.show(xs: List<&2, AmlHostOp>, first: Bool) -> String: 127 match xs: 128 case Nil{}: 129 "" 130 case Con{x, t}: 131 match first: 132 case True{}: 133 AmlHostOp.show(x) ++ hst.show(t, False{}) 134 case False{}: 135 "; " ++ AmlHostOp.show(x) ++ hst.show(t, False{}) 136 137 # the log as text, oldest first, "; " between accesses 138 def AmlMemHost.show_log(h: AmlMemHost) -> String: 139 hst.show(AmlMemHost.log(h), True{})