]> git.proxmox.com Git - ceph.git/blame - ceph/src/pmdk/src/libpmem2/auto_flush_windows.c
import ceph 16.2.7
[ceph.git] / ceph / src / pmdk / src / libpmem2 / auto_flush_windows.c
CommitLineData
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 */
20static int
21is_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
61err:
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 */
81static int
82is_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 */
101static struct platform_capabilities
102parse_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 */
129int
130pmem2_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
194err:
195 Free(nfit_buffer);
196 return -1;
197}