ReactOS 0.4.17-dev-1281-g065abb3
bootdev.cpp
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS SuperSpeed USB Hub Driver
3 * LICENSE: MIT (https://spdx.org/licenses/MIT)
4 * PURPOSE: Boot, paging and hibernation devices behind a hub
5 * COPYRIGHT: Copyright 2026 Justin Miller <justin.miller@reactos.org>
6 */
7
8#include "usbhub3.h"
9
10#define NDEBUG
11#include <debug.h>
12
13/* GLOBALS ********************************************************************/
14
15typedef
20
21/* What ExRegisterBootDevice takes; the layout is the kernel's */
23{
30};
31
32#ifdef _WIN64
33C_ASSERT(sizeof(HubBootRegistration) == 0x28);
34#else
36#endif
37
38#define HUB_BOOT_REGISTRATION_VERSION 1
39
40/* FUNCTIONS ******************************************************************/
41
42/* A device outside the machine's own container is external; no answer counts as external */
44static
47HubBootIsExternal(
48 _In_ WDFDEVICE Device)
49{
50 BOOLEAN InMachine = FALSE;
52 ULONG Size;
54
55 PAGED_CODE();
56
57 Status = IoGetDevicePropertyData(WdfDeviceWdmGetDeviceObject(Device),
58 &DEVPKEY_Device_InLocalMachineContainer,
60 0,
61 sizeof(InMachine),
62 &InMachine,
63 &Size,
64 &Type);
65 if (!NT_SUCCESS(Status))
66 {
67 DPRINT("No container answer for %p (0x%lx), treated as external\n", Device, Status);
68 return TRUE;
69 }
70
71 return InMachine == DEVPROP_FALSE;
72}
73
74/* Runs once a recovered boot device was counted as surprise removed */
75static
76VOID
79 _In_ WDFWORKITEM WorkItem)
80{
82
83 DPRINT("Boot device recovered after a surprise removal\n");
84 HubWmiNotifyBootSurpriseRemoval();
85 HubBumpBootSurpriseRemovalCount();
86}
87
88/* The work item is freed with its parent PDO */
90static
91VOID
93HubBootAfterRecovery(
95{
98 WDFWORKITEM WorkItem;
100
101 if (!Child->m_Port->HasFlag(PortFlag::PendingRecoveryUpdate))
102 return;
103
104 Child->m_Port->ClearFlag(PortFlag::PendingRecoveryUpdate);
105
108 Attributes.ParentObject = Child->m_Pdo->m_Device;
109
110 Status = WdfWorkItemCreate(&Config, &Attributes, &WorkItem);
111 if (!NT_SUCCESS(Status))
112 {
113 DPRINT1("Boot recovery work item failed 0x%lx\n", Status);
114 return;
115 }
116
118}
119
120/* Must run at PASSIVE_LEVEL since it sends a synchronous transfer */
122static
124NTAPI
125HubBootDeviceReady(
128{
130 HubPort* Port = Child->m_Port;
136
138
140
141 /* The hub reset that is running finishes the recovery */
142 if (Child->m_Hub->IsHubResetInProgress())
143 {
144 DPRINT("Boot device on port %u counted present during a hub reset\n", Port->Number());
145 Ready = TRUE;
146 goto Done;
147 }
148
149 InterlockedExchange(&Child->m_BootReportedMissing, 0);
150
151 /* A failed status read answers not ready; a hub reset follows */
152 ReadStatus = HubReadPortStatusForBootDevice(Child, &PortStatus);
154 {
155 DPRINT1("Boot device port %u status read failed 0x%lx\n", Port->Number(), ReadStatus);
156 goto Done;
157 }
158
159 Status = (USHORT)(PortStatus & 0xFFFF);
160 Change = (USHORT)(PortStatus >> 16);
161
163 {
164 DPRINT("Boot port %u still holds a different device\n", Port->Number());
165 goto Done;
166 }
167
169 {
170 DPRINT("Boot device on port %u failed its last enumeration\n", Port->Number());
171 goto Done;
172 }
173
175 {
176 DPRINT("Boot device on port %u is connected\n", Port->Number());
177 Ready = TRUE;
178 goto Done;
179 }
180
181 /* A real disconnect passes through SS.Inactive for at most about 20 ms */
183 {
185 {
186 DPRINT("Boot port %u stayed in SS.Inactive, device counted present\n", Port->Number());
187 Ready = TRUE;
188 goto Done;
189 }
190
191 DPRINT("Boot port %u in SS.Inactive for the first time\n", Port->Number());
193 goto Done;
194 }
195
197 DPRINT("Boot device on port %u still missing, status 0x%lx\n", Port->Number(), PortStatus);
198
200 /* Another thread reported it missing again meanwhile */
201 if (!Ready && InterlockedExchange(&Child->m_BootReportedMissing, 1) == 1)
202 {
203 DPRINT("Boot device on port %u reported missing again, answering ready\n", Port->Number());
204 Ready = TRUE;
205 }
206
207 if (Ready)
208 {
210 HubBootAfterRecovery(Child);
211 }
212
213 return Ready;
214}
215
216/* The kernel routines are looked up at the first registration */
218static
220NTAPI
221HubBootRegister(
223{
226
227 PAGED_CODE();
228
230 {
231 RtlInitUnicodeString(&Name, L"ExRegisterBootDevice");
233
234 RtlInitUnicodeString(&Name, L"ExNotifyBootDeviceRemoval");
236 }
237
240
243 Registration.DriverObject = HubDriver.DriverObject;
244 Registration.IsBootDeviceReady = HubBootDeviceReady;
245 Registration.Context = Child;
246
247 return HubDriver.RegisterBootDevice(&Registration, &Child->m_BootHandle);
248}
249
250/* Special files are never disabled once enabled, so a removal notice is handled like an add; under WinPE each notice prepares again */
252static
254NTAPI
255HubBootAddToPath(
256 _In_ HubPdo* Pdo,
259{
260 HubChild* Child = Pdo->m_Child;
261 HubFdo* Hub = Pdo->m_Hub;
263
264 PAGED_CODE();
265
267 return STATUS_SUCCESS;
268
269 if (Child->m_BootControl.Request == NULL)
270 {
271 Status = Child->m_BootControl.Create(Child->m_Object, WdfDeviceGetIoTarget(Hub->m_Device));
272 if (!NT_SUCCESS(Status))
273 {
274 DPRINT1("Boot request on port %u not created 0x%lx\n", Pdo->m_PortNumber, Status);
275 return Status;
276 }
277 }
278
279 if (!Child->m_Port->HasFlag(PortFlag::DeviceConnected))
280 {
281 DPRINT1("Boot device on port %u left before its usage notice\n", Pdo->m_PortNumber);
283 }
284
285 if (!WinPe)
286 {
287 /* Internal devices are not registered; saves memory where SATA was replaced by USB */
288 if (External)
289 {
290 Status = HubBootRegister(Child);
291 if (!NT_SUCCESS(Status))
292 DPRINT1("Boot device registration failed 0x%lx\n", Status);
293
294 if (!Child->HasProperty(ChildProperty::IsHub))
295 HubWmiRegisterBootSurpriseRemoval(Pdo->m_Device);
296 }
297 else
298 {
299 DPRINT1("Boot device on port %u is internal, not registered\n", Pdo->m_PortNumber);
300 }
301
302 Pdo->SetFlag(PdoFlag::InBootPath);
303 Child->m_Port->SetFlag(PortFlag::SupportsReattach);
304 }
305
306 /* The port machine follows the hub's forward progress setting */
307 Child->m_NeedsForwardProgress = TRUE;
308 DPRINT("Port %u device is now in the boot path\n", Pdo->m_PortNumber);
310}
311
314NTAPI
316 _In_ WDFDEVICE Device,
319{
320 HubPdo* Pdo = HubGetPdoContext(Device);
321 HubChild* Child = Pdo->m_Child;
324
325 PAGED_CODE();
326
327 WinPe = HubIsWinPe();
328
329 DPRINT("Port %u usage notice %d, in path %u\n", Pdo->m_PortNumber, (int)NotificationType, IsInNotificationPath);
330
331 if (!(Child->m_SqmFlags & (LONG)ChildSqm::HoldsPagingFiles))
332 {
335 }
336
338 {
339 /* Paging on an external disk that is not the boot disk is refused */
341 if (!WinPe)
342 {
343 External = HubBootIsExternal(Device);
344 if (External)
345 {
346 if (Pdo->HasFlag(PdoFlag::InBootPath))
347 return STATUS_SUCCESS;
348
349 DPRINT1("Paging refused on external port %u device\n", Pdo->m_PortNumber);
351 }
352 }
353 return HubBootAddToPath(Pdo, WinPe, External);
354
356 if (!WinPe && !Pdo->HasFlag(PdoFlag::InBootPath))
357 External = HubBootIsExternal(Device);
358 return HubBootAddToPath(Pdo, WinPe, External);
359
362 Child->m_NeedsForwardProgress = TRUE;
363 return STATUS_SUCCESS;
364
365 default:
366 return STATUS_SUCCESS;
367 }
368}
369
370VOID
372{
373 DPRINT1("A different device is on boot port %u\n", m_Device->m_Port->Number());
374 m_Device->SetState(ChildState::DifferentDeviceOnBootPort);
376}
Type
Definition: Type.h:7
unsigned char BOOLEAN
Definition: actypes.h:127
#define InterlockedExchange
Definition: armddk.h:54
LONG NTSTATUS
Definition: precomp.h:26
#define DPRINT1
Definition: precomp.h:8
_In_ BOOLEAN WinPe
Definition: bootdev.cpp:257
USHORT Status
Definition: bootdev.cpp:132
_In_ BOOLEAN _In_ BOOLEAN External
Definition: bootdev.cpp:259
InterlockedExchange & Child
Definition: bootdev.cpp:149
PAGED_CODE()
return STATUS_SUCCESS
Definition: bootdev.cpp:309
ULONG PortStatus
Definition: bootdev.cpp:131
NTSTATUS ReadStatus
Definition: bootdev.cpp:134
HubFdo * Hub
Definition: bootdev.cpp:261
static VOID NTAPI HubBootRecoveryWorkItem(_In_ WDFWORKITEM WorkItem)
Definition: bootdev.cpp:78
#define HUB_BOOT_REGISTRATION_VERSION
Definition: bootdev.cpp:38
HubPort * Port
Definition: bootdev.cpp:130
BOOLEAN Ready
Definition: bootdev.cpp:135
_In_ PVOID Context
Definition: bootdev.cpp:128
USHORT Change
Definition: bootdev.cpp:133
BOOLEAN(NTAPI * PFN_HUB_BOOT_DEVICE_READY)(_In_ PDEVICE_OBJECT Pdo, _In_ PVOID Context)
Definition: bootdev.cpp:17
HubFlushSqmFlags(Child)
EVT_WDF_DEVICE_USAGE_NOTIFICATION_EX HubPdoEvtUsageNotificationEx
Definition: childpdo.h:168
HubChild * m_Device
Definition: devsm.h:1832
VOID NotifyWrongDevice()
Definition: bootdev.cpp:371
HubPort * m_Port
Definition: hubchild.h:317
VOID SetState(_In_ ChildState Flag)
Definition: hubchild.h:267
PVOID m_BootHandle
Definition: hubchild.h:429
Definition: fdo.h:132
WDFDEVICE m_Device
Definition: fdo.h:349
USHORT Number() const
Definition: hubport.h:213
USB_CONNECTION_STATUS m_ConnectionStatus
Definition: hubport.h:254
VOID SetFlag(_In_ PortFlag Flag)
Definition: hubport.h:192
VOID ClearFlag(_In_ PortFlag Flag)
Definition: hubport.h:199
BOOLEAN HasFlag(_In_ PortFlag Flag) const
Definition: hubport.h:185
#define STATUS_NOT_SUPPORTED
Definition: d3dkmdt.h:48
LPWSTR Name
Definition: desk.c:124
#define DEVPROP_FALSE
Definition: devpropdef.h:67
ULONG DEVPROPTYPE
Definition: devpropdef.h:24
KEVENT Done
Definition: devreq.cpp:1162
#define DSM_KIND_SUPER_SPEED
Definition: devsm.h:150
_In_ ULONG Attributes
Definition: dispmprt.h:1077
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:33
#define L(x)
Definition: resources.c:13
#define _IRQL_requires_max_(irql)
Definition: driverspecs.h:230
#define _IRQL_requires_(irql)
Definition: driverspecs.h:229
VOID NTAPI HubNotifyBootDeviceRemoval(_In_opt_ PVOID Handle)
Definition: entry.cpp:335
HubDriverState HubDriver
Definition: entry.cpp:13
#define PASSIVE_LEVEL
Definition: env_spec_w32.h:693
#define DISPATCH_LEVEL
Definition: env_spec_w32.h:696
_Must_inspect_result_ _In_ CONST FLT_REGISTRATION * Registration
Definition: fltkernel.h:991
Status
Definition: gdiplustypes.h:24
@ HoldsPagingFiles
@ DifferentDeviceOnBootPort
VOID(NTAPI * PFN_HUB_NOTIFY_BOOT_DEVICE_REMOVAL)(_In_ PVOID Handle)
Definition: hubdriver.h:55
NTSTATUS(NTAPI * PFN_HUB_REGISTER_BOOT_DEVICE)(_In_ PVOID Registration, _Out_ PVOID *Handle)
Definition: hubdriver.h:49
@ SsInactiveSeenForBoot
@ SupportsReattach
@ DeviceConnected
@ PendingRecoveryUpdate
#define LINK_SS_INACTIVE
Definition: hubreq.h:119
#define PC_CONNECT
Definition: hubreq.h:136
#define PS_CONNECTED
Definition: hubreq.h:126
FORCEINLINE USHORT NTAPI HubLinkState(_In_ USHORT Status)
Definition: hubreq.h:159
WdfWorkItemEnqueue(WorkItem)
_Must_inspect_result_ _In_ PUCXHUB_DEVICE_CREATE_INFO _Out_ UCXUSBDEVICE * Device
Definition: hubucx.h:258
_In_opt_ PVOID _In_ PUCXHUB_WORKITEM WorkItem
Definition: hubucx.h:227
_In_ UCXUSBDEVICE _In_ PDEVICE_OBJECT Pdo
Definition: hubucx.h:280
#define InterlockedOr
Definition: interlocked.h:239
#define C_ASSERT(e)
Definition: intsafe.h:73
_In_ PIRP _In_ PIO_COMPLETION_ROUTINE _In_ PVOID Context
Definition: kse.h:81
#define _In_
Definition: no_sal2.h:158
NTSYSAPI VOID NTAPI RtlInitUnicodeString(PUNICODE_STRING DestinationString, PCWSTR SourceString)
#define LOCALE_NEUTRAL
#define UNREFERENCED_PARAMETER(P)
Definition: ntbasedef.h:329
#define BOOLEAN
Definition: pedump.c:73
long LONG
Definition: pedump.c:60
unsigned short USHORT
Definition: pedump.c:61
#define DPRINT
Definition: sndvol32.h:73
PDRIVER_OBJECT DriverObject
Definition: bootdev.cpp:26
PFN_HUB_BOOT_DEVICE_READY IsBootDeviceReady
Definition: bootdev.cpp:28
PDEVICE_OBJECT DeviceObject
Definition: bootdev.cpp:27
PFN_HUB_NOTIFY_BOOT_DEVICE_REMOVAL NotifyBootDeviceRemoval
Definition: hubdriver.h:113
PDRIVER_OBJECT DriverObject
Definition: hubdriver.h:89
PFN_HUB_REGISTER_BOOT_DEVICE RegisterBootDevice
Definition: hubdriver.h:112
PVOID NTAPI MmGetSystemRoutineAddress(IN PUNICODE_STRING SystemRoutineName)
Definition: sysldr.c:3672
#define NTAPI
Definition: typedefs.h:36
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:262
uint32_t ULONG
Definition: typedefs.h:59
_Must_inspect_result_ _In_ WDFDEVICE _In_ PUCX_CONTROLLER_CONFIG Config
#define STATUS_NO_SUCH_DEVICE
Definition: udferr_usr.h:136
@ DeviceFailedEnumeration
Definition: usbioctl.h:280
enum _WDF_SPECIAL_FILE_TYPE WDF_SPECIAL_FILE_TYPE
_In_ WDF_SPECIAL_FILE_TYPE _In_ BOOLEAN IsInNotificationPath
Definition: wdfdevice.h:1027
_In_ WDF_SPECIAL_FILE_TYPE NotificationType
Definition: wdfdevice.h:1024
@ WdfSpecialFileBoot
Definition: wdfdevice.h:445
@ WdfSpecialFileHibernation
Definition: wdfdevice.h:443
@ WdfSpecialFilePaging
Definition: wdfdevice.h:442
_Must_inspect_result_ _In_ WDFDEVICE _In_ PWDF_DEVICE_PROPERTY_DATA _In_ DEVPROPTYPE _In_ ULONG Size
Definition: wdfdevice.h:4539
_Must_inspect_result_ _In_ WDFDEVICE _In_ WDFDEVICE Child
Definition: wdffdo.h:536
FORCEINLINE VOID WDF_OBJECT_ATTRIBUTES_INIT(_Out_ PWDF_OBJECT_ATTRIBUTES Attributes)
Definition: wdfobject.h:147
FORCEINLINE VOID WDF_WORKITEM_CONFIG_INIT(_Out_ PWDF_WORKITEM_CONFIG Config, _In_ PFN_WDF_WORKITEM EvtWorkItemFunc)
Definition: wdfworkitem.h:85