[Kernel] Keep oversized reservations at the guest's hint and sign-extend user event ids (#1073)
* [Kernel] Keep an oversized reservation at the guest's hint instead of failing A 768 GB sceKernelReserveVirtualRange with a hint at 0x1000000000 cannot use the sparse aperture (it starts at 256 GB, and 256 GB plus 768 GB passes the 1008 GB user limit), so it needed one contiguous host placeholder. When a host allocation made before the startup reservation sits inside that window, the call fails with 0x8002000C and the guest stops at boot. Reserve such a range at the guest's own hint as a guest-only sparse range, as smaller large reservations already are. * [Kernel] Sign-extend the int id of user events into the event ident sceKernelAddUserEvent, sceKernelAddUserEventEdge, sceKernelDeleteUserEvent and sceKernelTriggerUserEvent take an int id, which the kernel stores in the uintptr_t ident of the event. A negative id such as -1 therefore reads back from sceKernelGetEventId as 0xFFFFFFFFFFFFFFFF. The exports used the zero-extended register (0x00000000FFFFFFFF), so a guest that treats a negative id as a wake-up event took its normal event path instead and never went back to wait. Use the sign-extended id in all four exports.
This commit is contained in:
parent
f32266834c
commit
46a7d3a446
|
|
@ -558,6 +558,9 @@ public static class KernelEventQueueCompatExports
|
|||
: (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// The user event id is an int that the kernel stores in a uintptr_t ident, so -1 becomes 0xFFFFFFFFFFFFFFFF.
|
||||
private static ulong UserEventIdent(CpuContext ctx) => unchecked((ulong)(int)ctx[CpuRegister.Rsi]);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "WDszmSbWuDk",
|
||||
ExportName = "sceKernelAddUserEventEdge",
|
||||
|
|
@ -568,7 +571,7 @@ public static class KernelEventQueueCompatExports
|
|||
var handle = ctx[CpuRegister.Rdi];
|
||||
var registered = RegisterEvent(
|
||||
handle,
|
||||
ctx[CpuRegister.Rsi],
|
||||
UserEventIdent(ctx),
|
||||
KernelEventFilterUser,
|
||||
0,
|
||||
KernelEventFlagClear);
|
||||
|
|
@ -588,7 +591,7 @@ public static class KernelEventQueueCompatExports
|
|||
var handle = ctx[CpuRegister.Rdi];
|
||||
var registered = RegisterEvent(
|
||||
handle,
|
||||
ctx[CpuRegister.Rsi],
|
||||
UserEventIdent(ctx),
|
||||
KernelEventFilterUser,
|
||||
0,
|
||||
flags: 0);
|
||||
|
|
@ -608,7 +611,7 @@ public static class KernelEventQueueCompatExports
|
|||
var handle = ctx[CpuRegister.Rdi];
|
||||
var deleted = DeleteRegisteredEvent(
|
||||
handle,
|
||||
ctx[CpuRegister.Rsi],
|
||||
UserEventIdent(ctx),
|
||||
KernelEventFilterUser);
|
||||
TraceEventQueue(ctx, "delete_user", handle);
|
||||
return deleted
|
||||
|
|
@ -626,7 +629,7 @@ public static class KernelEventQueueCompatExports
|
|||
var handle = ctx[CpuRegister.Rdi];
|
||||
var triggered = TriggerRegisteredEvent(
|
||||
handle,
|
||||
ctx[CpuRegister.Rsi],
|
||||
UserEventIdent(ctx),
|
||||
KernelEventFilterUser,
|
||||
userData: ctx[CpuRegister.Rdx]);
|
||||
TraceEventQueue(ctx, "trigger_user", handle);
|
||||
|
|
|
|||
|
|
@ -494,6 +494,9 @@ public static partial class KernelMemoryCompatExports
|
|||
if (candidate > UserAddressLimit - length)
|
||||
{
|
||||
candidate = AlignUp(SparseReservationBase, alignment);
|
||||
// A range too large for the aperture can only live at the guest's own hint.
|
||||
if (candidate > UserAddressLimit - length)
|
||||
candidate = AlignUp(requested, alignment);
|
||||
if (candidate == 0 || candidate > UserAddressLimit - length)
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -318,6 +318,25 @@ public sealed class KernelBackedMemoryTests
|
|||
Assert.Equal((address, address + 0x4000), test.Query(address));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReservationTooLargeForTheSparseApertureKeepsTheGuestHint()
|
||||
{
|
||||
using var test = new BackedKernelMemory();
|
||||
const ulong hint = 0x1000000000;
|
||||
const ulong size = 0xC000000000;
|
||||
test.Host.FailReserveHoleWhen = (_, requestedSize) => requestedSize >= size;
|
||||
Assert.True(test.Context.TryWriteUInt64(test.Output, hint));
|
||||
test.Context[CpuRegister.Rdi] = test.Output;
|
||||
test.Context[CpuRegister.Rsi] = size;
|
||||
test.Context[CpuRegister.Rdx] = 0;
|
||||
test.Context[CpuRegister.Rcx] = 0x200000;
|
||||
|
||||
Assert.Equal(0, KernelRuntimeCompatExports.KernelReserveVirtualRange(test.Context));
|
||||
Assert.True(test.Context.TryReadUInt64(test.Output, out var address));
|
||||
Assert.Equal(hint, address);
|
||||
Assert.Equal((hint, hint + size), test.Query(hint));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddressSearchUsesTheFullReservationExtent()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -110,24 +110,27 @@ public sealed class KernelEventQueueCompatExportsTests
|
|||
// the queued event with the registered ident/filter and the trigger data.
|
||||
// DequeueEvents runs before the blocking path, so a pre-triggered queue
|
||||
// returns immediately without touching the guest thread scheduler.
|
||||
[Fact]
|
||||
public void CreateAddTriggerWait_DeliversTriggeredUserEvent()
|
||||
// The id is an int that the kernel stores in a uintptr_t ident, so -1 (0xFFFFFFFF in the register)
|
||||
// is delivered as 0xFFFFFFFFFFFFFFFF.
|
||||
[Theory]
|
||||
[InlineData(0x4242UL, 0x4242UL)]
|
||||
[InlineData(0xFFFF_FFFFUL, ulong.MaxValue)]
|
||||
public void CreateAddTriggerWait_DeliversTriggeredUserEvent(ulong id, ulong eventIdent)
|
||||
{
|
||||
const ulong eventIdent = 0x4242;
|
||||
const ulong triggerData = 0x55AA_55AA;
|
||||
var handle = CreateEqueue();
|
||||
|
||||
// Register the user event on the queue.
|
||||
var (addCtx, _) = NewContextWithOutSlot();
|
||||
addCtx[CpuRegister.Rdi] = handle;
|
||||
addCtx[CpuRegister.Rsi] = eventIdent;
|
||||
addCtx[CpuRegister.Rsi] = id;
|
||||
Assert.Equal((int)OrbisGen2Result.ORBIS_GEN2_OK,
|
||||
KernelEventQueueCompatExports.KernelAddUserEvent(addCtx));
|
||||
|
||||
// Trigger it with a distinct data payload.
|
||||
var (triggerCtx, _) = NewContextWithOutSlot();
|
||||
triggerCtx[CpuRegister.Rdi] = handle;
|
||||
triggerCtx[CpuRegister.Rsi] = eventIdent;
|
||||
triggerCtx[CpuRegister.Rsi] = id;
|
||||
triggerCtx[CpuRegister.Rdx] = triggerData;
|
||||
Assert.Equal((int)OrbisGen2Result.ORBIS_GEN2_OK,
|
||||
KernelEventQueueCompatExports.KernelTriggerUserEvent(triggerCtx));
|
||||
|
|
|
|||
Loading…
Reference in New Issue