Skip to content

Commit 58046af

Browse files
committed
usb: implement usage of buffer alignment to fix uhci cross paging with user-supplied buffers
1 parent af45398 commit 58046af

20 files changed

Lines changed: 813 additions & 520 deletions

File tree

kernel/include/shm.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ SHMExport(
6565
KERNELAPI oserr_t KERNELABI
6666
SHMConform(
6767
_In_ uuid_t shmID,
68-
_In_ enum OSMemoryConformity conformity,
68+
_In_ SHMConformityOptions_t* conformity,
6969
_In_ unsigned int flags,
7070
_In_ unsigned int access,
7171
_In_ size_t offset,

kernel/memory/ms_shm.c

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -491,14 +491,57 @@ __GatherSGList(
491491
}
492492

493493
static bool
494-
__VerifySGConformity(
494+
__VerifySGAlignment(
495+
_In_ SHMSG_t* sg,
496+
_In_ int sgCount,
497+
_In_ size_t offset,
498+
_In_ uint32_t alignment)
499+
{
500+
size_t bytesLeft = offset;
501+
502+
if (alignment == 0) {
503+
return true;
504+
}
505+
506+
// Alignment must be a power of two.
507+
if (!IsPowerOfTwo(alignment)) {
508+
ERROR("__VerifySGAlignment: alignment requirement 0x%x is not a power of two!", alignment);
509+
return false;
510+
}
511+
512+
for (int i = 0; i < sgCount; i++) {
513+
uintptr_t address = sg[i].Address;
514+
if (sg[i].Length > bytesLeft) {
515+
// found correct SG entry
516+
address += bytesLeft;
517+
if (address & (alignment - 1)) {
518+
return false;
519+
}
520+
return true;
521+
}
522+
bytesLeft -= sg[i].Length;
523+
}
524+
525+
// offset was wild, just check what's left.
526+
if (bytesLeft & (alignment - 1)) {
527+
return false;
528+
}
529+
return true;
530+
}
531+
532+
static bool
533+
__VerifyMemoryConformity(
495534
_In_ SHMSG_t* sg,
496535
_In_ int sgCount,
497536
_In_ enum OSMemoryConformity conformity)
498537
{
499538
size_t pageMask = __MASK;
500539

501-
// Lookup the page-mask for the specific conformity
540+
if (conformity == OSMEMORYCONFORMITY_NONE) {
541+
return true;
542+
}
543+
544+
// Lookup the page-mask for the specific conformityOpts
502545
ArchSHMTypeToPageMask(conformity, &pageMask);
503546

504547
// Verify all SG entries against the page-mask
@@ -510,6 +553,22 @@ __VerifySGConformity(
510553
return true;
511554
}
512555

556+
static bool
557+
__VerifySGConformity(
558+
_In_ SHMSG_t* sg,
559+
_In_ int sgCount,
560+
_In_ size_t offset,
561+
_In_ SHMConformityOptions_t* conformityOpts)
562+
{
563+
if (!__VerifySGAlignment(sg, sgCount, offset, conformityOpts->BufferAlignment)) {
564+
return false;
565+
}
566+
if (!__VerifyMemoryConformity(sg, sgCount, conformityOpts->Conformity)) {
567+
return false;
568+
}
569+
return true;
570+
}
571+
513572
static oserr_t
514573
__MapOriginalBuffer(
515574
_In_ uuid_t shmID,
@@ -764,7 +823,7 @@ __ClampLength(
764823
oserr_t
765824
SHMConform(
766825
_In_ uuid_t shmID,
767-
_In_ enum OSMemoryConformity conformity,
826+
_In_ SHMConformityOptions_t* conformity,
768827
_In_ unsigned int flags,
769828
_In_ unsigned int access,
770829
_In_ size_t offset,
@@ -788,7 +847,8 @@ SHMConform(
788847
return oserr;
789848
}
790849

791-
if (__VerifySGConformity(sg, sgCount, conformity)) {
850+
// Verify the buffer conformity
851+
if (__VerifySGConformity(sg, sgCount, offset, conformity)) {
792852
DestroyHandle(shmID);
793853
kfree(sg);
794854
return __MapOriginalBuffer(
@@ -802,7 +862,7 @@ SHMConform(
802862
correctedLength = __ClampLength(source, offset, length);
803863
oserr = __CloneConformBuffer(
804864
source,
805-
conformity,
865+
conformity->Conformity,
806866
flags,
807867
offset,
808868
correctedLength,

kernel/memory/ms_shm_test.c

Lines changed: 132 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -866,7 +866,10 @@ void TestSHMConform_IsConformed(void** state)
866866
// will be 0xFFFFF, which will cover our pages.
867867
oserr = SHMConform(
868868
handle.ID,
869-
OSMEMORYCONFORMITY_LOW,
869+
&(SHMConformityOptions_t) {
870+
.BufferAlignment = 0,
871+
.Conformity = OSMEMORYCONFORMITY_LOW,
872+
},
870873
0,
871874
SHM_ACCESS_READ,
872875
0,
@@ -894,7 +897,120 @@ void TestSHMConform_IsConformed(void** state)
894897
TeardownTest(state);
895898
}
896899

897-
void TestSHMConform_NotConformed(void** state)
900+
void TestSHMConform_NotAlignmentConformed(void** state)
901+
{
902+
oserr_t oserr;
903+
SHMHandle_t handle;
904+
SHMHandle_t conformedHandle;
905+
int pageCount = 9;
906+
paddr_t page[9] = {
907+
0x1000856,
908+
0x1400000,
909+
0x1500000,
910+
0x1600000,
911+
0x1700000,
912+
0x1C00000,
913+
0x1A00000,
914+
0x2000000,
915+
0x2A00000,
916+
};
917+
918+
// The following function calls are expected during normal
919+
// creation:
920+
921+
// 1. CreateHandle, return a non-standard id as we use it twice
922+
g_testContext.CreateHandle.Calls[0].ReturnedID = 0x10;
923+
g_testContext.CreateHandle.Calls[0].ReturnedIDProvided = true;
924+
925+
// 2. GetMemorySpaceMapping
926+
g_testContext.GetMemorySpaceMapping.ExpectedAddress = 0x1596856;
927+
g_testContext.GetMemorySpaceMapping.CheckAddress = true;
928+
g_testContext.GetMemorySpaceMapping.ExpectedPageCount = pageCount;
929+
g_testContext.GetMemorySpaceMapping.CheckPageCount = true;
930+
g_testContext.GetMemorySpaceMapping.PageValues = &page[0];
931+
g_testContext.GetMemorySpaceMapping.PageValuesProvided = true;
932+
g_testContext.GetMemorySpaceMapping.ReturnValue = OS_EOK;
933+
934+
// We use garbage values as the memory contents are not accessed
935+
oserr = SHMExport(
936+
(void*)0x1596856,
937+
0x8400,
938+
0,
939+
0,
940+
&handle
941+
);
942+
assert_int_equal(oserr, OS_EOK);
943+
assert_int_equal(handle.ID, 0x10);
944+
assert_int_equal(handle.SourceID, UUID_INVALID);
945+
assert_int_equal(handle.SourceFlags, 0);
946+
assert_ptr_equal(handle.Buffer, 0x1596856);
947+
assert_int_equal(handle.Capacity, 0x8400);
948+
assert_int_equal(handle.Length, 0x8400);
949+
assert_int_equal(handle.Offset, 0);
950+
951+
// 3. CreateHandle, return a non-standard id as we use it twice
952+
g_testContext.CreateHandle.Calls[1].ReturnedID = 0x20;
953+
g_testContext.CreateHandle.Calls[1].ReturnedIDProvided = true;
954+
955+
// 4. AcquireHandleOfType. We will be acquiring the original buffer created.
956+
g_testContext.AcquireHandleOfType.Resource = g_testContext.CreateHandle.Calls[0].CreatedResource;
957+
g_testContext.AcquireHandleOfType.ResourceProvided = true;
958+
959+
// 5. ArchSHMTypeToPageMask
960+
g_testContext.ArchSHMTypeToPageMask.PageMask = 0xFFFFF;
961+
g_testContext.ArchSHMTypeToPageMask.PageMaskProvided = true;
962+
g_testContext.ArchSHMTypeToPageMask.ReturnValue = OS_EOK;
963+
964+
// 6. MemorySpaceMap, this is the most interesting call to check, as that
965+
// needs to contain the expected setup for the virtual region
966+
g_testContext.MemorySpaceMap.Calls[0].ExpectedSHMTag = 0x20;
967+
g_testContext.MemorySpaceMap.Calls[0].CheckSHMTag = true;
968+
g_testContext.MemorySpaceMap.Calls[0].ExpectedLength = 0x83C0; // 0x8400-64
969+
g_testContext.MemorySpaceMap.Calls[0].CheckLength = true;
970+
g_testContext.MemorySpaceMap.Calls[0].ExpectedFlags = MAPPING_COMMIT | MAPPING_PERSISTENT | MAPPING_USERSPACE;
971+
g_testContext.MemorySpaceMap.Calls[0].CheckFlags = true;
972+
g_testContext.MemorySpaceMap.Calls[0].ExpectedPlacement = MAPPING_VIRTUAL_PROCESS;
973+
g_testContext.MemorySpaceMap.Calls[0].CheckPlacement = true;
974+
g_testContext.MemorySpaceMap.Calls[0].ReturnedMapping = 0x40000000;
975+
g_testContext.MemorySpaceMap.Calls[0].ReturnedMappingProvided = true;
976+
g_testContext.MemorySpaceMap.Calls[0].ReturnValue = OS_EOK;
977+
978+
// We fake that the buffer is conformed. It does not matter which conformity
979+
// we test with, as we mock the translation call. In this case the PageMask
980+
// will be 0xFFFFF, which will cover our pages.
981+
oserr = SHMConform(
982+
handle.ID,
983+
&(SHMConformityOptions_t) {
984+
.BufferAlignment = 128,
985+
.Conformity = OSMEMORYCONFORMITY_LOW,
986+
},
987+
0,
988+
SHM_ACCESS_READ,
989+
64,
990+
handle.Length,
991+
&conformedHandle
992+
);
993+
assert_int_equal(oserr, OS_EOK);
994+
assert_int_equal(conformedHandle.ID, 0x20);
995+
assert_int_equal(conformedHandle.SourceID, 0x10); // This is now cloned
996+
assert_int_equal(conformedHandle.SourceFlags, 0);
997+
assert_ptr_equal(conformedHandle.Buffer, 0x40000000);
998+
assert_int_equal(conformedHandle.Capacity, 0x83C0);
999+
assert_int_equal(conformedHandle.Length, 0x83C0);
1000+
assert_int_equal(conformedHandle.Offset, 64);
1001+
1002+
// When a handle is already conformed, SHMConform will just attach
1003+
// and map. So verify this happened as we expected
1004+
// Ensure the right number of calls were made.
1005+
assert_int_equal(g_testContext.CreateHandle.CallCount, 2);
1006+
assert_int_equal(g_testContext.ArchSHMTypeToPageMask.Calls, 1);
1007+
assert_int_equal(g_testContext.AcquireHandleOfType.Calls, 1);
1008+
assert_int_equal(g_testContext.GetMemorySpaceMapping.Calls, 1);
1009+
assert_int_equal(g_testContext.MemorySpaceMap.CallCount, 1);
1010+
TeardownTest(state);
1011+
}
1012+
1013+
void TestSHMConform_NotMemoryConformed(void** state)
8981014
{
8991015
oserr_t oserr;
9001016
SHMHandle_t handle;
@@ -977,7 +1093,10 @@ void TestSHMConform_NotConformed(void** state)
9771093
// will be 0xFFFFF, which will cover our pages.
9781094
oserr = SHMConform(
9791095
handle.ID,
980-
OSMEMORYCONFORMITY_LOW,
1096+
&(SHMConformityOptions_t) {
1097+
.BufferAlignment = 0,
1098+
.Conformity = OSMEMORYCONFORMITY_LOW,
1099+
},
9811100
0,
9821101
SHM_ACCESS_READ,
9831102
0,
@@ -1122,7 +1241,10 @@ void TestSHMConform_NotConformedFilledOnCreation(void** state)
11221241
// will be 0xFFFFF, which will cover our pages.
11231242
oserr = SHMConform(
11241243
handle.ID,
1125-
OSMEMORYCONFORMITY_LOW,
1244+
&(SHMConformityOptions_t) {
1245+
.BufferAlignment = 0,
1246+
.Conformity = OSMEMORYCONFORMITY_LOW,
1247+
},
11261248
SHM_CONFORM_FILL_ON_CREATION,
11271249
SHM_ACCESS_READ,
11281250
0,
@@ -1255,7 +1377,10 @@ void TestSHMConform_NotConformedBackfilledOnUnmap(void** state)
12551377
// will be 0xFFFFF, which will cover our pages.
12561378
oserr = SHMConform(
12571379
handle.ID,
1258-
OSMEMORYCONFORMITY_LOW,
1380+
&(SHMConformityOptions_t) {
1381+
.BufferAlignment = 0,
1382+
.Conformity = OSMEMORYCONFORMITY_LOW,
1383+
},
12591384
SHM_CONFORM_BACKFILL_ON_UNMAP,
12601385
SHM_ACCESS_READ,
12611386
0,
@@ -1664,7 +1789,8 @@ int main(void)
16641789
cmocka_unit_test_setup(TestSHMExport_NotPageAligned, SetupTest),
16651790
cmocka_unit_test_setup(TestSHMExport_PRIVATE, SetupTest),
16661791
cmocka_unit_test_setup(TestSHMConform_IsConformed, SetupTest),
1667-
cmocka_unit_test_setup(TestSHMConform_NotConformed, SetupTest),
1792+
cmocka_unit_test_setup(TestSHMConform_NotAlignmentConformed, SetupTest),
1793+
cmocka_unit_test_setup(TestSHMConform_NotMemoryConformed, SetupTest),
16681794
cmocka_unit_test_setup(TestSHMConform_NotConformedFilledOnCreation, SetupTest),
16691795
cmocka_unit_test_setup(TestSHMConform_NotConformedBackfilledOnUnmap, SetupTest),
16701796
cmocka_unit_test_setup(TestSHMAttach_Simple, SetupTest),

librt/libos/include/os/shm.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ SHMExport(
6868
CRTDECL(oserr_t,
6969
SHMConform(
7070
_In_ uuid_t shmID,
71-
_In_ enum OSMemoryConformity conformity,
71+
_In_ SHMConformityOptions_t* conformity,
7272
_In_ unsigned int flags,
7373
_In_ unsigned int access,
7474
_In_ size_t offset,

librt/libos/include/os/types/shm.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,15 @@ typedef struct SHMSGTable {
124124
int Count;
125125
} SHMSGTable_t;
126126

127+
typedef struct SHMConformityOptions {
128+
// The alignment the buffer must have to be conformed. An alignment of 0
129+
// means no alignment is required. The alignment must *always* be a power
130+
// of two.
131+
uint32_t BufferAlignment;
132+
// The memory conformity is the location in physical memory that the memory
133+
// range should reside. This is useful for drivers to specify requirements
134+
// in terms of backwards compatability.
135+
enum OSMemoryConformity Conformity;
136+
} SHMConformityOptions_t;
137+
127138
#endif //!__OS_TYPES_SHM_H__

librt/libos/include/os/types/syscall.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919
#define __TYPES_SYSCALL_H__
2020

2121
#include <os/types/memory.h>
22-
#include <os/types/time.h>
2322
#include <os/types/syslog.h>
23+
#include <os/types/shm.h>
24+
#include <os/types/time.h>
2425
#include <time.h> // for clock_t
2526

2627
typedef struct OSKernelLogEntry {
@@ -49,7 +50,7 @@ typedef struct OSFutexParameters {
4950
} OSFutexParameters_t;
5051

5152
typedef struct OSSHMConformParameters {
52-
enum OSMemoryConformity Conformity;
53+
SHMConformityOptions_t* Conformity;
5354
unsigned int Flags;
5455
unsigned int Access;
5556
size_t Offset;

librt/libos/shm.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ SHMExport(
159159
oserr_t
160160
SHMConform(
161161
_In_ uuid_t shmID,
162-
_In_ enum OSMemoryConformity conformity,
162+
_In_ SHMConformityOptions_t* conformity,
163163
_In_ unsigned int flags,
164164
_In_ unsigned int access,
165165
_In_ size_t offset,

modules/filesystems/common/include/fs/common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
* of all filesystem context structures.
2828
*/
2929
struct FSBaseContext {
30+
uint32_t IOBufferAlignment;
3031
enum OSMemoryConformity IOConformity;
3132
};
3233

modules/filesystems/common/requests.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,10 @@ __MapUserBufferRead(
256256
{
257257
return SHMConform(
258258
handle,
259-
fsBaseContext->IOConformity,
259+
&(SHMConformityOptions_t) {
260+
.BufferAlignment = fsBaseContext->IOBufferAlignment,
261+
.Conformity = fsBaseContext->IOConformity
262+
},
260263
SHM_CONFORM_BACKFILL_ON_UNMAP,
261264
SHM_ACCESS_READ | SHM_ACCESS_WRITE,
262265
0,
@@ -311,7 +314,10 @@ __MapUserBufferWrite(
311314
{
312315
return SHMConform(
313316
handle,
314-
fsBaseContext->IOConformity,
317+
&(SHMConformityOptions_t) {
318+
.BufferAlignment = fsBaseContext->IOBufferAlignment,
319+
.Conformity = fsBaseContext->IOConformity
320+
},
315321
SHM_CONFORM_FILL_ON_CREATION,
316322
SHM_ACCESS_READ,
317323
0,

modules/filesystems/common/storage.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,11 @@ FSBaseContextInitialize(
4848
if (oserr != OS_EOK) {
4949
return oserr;
5050
}
51+
fsBaseContext->IOBufferAlignment = ioRequirements.BufferAlignment;
5152
fsBaseContext->IOConformity = ioRequirements.Conformity;
5253
} break;
5354
case VFSSTORAGE_TYPE_FILE: {
55+
fsBaseContext->IOBufferAlignment = 0;
5456
fsBaseContext->IOConformity = OSMEMORYCONFORMITY_NONE;
5557
} break;
5658
}

0 commit comments

Comments
 (0)