]>
Commit | Line | Data |
---|---|---|
a4b75251 TL |
1 | // SPDX-License-Identifier: BSD-3-Clause |
2 | /* Copyright 2018-2019, Intel Corporation */ | |
3 | ||
4 | /* | |
5 | * auto_flush_windows.c -- Windows auto flush detection | |
6 | */ | |
7 | ||
8 | #include <windows.h> | |
9 | #include <inttypes.h> | |
10 | ||
11 | #include "alloc.h" | |
12 | #include "out.h" | |
13 | #include "os.h" | |
14 | #include "endian.h" | |
15 | #include "auto_flush_windows.h" | |
16 | ||
17 | /* | |
18 | * is_nfit_available -- (internal) check if platform supports NFIT table. | |
19 | */ | |
20 | static int | |
21 | is_nfit_available() | |
22 | { | |
23 | LOG(3, "is_nfit_available()"); | |
24 | ||
25 | DWORD signatures_size; | |
26 | char *signatures = NULL; | |
27 | int is_nfit = 0; | |
28 | DWORD offset = 0; | |
29 | ||
30 | signatures_size = EnumSystemFirmwareTables(ACPI_SIGNATURE, NULL, 0); | |
31 | if (signatures_size == 0) { | |
32 | ERR("!EnumSystemFirmwareTables"); | |
33 | return -1; | |
34 | } | |
35 | signatures = (char *)Malloc(signatures_size + 1); | |
36 | if (signatures == NULL) { | |
37 | ERR("!malloc"); | |
38 | return -1; | |
39 | } | |
40 | int ret = EnumSystemFirmwareTables(ACPI_SIGNATURE, | |
41 | signatures, signatures_size); | |
42 | signatures[signatures_size] = '\0'; | |
43 | if (ret != signatures_size) { | |
44 | ERR("!EnumSystemFirmwareTables"); | |
45 | goto err; | |
46 | } | |
47 | ||
48 | while (offset <= signatures_size) { | |
49 | int nfit_sig = strncmp(signatures + offset, | |
50 | NFIT_STR_SIGNATURE, NFIT_SIGNATURE_LEN); | |
51 | if (nfit_sig == 0) { | |
52 | is_nfit = 1; | |
53 | break; | |
54 | } | |
55 | offset += NFIT_SIGNATURE_LEN; | |
56 | } | |
57 | ||
58 | Free(signatures); | |
59 | return is_nfit; | |
60 | ||
61 | err: | |
62 | Free(signatures); | |
63 | return -1; | |
64 | } | |
65 | ||
66 | /* | |
67 | * is_auto_flush_cap_set -- (internal) check if specific | |
68 | * capabilities bits are set. | |
69 | * | |
70 | * ACPI 6.2A Specification: | |
71 | * Bit[0] - CPU Cache Flush to NVDIMM Durability on | |
72 | * Power Loss Capable. If set to 1, indicates that platform | |
73 | * ensures the entire CPU store data path is flushed to | |
74 | * persistent memory on system power loss. | |
75 | * Bit[1] - Memory Controller Flush to NVDIMM Durability on Power Loss Capable. | |
76 | * If set to 1, indicates that platform provides mechanisms to automatically | |
77 | * flush outstanding write data from the memory controller to persistent memory | |
78 | * in the event of platform power loss. Note: If bit 0 is set to 1 then this bit | |
79 | * shall be set to 1 as well. | |
80 | */ | |
81 | static int | |
82 | is_auto_flush_cap_set(uint32_t capabilities) | |
83 | { | |
84 | LOG(3, "is_auto_flush_cap_set capabilities 0x%" PRIx32, capabilities); | |
85 | ||
86 | int CPU_cache_flush = CHECK_BIT(capabilities, 0); | |
87 | int memory_controller_flush = CHECK_BIT(capabilities, 1); | |
88 | ||
89 | LOG(15, "CPU_cache_flush %d, memory_controller_flush %d", | |
90 | CPU_cache_flush, memory_controller_flush); | |
91 | if (memory_controller_flush == 1 && CPU_cache_flush == 1) | |
92 | return 1; | |
93 | ||
94 | return 0; | |
95 | } | |
96 | ||
97 | /* | |
98 | * parse_nfit_buffer -- (internal) parse nfit buffer | |
99 | * if platform_capabilities struct is available return pcs structure. | |
100 | */ | |
101 | static struct platform_capabilities | |
102 | parse_nfit_buffer(const unsigned char *nfit_buffer, unsigned long buffer_size) | |
103 | { | |
104 | LOG(3, "parse_nfit_buffer nfit_buffer %s, buffer_size %lu", | |
105 | nfit_buffer, buffer_size); | |
106 | ||
107 | uint16_t type; | |
108 | uint16_t length; | |
109 | size_t offset = sizeof(struct nfit_header); | |
110 | struct platform_capabilities pcs = {0}; | |
111 | ||
112 | while (offset < buffer_size) { | |
113 | type = *(nfit_buffer + offset); | |
114 | length = *(nfit_buffer + offset + 2); | |
115 | if (type == PCS_TYPE_NUMBER) { | |
116 | if (length == sizeof(struct platform_capabilities)) { | |
117 | memmove(&pcs, nfit_buffer + offset, length); | |
118 | return pcs; | |
119 | } | |
120 | } | |
121 | offset += length; | |
122 | } | |
123 | return pcs; | |
124 | } | |
125 | ||
126 | /* | |
127 | * pmem2_auto_flush -- check if platform supports auto flush. | |
128 | */ | |
129 | int | |
130 | pmem2_auto_flush(void) | |
131 | { | |
132 | LOG(3, NULL); | |
133 | ||
134 | DWORD nfit_buffer_size = 0; | |
135 | DWORD nfit_written = 0; | |
136 | PVOID nfit_buffer = NULL; | |
137 | struct nfit_header *nfit_data; | |
138 | struct platform_capabilities *pc = NULL; | |
139 | ||
140 | int eADR = 0; | |
141 | int is_nfit = is_nfit_available(); | |
142 | if (is_nfit == 0) { | |
143 | LOG(15, "ACPI NFIT table not available"); | |
144 | return 0; | |
145 | } | |
146 | if (is_nfit < 0 || is_nfit != 1) { | |
147 | LOG(1, "!is_nfit_available"); | |
148 | return -1; | |
149 | } | |
150 | ||
151 | /* get the entire nfit size */ | |
152 | nfit_buffer_size = GetSystemFirmwareTable( | |
153 | (DWORD)ACPI_SIGNATURE, (DWORD)NFIT_REV_SIGNATURE, NULL, 0); | |
154 | if (nfit_buffer_size == 0) { | |
155 | ERR("!GetSystemFirmwareTable"); | |
156 | return -1; | |
157 | } | |
158 | /* reserve buffer */ | |
159 | nfit_buffer = (unsigned char *)Malloc(nfit_buffer_size); | |
160 | if (nfit_buffer == NULL) { | |
161 | ERR("!malloc"); | |
162 | goto err; | |
163 | } | |
164 | /* write actual nfit to buffer */ | |
165 | nfit_written = GetSystemFirmwareTable( | |
166 | (DWORD)ACPI_SIGNATURE, (DWORD)NFIT_REV_SIGNATURE, | |
167 | nfit_buffer, nfit_buffer_size); | |
168 | if (nfit_written == 0) { | |
169 | ERR("!GetSystemFirmwareTable"); | |
170 | goto err; | |
171 | } | |
172 | ||
173 | if (nfit_buffer_size != nfit_written) { | |
174 | errno = ERROR_INVALID_DATA; | |
175 | ERR("!GetSystemFirmwareTable invalid data"); | |
176 | goto err; | |
177 | } | |
178 | ||
179 | nfit_data = (struct nfit_header *)nfit_buffer; | |
180 | int nfit_sig = strncmp(nfit_data->signature, | |
181 | NFIT_STR_SIGNATURE, NFIT_SIGNATURE_LEN); | |
182 | if (nfit_sig != 0) { | |
183 | ERR("!NFIT buffer has invalid data"); | |
184 | goto err; | |
185 | } | |
186 | ||
187 | struct platform_capabilities pcs = parse_nfit_buffer( | |
188 | nfit_buffer, nfit_buffer_size); | |
189 | eADR = is_auto_flush_cap_set(pcs.capabilities); | |
190 | ||
191 | Free(nfit_buffer); | |
192 | return eADR; | |
193 | ||
194 | err: | |
195 | Free(nfit_buffer); | |
196 | return -1; | |
197 | } |