Acpi C
Acpi C
2 * https://wiki.osdev.org/Shutdown
3 * https://elixir.bootlin.com/linux/2.3.35/source/arch/i386/kernel/acpi.c#L879
4 * https://forum.osdev.org/viewtopic.php?t=16990
5 */
6 #include "acpi.h"
7 #include "libc/memory.h"
8 #include "libc/pio.h"
9
10 // global variables
11 static b16 SLP_TYPa;
12 static b16 SLP_TYPb;
13 static struct acpi_fadt *fadt;
14
15 // initialization
16 static struct acpi_rsdp *search_RSDP_from_BIOS_memory(void);
17 static struct acpi_rsdt *fetch_and_validate_RSDT_from_RSDP(struct acpi_rsdp *rsdp);
18 static struct acpi_fadt *search_FADT_from_RSDT(struct acpi_rsdt *rsdt);
19 static struct acpi_dsdt *fetch_and_validate_DSDT_from_FADT(struct acpi_fadt *fadt);
20 static byte *search_S5_from_DSDT(struct acpi_dsdt *dsdt);
21 static bool is_valid_S5_address(byte *S5_address);
22 static void extract_SLP_TYPa_and_SLP_TYPb(byte *S5_address, b16 *SLP_TYPa_ptr, b16
*SLP_TYPb_ptr);
23
24 STATUS acpi_init(void)
25 {
26 struct acpi_rsdp *rsdp;
27 struct acpi_rsdt *rsdt;
28 struct acpi_dsdt *dsdt;
29 byte *S5_address;
30
31 if ((rsdp = search_RSDP_from_BIOS_memory()) == NULL)
32 {
33 return ENODEV;
34 }
35
36 if ((rsdt = fetch_and_validate_RSDT_from_RSDP(rsdp)) == NULL)
37 {
38 return ENODEV;
39 }
40
41 if ((fadt = search_FADT_from_RSDT(rsdt)) == NULL)
42 {
43 return ENODEV;
44 }
45
46 if ((dsdt = fetch_and_validate_DSDT_from_FADT(fadt)) == NULL)
47 {
48 return ENODEV;
49 }
50
51 /**
52 * bytecode of the \_S5 object
53 * -----------------------------------------
54 * / | (optional) | | point by S5_address
55 * AML code | NameOP | '\' | '_' | 'S' | '5' | '_'
56 * byte value | 08 | 5A | 5F | 53 | 35 | 5F
57
58 * ----------------------------------------------------------------------------
-------------------------------
59 * | | | ( SLP_TYPa ) | ( SLP_TYPb ) | (
Reserved ) | (Reserved )
60 * PackageOP | PkgLength | NumElements | byteprefix Num | byteprefix Num |
byteprefix Num | byteprefix Num
61 * 12 | 0A | 04 | 0A 05 | 0A 05 | 0A
05 | 0A 05
62
63 * ----this-structure-was-also-seen----------------------
64 * PackageOP | PkgLength | NumElements |
65 * 12 | 06 | 04 | 00 00 00 00
66 */
67 if ((S5_address = search_S5_from_DSDT(dsdt)) == NULL)
68 {
69 return ENODEV;
70 }
71
72 if (!is_valid_S5_address(S5_address))
73 {
74 return EACCERT;
75 }
76
77 extract_SLP_TYPa_and_SLP_TYPb(S5_address, &SLP_TYPa, &SLP_TYPb);
78
79 return SUCCESS;
80 }
81
82 static struct acpi_rsdp *search_RSDP_from_BIOS_memory(void)
83 {
84 nat32 i = 0;
85 struct acpi_rsdp *rsdp;
86 for (i = ACPI_BIOS_ROM_BASE; i < ACPI_BIOS_ROM_END; i += 16)
87 {
88 rsdp = (struct acpi_rsdp *)i;
89 if (rsdp->signature[0] == ACPI_RSDP1_SIG && rsdp->signature[1] ==
ACPI_RSDP2_SIG)
90 {
91 return rsdp;
92 }
93 }
94 return NULL;
95 }
96
97 static struct acpi_rsdt *fetch_and_validate_RSDT_from_RSDP(struct acpi_rsdp *rsdp)
98 {
99 struct acpi_rsdt *rsdt = rsdp->rsdt;
100 if (rsdt && rsdt->header.signature == ACPI_RSDT_SIG)
101 {
102 return rsdt;
103 }
104 return NULL;
105 }
106
107 struct acpi_fadt *search_FADT_from_RSDT(struct acpi_rsdt *rsdt)
108 {
109 nat32 sdt_entry_index;
110 nat32 sdt_entries_count = (nat32)((rsdt->header.length - sizeof(rsdt->header)) /
sizeof(struct acpi_sdt_header *));
111 for (sdt_entry_index = 0; sdt_entry_index < sdt_entries_count;
sdt_entry_index++)
112 {
113 struct acpi_sdt_header *sdt_entry = rsdt->sdt_ptrs[sdt_entry_index];
114 if (sdt_entry && sdt_entry->signature == ACPI_FADT_SIG)
115 {
116 return (struct acpi_fadt *)sdt_entry;
117 }
118 }
119 return NULL;
120 }
121
122 static struct acpi_dsdt *fetch_and_validate_DSDT_from_FADT(struct acpi_fadt *fadt)
123 {
124 struct acpi_dsdt *dsdt = fadt->dsdt;
125 if (dsdt && dsdt->header.signature == ACPI_DSDT_SIG)
126 {
127 return dsdt;
128 }
129 return NULL;
130 }
131
132 static byte *search_S5_from_DSDT(struct acpi_dsdt *dsdt)
133 {
134 byte *S5_address = dsdt->aml_code;
135 nat32 aml_code_length = dsdt->header.length - sizeof(dsdt->header);
136 while (aml_code_length-- > 0)
137 {
138 if (memcmp(S5_address, (byte *)("_S5_"), 4) == 0)
139 {
140 return S5_address;
141 }
142 ++S5_address;
143 }
144 return NULL;
145 }
146
147 static bool is_valid_S5_address(byte *S5_address)
148 {
149 return (*(S5_address - 1) == 0x08 || (*(S5_address - 2) == 0x08 && *(S5_address
- 1) == '\\')) && *(S5_address + 4) == 0x12;
150 }
151
152 #define ACPI_SLP_TYP_SHIFT 10
153 static void extract_SLP_TYPa_and_SLP_TYPb(byte *S5_address, b16 *SLP_TYPa_ptr, b16
*SLP_TYPb_ptr)
154 {
155 S5_address += 5;
156 S5_address += ((*S5_address & 0xC0) >> 6) + 2; // calculate PkgLength size
157
158 if (*S5_address == 0x0A)
159 S5_address++; // skip byteprefix
160 *SLP_TYPa_ptr = *(S5_address) << ACPI_SLP_TYP_SHIFT;
161 S5_address++;
162
163 if (*S5_address == 0x0A)
164 S5_address++; // skip byteprefix
165 *SLP_TYPb_ptr = *(S5_address) << ACPI_SLP_TYP_SHIFT;
166 }
167
168 // power off command
169 static STATUS ensure_acpi_is_enabled(struct acpi_fadt *fadt);
170 static void sendShutdownCommand(struct acpi_fadt *fadt, b16 SLP_TYPa, b16 SLP_TYPb);
171 STATUS acpi_power_off(void)
172 {
173 STATUS status;
174 status = ensure_acpi_is_enabled(fadt);
175 if (status != SUCCESS)
176 {
177 return status;
178 }
179 sendShutdownCommand(fadt, SLP_TYPa, SLP_TYPb);
180 return SUCCESS;
181 }
182
183 static bool acpi_is_enabled(struct acpi_fadt *fadt);
184 static void acpi_enable_SCI(struct acpi_fadt *fadt);
185 static STATUS ensure_acpi_is_enabled(struct acpi_fadt *fadt)
186 {
187 nat32 attempts = 5000;
188 if (!acpi_is_enabled(fadt))
189 {
190 acpi_enable_SCI(fadt);
191 while (!acpi_is_enabled(fadt) && attempts-- > 0)
192 {
193 io_wait();
194 }
195 if (!acpi_is_enabled(fadt))
196 {
197 return EACCERT;
198 }
199 }
200 return SUCCESS;
201 }
202
203 #define ACPI_SCI_EN 0x0001
204 static void acpi_enable_SCI(struct acpi_fadt *fadt)
205 {
206 if (fadt->smi_cmd)
207 out_byte((nat16)fadt->smi_cmd, fadt->acpi_enable);
208 }
209
210 static word acpi_read_pm1_control(struct acpi_fadt *fadt);
211 static bool acpi_is_enabled(struct acpi_fadt *fadt)
212 {
213 return ((acpi_read_pm1_control(fadt) & ACPI_SCI_EN) ? true : false);
214 }
215
216 static word acpi_read_pm1_control(struct acpi_fadt *fadt)
217 {
218 word value = 0;
219 if (fadt->pm1a_cnt)
220 value = in_b16(fadt->pm1a_cnt);
221 if (fadt->pm1b_cnt)
222 value |= in_b16(fadt->pm1b_cnt);
223 return value;
224 }
225
226 #define ACPI_SLP_TYP_MASK 0x1c00
227 #define ACPI_SLP_EN 0x2000 // = 1 << 13
228 static void sendShutdownCommand(struct acpi_fadt *fadt, b16 SLP_TYPa, b16 SLP_TYPb)
229 {
230 b16 value = in_b16(fadt->pm1a_cnt) & ~ACPI_SLP_TYP_MASK;
231 out_b16(fadt->pm1a_cnt, value | SLP_TYPa | ACPI_SLP_EN);
232 if (fadt->pm1b_cnt != 0)
233 {
234 value = in_b16(fadt->pm1b_cnt) & ~ACPI_SLP_TYP_MASK;
235 out_b16(fadt->pm1b_cnt, value | SLP_TYPb | ACPI_SLP_EN);
236 }
237 }