aml_host.bend

bend-osdev-acpi@0.1.2.0 · 139 lines · 0 definitions

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<H>>
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.
24import Base
25import bend-osdev-word64@0.1.1.1/word64.bend as W
26import bend-osdev-bytes@0.1.1.1/bytes.bend as B
27import bend-osdev-acpi@0.1.2.0/error.bend as E
28
29type AmlAccess is Data:
30  AmlAccess{space: U32, address: W.W64, width: Nat}
31
32type AmlHostOp is Data:
33  AmlHRead{a: AmlAccess}
34  AmlHWrite{a: AmlAccess, v: W.W64}
35
36type AmlHostAns<-H: Data> is Data:
37  AmlHostAns{host: H, value: W.W64}
38
39def 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
44type AmlMemHost is Data:
45  AmlMemHost{bytes: Map<&2, U32>, log: List<&2, AmlHostOp>}
46
47def AmlMemHost.new() -> AmlMemHost:
48  AmlMemHost{Map.new(&2, U32), Nil{}}
49
50def hst.key(+space: U32, +a: W.W64) -> String:
51  U32.show(space) ++ ":" ++ W.W64.show_hex(a)
52
53def hst.snd(r: Map<&2, U32> & U32) -> U32:
54  (m, v) = r
55  v
56
57def 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
61def 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
68def 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
75def hst.go(h: AmlMemHost, op: AmlHostOp) -> Result<&2, &2, E.AcpiError, AmlHostAns<AmlMemHost>>:
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)
84def AmlMemHost.io(h: AmlMemHost, op: AmlHostOp) -> Result<&2, &2, E.AcpiError, AmlHostAns<AmlMemHost>>:
85  hst.go(h, op)
86
87def 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)
95def 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
101def 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
106def 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
114def AmlMemHost.log(h: AmlMemHost) -> List<&2, AmlHostOp>:
115  match h:
116    case AmlMemHost{m, log}:
117      hst.rev(log, Nil{})
118
119def 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
126def 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
138def AmlMemHost.show_log(h: AmlMemHost) -> String:
139  hst.show(AmlMemHost.log(h), True{})