Форум программистов, компьютерный форум, киберфорум
Программирование драйверов
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.73/15: Рейтинг темы: голосов - 15, средняя оценка - 4.73
7 / 7 / 0
Регистрация: 05.08.2023
Сообщений: 49

Как работать с жестким диском на уровне эквивалетном уровню IRQL DISPATCH или выше?

01.09.2023, 19:25. Показов 5070. Ответов 41

Студворк — интернет-сервис помощи студентам
Всем привет!
Я пишу свой гипервизор и у меня всё-таки возникла необходимость после выхода из гостевой VM писать что-либо на жесткий диск.

строка 55
Кликните здесь для просмотра всего текста
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
#include <ntddk.h>
 
#include "GuestContext.hpp"
#include "Logger/LogSpin.hpp"
#include "include/HyperVisor/HyperVisor.hpp"
 
HyperVisorSvm objHyperVisorSvm; 
Log objLog;
UINT64 Counter = 0;
 
#define LogInfo(format, ...)  \
    objLog.LogSendMessageToQueue(OPERATION_LOG_INFO_MESSAGE, false, false, "[+] Information (%s:%d) | " format "\n",    \
         __func__, __LINE__, __VA_ARGS__)
 
#define LogInfoImmediate(format, ...)  \
    objLog.LogSendMessageToQueue(OPERATION_LOG_INFO_MESSAGE, TRUE, false, "[+] Information (%s:%d) | " format "\n", \
         __func__, __LINE__, __VA_ARGS__)
 
#define LogWarning(format, ...)  \
    objLog.LogSendMessageToQueue(OPERATION_LOG_WARNING_MESSAGE, TRUE, false, "[-] Warning (%s:%d) | " format "\n",  \
        __func__, __LINE__, __VA_ARGS__)
 
#define LogError(format, ...)  \
    objLog.LogSendMessageToQueue(OPERATION_LOG_ERROR_MESSAGE, TRUE, false, "[!] Error (%s:%d) | " format "\n",  \
         __func__, __LINE__, __VA_ARGS__);  \
        DbgBreakPoint()
 
// Log without any prefix
#define Log(format, ...)  \
    objLog.LogSendMessageToQueue(OPERATION_LOG_INFO_MESSAGE, false, false, format "\n", __VA_ARGS__)
 
SVM::PRIVATE_VM_DATA* Interceptions(
    _Inout_ SVM::PRIVATE_VM_DATA* Private)
{
    Private->Guest.ControlArea.InterceptCpuid = TRUE;
    Private->Guest.ControlArea.InterceptVmrun = TRUE;
    //Private->Guest.ControlArea.InterceptExceptions.Bitmap.InterceptionVectorDB = TRUE;
    Private->Guest.ControlArea.InterceptMsr = TRUE;
    Private->Guest.ControlArea.MsrpmBasePa = reinterpret_cast<UINT64>(PhysicalMemory::GetPhysicalAddress(&Private->Msrpm));
 
    return Private;
}
 
void InjectEvent(__out SVM::VMCB* Guest, unsigned char Vector, unsigned char Type, unsigned int Code)
{
    SVM::EVENTINJ Event = {};
    Event.Bitmap.Vector = Vector;
    Event.Bitmap.Type = Type;
    Event.Bitmap.ErrorCodeValid = TRUE;
    Event.Bitmap.Valid = TRUE;
    Event.Bitmap.ErrorCode = Code;
    Guest->ControlArea.EventInjection = Event.Value;
}
 
extern "C" SVM::VMM_STATUS SvmVmexitHandler(
    _In_ SVM::PRIVATE_VM_DATA * Private,
    _In_ GuestContext * Context)
{
    // Load the host state:
    __svm_vmload(reinterpret_cast<size_t>(Private->VmmStack.Layout.InitialStack.HostVmcbPa));
 
    // Restore the guest's RAX that was overwritten by host's RAX on #VMEXIT:
    Context->Rax = Private->Guest.StateSaveArea.Rax;
 
    SVM::VMM_STATUS Status = SVM::VMM_STATUS::VMM_CONTINUE; 
    LogInfo("ExitCode %x\n", Private->Guest.ControlArea.ExitCode);
    LogInfo("Counter is %p\n", Counter);
    ++Counter;
 
    switch (Private->Guest.ControlArea.ExitCode)
    {
    case SVM::SVM_EXIT_CODE::VMEXIT_CPUID://== 0x72h
    {
        CPUID_REGS Regs = {};
        int Function = static_cast<int>(Context->Rax);
        int SubLeaf = static_cast<int>(Context->Rcx);
        __cpuidex(Regs.Raw, Function, SubLeaf);
 
        if (Function == CPUID_VMM_SHUTDOWN) { Status = SVM::VMM_STATUS::VMM_SHUTDOWN; }
        else 
        {
            Context->Rax = Regs.Regs.Eax;
            Context->Rbx = Regs.Regs.Ebx;
            Context->Rcx = Regs.Regs.Ecx;
            Context->Rdx = Regs.Regs.Edx;
        }
        break;
    }
    case SVM::SVM_EXIT_CODE::VMEXIT_MSR:
    {
        if ((Context->Rcx & MAXUINT32) == static_cast<unsigned int>(AMD::AMD_MSR::MSR_EFER) && Private->Guest.ControlArea.ExitInfo1)
        {
            AMD::EFER Efer = {};
            Efer.Value = ((Context->Rdx & MAXUINT32) << 32) | (Context->Rax & MAXUINT32);
            if (!Efer.Bitmap.SecureVirtualMachineEnable)
            {
                InjectEvent(&Private->Guest, INTERRUPT_VECTOR::GeneralProtection, EXCEPTION_VECTOR::FaultTrapException, 0); // #GP (Vector = 13, Type = Exception)
                break;
            }
            Private->Guest.StateSaveArea.Efer = Efer.Value;
        }
        break;
    }
    case SVM::SVM_EXIT_CODE::VMEXIT_VMRUN:
    {
        InjectEvent(&Private->Guest, INTERRUPT_VECTOR::GeneralProtection, EXCEPTION_VECTOR::FaultTrapException, 0); // #GP (Vector = 13, Type = Exception)
        break;
    }
    }
    
    KdPrint(("Exit code %p\n", Private->Guest.ControlArea.ExitCode));
 
    Private->Guest.StateSaveArea.Rax = Context->Rax;
 
    // Go to the next instruction:
    Private->Guest.StateSaveArea.Rip = Private->Guest.ControlArea.NextRip;
 
    return Status;
}
 
//Define in asm file(in my example)
extern "C" void SvmVmmRun(_In_ void* InitialVmmStackPointer);
 
void DrvUnload(_In_ PDRIVER_OBJECT DriverObj)
{
    UNREFERENCED_PARAMETER(DriverObj);
    KdPrint(("Sample driver Unload called\n"));
}
 
NTSTATUS DrvUnsupported(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    UNREFERENCED_PARAMETER(DeviceObject);
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
 
    return STATUS_SUCCESS;
}
 
NTSTATUS DrvCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    UNREFERENCED_PARAMETER(DeviceObject);
    objHyperVisorSvm.PInterceptions = &Interceptions;
    objHyperVisorSvm.PSvmVmmRun = &SvmVmmRun;
 
    if (objHyperVisorSvm.IsSvmSupported()) { LogInfoImmediate("Hypervisor start =)\n"); objHyperVisorSvm.VirtualizeAllProcessors(); }
 
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
 
    return STATUS_SUCCESS;
}
 
NTSTATUS DrvDispatchIoControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    PIO_STACK_LOCATION  IrpStack;
    PREGISTER_EVENT RegisterEvent;
    NTSTATUS    Status;
    UNREFERENCED_PARAMETER(DeviceObject);
 
    IrpStack = IoGetCurrentIrpStackLocation(Irp);
 
    switch (IrpStack->Parameters.DeviceIoControl.IoControlCode)
    {
    case IOCTL_REGISTER_EVENT:
    {
        if (IrpStack->Parameters.DeviceIoControl.InputBufferLength < SIZEOF_REGISTER_EVENT || Irp->AssociatedIrp.SystemBuffer == NULL) {
            Status = STATUS_INVALID_PARAMETER;
            break;
        }
 
        RegisterEvent = (PREGISTER_EVENT)Irp->AssociatedIrp.SystemBuffer;
 
        switch (RegisterEvent->Type)
        {
        case IRP_BASED:
            Status = objLog.LogRegisterIrpBasedNotification(DeviceObject, Irp);
            break;
        case EVENT_BASED:
            Status = objLog.LogRegisterEventBasedNotification(DeviceObject, Irp);
            break;
        default:
            Status = STATUS_INVALID_PARAMETER;
            break;
        }
        break;
    }
    case IOCTL_BUFFER_CHECK:
    {
        RtlCopyBytes(Irp->AssociatedIrp.SystemBuffer, &objLog.BufferIsReady, sizeof(bool));
        Irp->IoStatus.Information = sizeof(bool);
        Irp->IoStatus.Status = STATUS_SUCCESS;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        Status = STATUS_SUCCESS;
 
        break;
    }
    case IOCTL_BUFFER_CHECK_SUCCESS:
    {
        objLog.BufferIsReady = FALSE;
        Irp->IoStatus.Status = STATUS_SUCCESS;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        Status = STATUS_SUCCESS;
 
        break;
    }
    }
    return STATUS_SUCCESS;
}
 
NTSTATUS DrvClose(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    UNREFERENCED_PARAMETER(DeviceObject);
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
 
    return STATUS_SUCCESS;
}
 
extern "C" NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
    UNREFERENCED_PARAMETER(RegisterPath); NTSTATUS Ntstatus = STATUS_SUCCESS;
    PDEVICE_OBJECT DeviceObject = NULL; UNICODE_STRING DriverName, DosDeviceName;
 
    DbgPrint("[*] LogInitialize is started !\n");
    if (!objLog.LogInitialize()) { DbgPrint("[*] Log buffer is not initialized !\n"); DbgBreakPoint(); }
    DbgPrint("[*] LogInitialize done !\n");
    RtlInitUnicodeString(&DriverName, L"\\Device\\MyHypervisorDevice");
 
    RtlInitUnicodeString(&DosDeviceName, L"\\DosDevices\\MyHypervisorDevice");
    Ntstatus = IoCreateDevice(DriverObject, 0, &DriverName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject);
 
    if (Ntstatus == STATUS_SUCCESS)
    {
        for (UINT64 Index = 0; Index < IRP_MJ_MAXIMUM_FUNCTION; Index++) { DriverObject->MajorFunction[Index] = DrvUnsupported; }
 
        DriverObject->MajorFunction[IRP_MJ_CLOSE] = DrvClose;
        DriverObject->MajorFunction[IRP_MJ_CREATE] = DrvCreate;
        DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DrvDispatchIoControl;
        DriverObject->DriverUnload = DrvUnload;
        IoCreateSymbolicLink(&DosDeviceName, &DriverName);
    }
 
    return STATUS_SUCCESS;
}


https://github.com/Nitr0-G/SVM... in.cpp#L55

Когда я выхожу из гостевой VM и попадаю к себе, то я перехожу к SVMExitHandler(выше показан пример. Раскидываются коды аналогично того как это делает debug main loop по ExitCode, но только я нахожусь на уровне гипервизора, т.е. по сути на Ring -1 и управляю эксепшионами своего ЦП)

Пример с картинкой:



По сути я нахожусь на том месте, который я выделил красным квадратом. Моя задача писать из определенного ExitCode, к примеру DB Exception(single step трассировка) на жесткий диск (строка 70)
(https://github.com/Nitr0-G/SVM... in.cpp#L70)

Использование ZwWriteFile и т.п. API приводит к BSOD из-за того, что они хоть и вызываются формально на IRQL 0, но по сути я нахожусь на максимальном IRQL. В аналогии с Intel это называется VMX root mode(https://docs.hyperdbg.org/tips... -root-mode) у меня соответственно SVM root mode. Так вот встаёт главный вопрос, как драйвер для тех же sata жестких дисков выполняют на них запись? А ещё один вопрос: как происходит запись на RamDisk? Где я могу посмотреть возможные реализации того, что мне нужно и почитать теорию? Т.е. по сути мне нужно написать свою максимально низкоуровневую ZwWriteFile.

P.S.
Делаю это как свой проект. Сначала я пытаюсь вирутализировать свою систему, потом на этих наработках попытаюсь сделать свой гипервизор типа VirtualBox и т.п. с расширенными возможностями и меньшим детектом того, что Винда работает на гипервизоре типа VmWare со стороны системы и различных вирусов.
P.P.S.
Надеюсь на любую помощь:
Начиная с названий различных книг, которые мне помогут в решении проблемы выше и, заканчивая предложением того, что я могу пореверсить для того, чтобы понять как оно работает.
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
01.09.2023, 19:25
Ответы с готовыми решениями:

Материнская плата работает с одним жестким диском и отказывается работать с другим
Всем доброго времени суток. Случилось такое, что материнская плата напрочь отказывается работать с рабочим жестким диском, но при...

Компьютер самостоятельно начинает работать с жестким диском после 20-30 минут простоя
Добрый день. Включенный компьютер, после 20-30 минут простоя начинает активно работать с жестким диском (с характерным звуком и...

Проблемы с компом или жестким диском
Проблема заключается вот в чем: Подключив жесткий диск с довольно старого компа, в первый раз при загрузке, комп его показал, но через...

41
Эксперт Hardware
Эксперт Hardware
 Аватар для R71MT
6215 / 2449 / 403
Регистрация: 29.07.2014
Сообщений: 3,181
Записей в блоге: 4
03.09.2023, 22:32
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Nitro-G Посмотреть сообщение
Не во всех.
именно во-всех,
просто ваш Bus:device:Function 00:31:00 указан в 10-тичном, а в hex это будет 00:1F:00
руткит по ссылки выше бесполезно парсить - он заточен под чипсеты Интела с константами - вот фрагмент из него:
C++
1
2
3
4
5
6
7
   // Значение RCBA можно прочитать из регистра конфигурации ICH PCI B:D:F = 0:31:0, смещение 0xF0.
   // Устройство LPC в ICH, B:D:F = 0:31:0
   
   lpc_rcba_addr = pci_addr(0, 31, 0, LPC_RCBA_REG);
   _outpd(0xcf8, lpc_rcba_addr);
   rcba_reg = _inpd(0xcfc);
   pa.LowPart = rcba_reg & 0xffffc000;
а доки от AMD полистаю.. может найдём в них что-нибудь полезное.
1
7 / 7 / 0
Регистрация: 05.08.2023
Сообщений: 49
03.09.2023, 22:56  [ТС]
Цитата Сообщение от R71MT Посмотреть сообщение
именно во-всех,
просто ваш Bus:device:Function 00:31:00 указан в 10-тичном, а в hex это будет 00:1F:00
Извиняюсь! Я подумал, что они 31 писали в hex...
Цитата Сообщение от R71MT Посмотреть сообщение
руткит по ссылки выше бесполезно парсить - он заточен под чипсеты Интела с константами - вот фрагмент из него:
Вас понял!
Цитата Сообщение от R71MT Посмотреть сообщение
а доки от AMD полистаю.. может найдём в них что-нибудь полезное.
Вас понял! Спасибо! Я там им ещё реквест насчет доков для чипсета оформил. Возможно, что они ответят...
0
Эксперт Hardware
Эксперт Hardware
 Аватар для R71MT
6215 / 2449 / 403
Регистрация: 29.07.2014
Сообщений: 3,181
Записей в блоге: 4
03.09.2023, 23:21
Вот общий план действий, чтобы подобраться к регистрам ATA/SATA..
Известно, что контролёры всех вторичных девайсов висят на шине PCI. Для хранения свойств этих девайсов (в том числе и регистров) используется "PCI Config Space", или просто конфигурационное пространство. Для PciCfg биос выделяет блок вирт.памяти в самом верхнем диапазоне 32-битного адреса, т.е. 4Gb - xx. Но с приходом PCI-Express архитектура чипсетов изменилась, и теперь обычная шина PCI подключается через мост Bridge и стала дочерней по отношению к PCIe. То-есть схема стала такая:



Соответственно, чтобы подобраться к какому-либо устройству, нам нужно знать адрес Root-комплекса, или RCBA. Сам Root это тоже девайс, и как все имеет свои регистры, которые ходят под ником RCRB или "Root Complex Registers Block". Таким образом, в любом случае нужно получить базу рута RCBA, через которую уже можно читать регистры любого девайса из конфиг.пространста Pci.

Возьмём к примеру PciCfg контролёра SATA. По смещению от 10h до 24h в любом PciCfg лежат 6 регистров BAR девайса, или "Base Addr Reg". Значения в этих полях указываются как смещения относительно RCBA, в результате получаем дерево, с корнем "RootComplex". Если-бы в BAR лежали не смещения RVA от базы, а конкретные вирт.адреса VA, то проблем-бы не было - просто читаешь их, и попадаешь сразу в область порта регистров.

Вот например BAR'ы моего контролёра диска из "PCIScope" pciAddr = 00:1F:02. Вы можете у себя в утилите "RW" выбрать из выпадающего списка "SATA Controller" и сравнить их со-своими значениями, учитывая размеры полей (удобно выставить их в 8-бит, а не 32):

Кликните здесь для просмотра всего текста
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
Offset Value     Name                                                         Type  Access                 
 
00h    8086h     Vendor ID                                                    WORD  Read only              
02h    27C0h     Device ID                                                    WORD  Read only              
04h    0005h     Command Register                                             WORD  Mixed                  
06h    02B0h     Status Register                                              WORD  Mixed                  
08h    01h       Revision ID                                                  BYTE  Read only              
09h    8Fh       Programming Interface                                        BYTE  Read only              
0Ah    01h       Subclass                                                     BYTE  Read only              
0Bh    01h       Class Code                                                   BYTE  Read only              
0Ch    00h       Cache Line Size                                              BYTE  Read / Write           
0Dh    00h       Latency Timer                                                BYTE  Read / Write           
0Eh    00h       Header Layout                                                BYTE  Read only              
0Fh    00h       BIST - Built-in Self Test                                    BYTE  Mixed                  
10h    0000B401h PCMD_BAR - Primary Command Block Base Address Register       DWORD Mixed            
14h    0000B081h PCNL_BAR - Primary Control Block Base Address Register       DWORD Mixed                  
18h    0000B001h SCMD_BAR - Secondary Command Block Base Address Register     DWORD Mixed                  
1Ch    0000AC01h SCNL_BAR - Secondary Control Block Base Address Register     DWORD Mixed                  
20h    0000A881h BM_BASE - Bus Master Base Address Register                   DWORD Mixed                  
24h    00000000h Non AHCI Capable                                             DWORD Read / Write           
28h    00000000h Reserved                                                     DWORD Mixed                  
2Ch    1462h     SVID - Subsystem Vendor Identification Register              WORD  Read / Write           
2Eh    7529h     SID - Subsystem Identification Register                      WORD  Read / Write           
30h    00000000h Reserved                                                     DWORD Mixed                  
34h    70h       CAP - Capabilities Pointer Register                          BYTE  Read only              
35h    00h       Reserved                                                     BYTE  Mixed                  
36h    0000h     Reserved                                                     WORD  Mixed                  
38h    00000000h Reserved                                                     DWORD Mixed                  
3Ch    13h       INT_LN - Interrupt Line Register                             BYTE  Read / Write           
3Dh    02h       INTR_PN - Interrupt Pin Register                             BYTE  Read only              
3Eh    0000h     Reserved                                                     WORD  Mixed                  
40h    A303h     IDE_TIMP - Primary IDE Timing Register                       WORD  Read / Write           
42h    8000h     IDE_TIMS - Secondary IDE Timing Register                     WORD  Read / Write           
44h    00h       SLV_IDETIM - Slave (Drive 1) IDE Timing Register             BYTE  Read / Write           
45h    00h       Reserved                                                     BYTE  Mixed                  
46h    0000h     Reserved                                                     WORD  Mixed                  
48h    01h       SDMA_CNT - Synchronous DMA Control Register                  BYTE  Read / Write           
49h    00h       Reserved                                                     BYTE  Mixed                  
4Ah    0001h     SDMA_TIM - Synchronous DMA Timing Register                   WORD  Read / Write           
4Ch    00000000h Reserved                                                     DWORD Mixed                  
50h    00000000h Reserved                                                     DWORD Mixed                  
54h    00001030h IDE_CONFIG - IDE I/O Configuration Register                  DWORD Read / Write           
58h    00000000h Reserved                                                     DWORD Mixed                  
5Ch    00000000h Reserved                                                     DWORD Mixed                  
60h    00000000h Reserved                                                     DWORD Mixed                  
64h    00000000h Reserved                                                     DWORD Mixed                  
68h    00000000h Reserved                                                     DWORD Mixed                  
6Ch    00000000h Reserved                                                     DWORD Mixed                  
70h    01h       Cap_ID - Capability Identifier                               BYTE  Read only              
71h    00h       Next_Item_Ptr - Next Item Pointer                            BYTE  Read only              
72h    4002h     PMC - Power Management Capabilities                          WORD  Read only              
74h    0000h     PMCSR - Power Management Control/Status                      WORD  Mixed                  
76h    00h       Data Register                                                BYTE  Read only              
78h    00000000h Reserved                                                     DWORD Mixed                  
7Ch    00000000h Reserved                                                     DWORD Mixed                  
80h    7005h     MSICI - Message Signaled Interrupt Capability Identification WORD  Read only              
82h    0000h     MSIMC - Message Signaled Interrupt Message Control           WORD  Read / Write           
84h    00000000h MSIMA - Message Signaled Interrupt Message Address           DWORD Mixed                  
88h    0000h     MSIMD - Message Signaled Interrupt Message Data              WORD  Read / Write           
8Ah    0000h     Reserved                                                     WORD  Mixed                  
8Ch    00000000h Reserved                                                     DWORD Mixed                  
90h    00h       MAP - Address Map Register                                   BYTE  Read / Write           
91h    00h       Reserved                                                     BYTE  Mixed                  
92h    001Fh     PCS - Port Control and Status Register                       WORD  Mixed                  
94h    40000180h SIR - SATA Initialization Register                           DWORD Read / Write           
9Ch    00000000h Reserved                                                     DWORD Mixed                  
A0h    00h       SIRI - SATA Indexed Registers Index                          BYTE  Read / Write           
A1h    00h       Reserved                                                     BYTE  Mixed                  
A2h    0000h     Reserved                                                     WORD  Mixed                  
A4h    00000000h STRD - SATA Indexed Register Data                            DWORD Read only              
A8h    00000000h SCAP0 - SATA Capability Register 0                           DWORD Read only              
ACh    00000000h SCAP1 - SATA Capability Register 1                           DWORD Read only              
B0h    00000000h Reserved                                                     DWORD Mixed                  
B4h    00000000h Reserved                                                     DWORD Mixed                  
B8h    00000000h Reserved                                                     DWORD Mixed                  
BCh    00000000h Reserved                                                     DWORD Mixed                  
C0h    00h       ATC - APM Trapping Control Register                          BYTE  Read / Write           
C1h    00h       Reserved                                                     BYTE  Mixed                  
C2h    0000h     Reserved                                                     WORD  Mixed                  
C4h    05h       ATS - APM Trapping Status Register                           BYTE  Read / Write one clear 
C5h    00h       Reserved                                                     BYTE  Mixed                  
C6h    0000h     Reserved                                                     WORD  Mixed                  
C8h    00000000h Reserved                                                     DWORD Mixed                  
CCh    00000000h Reserved                                                     DWORD Mixed                  
D0h    00000000h SP - Scratch Pad Register                                    DWORD Read / Write           
D4h    00000000h Reserved                                                     DWORD Mixed                  
D8h    00000000h Reserved                                                     DWORD Mixed                  
DCh    00000000h Reserved                                                     DWORD Mixed                  
E0h    00000000h BFCS - BIST FIS Control/Status Register                      DWORD Mixed                  
E8h    00000000h BFTD2 - BIST FIS Transmit Data2 Register                     DWORD Read / Write           
ECh    00000000h Reserved                                                     DWORD Mixed                  
F0h    00000000h Reserved                                                     DWORD Mixed                  
F4h    00000000h Reserved                                                     DWORD Mixed                  
F8h    00010F86h Reserved                                                     DWORD Mixed                  
FCh    00000000h Reserved                                                     DWORD Mixed
1
Эксперт Hardware
Эксперт Hardware
 Аватар для R71MT
6215 / 2449 / 403
Регистрация: 29.07.2014
Сообщений: 3,181
Записей в блоге: 4
03.09.2023, 23:44
По сути, можно без проблем прочитать cfg любого девайса в системе, и собрать таким образом все сведения о них. Опознать девайс можно по его кодам: "Class, Sub-Class, Prog-Interface". Например для контролёров SATA они будут равны 01018Fh, для моста PCI-to-LPC 060100h и т.д. Только какой от этого прок, если у нас нет значения RCBA?

Драйвера читают значения Cfg или функцией HalGetBusDataByOffset(), или-же обратившись напрямую к диспетчеру PnP с мажорным кодом IRP_MJ_PNP, и минорными IRP_MN_QUERY_INTERFACE + IRP_MN_READ_CONFIG.

C++
1
2
3
4
5
6
7
8
NTHALAPI ULONG HalGetBusDataByOffset(
  [in] BUS_DATA_TYPE BusDataType,     ;// = PCIConfig
  [in] ULONG         BusNumber,       ;// = bus
  [in] ULONG         SlotNumber,      ;// = def + func
  [in] PVOID         Buffer,          ;// = 
  [in] ULONG         Offset,          ;// = смещение поля в PciCfg
  [in] ULONG         Length           ;// = размер буфера
);
1
7 / 7 / 0
Регистрация: 05.08.2023
Сообщений: 49
04.09.2023, 00:19  [ТС]
Спасибо за столь подробное объяснение!
Цитата Сообщение от R71MT Посмотреть сообщение
Только какой от этого прок, если у нас нет значения RCBA?
Да, надобно как-нибудь бы его получить... Я в док-тах именно о RCBA особо ничего не нашел
0
Эксперт Hardware
Эксперт Hardware
 Аватар для R71MT
6215 / 2449 / 403
Регистрация: 29.07.2014
Сообщений: 3,181
Записей в блоге: 4
04.09.2023, 00:37
Цитата Сообщение от Nitro-G Посмотреть сообщение
Да, надобно как-нибудь бы его получить...
Помоему в Linux'e есть какая-то команда, которая возвращает RCBA.
Можно найти кореша у кого линух (ну или поставить его на виртуалку самому), и запросить эту команду. Мне резона нет, т.к. сижу на чипсете Intel, а вот вам - как вариант. Хотя это разовое решение, только для тестов на лок.машине.

Интересным является поле в PciCfg по смещению(4) "Command Register". Это маска, в которой зарыта базовая инфа о девайсе. Например, если не установлен бит(1), то обмен через MMIO (Memory Mapped I/O) не возможен. Расшифровывается этот регистр так:

1
7 / 7 / 0
Регистрация: 05.08.2023
Сообщений: 49
04.09.2023, 00:41  [ТС]
Цитата Сообщение от R71MT Посмотреть сообщение
Помоему в Linux'e есть какая-то команда, которая возвращает RCBA.
Можно найти кореша у кого линух (ну или поставить его на виртуалку самому), и запросить эту команду. Мне резона нет, т.к. сижу на чипсете Intel, а вот вам - как вариант. Хотя это разовое решение, только для тестов на лок.машине.
У меня убунта сейчас на виртуалке крутится. Сейчас попробую найти подобную команду...
Цитата Сообщение от R71MT Посмотреть сообщение
Хотя это разовое решение, только для тестов на лок.машине.
Хмм, так там должна быть и реализация считывания RCBA, если там есть такая команда - и функция. Надо будет только в коде найти и переписать под винду и msvc.
1
Эксперт Hardware
Эксперт Hardware
 Аватар для R71MT
6215 / 2449 / 403
Регистрация: 29.07.2014
Сообщений: 3,181
Записей в блоге: 4
04.09.2023, 01:29
Можно посмотреть на это дело с иного ракурса.. например позвать на помощь драйвер, которому подсунуть пакет "IRP_MJ_WRITE". Только нужно разобраться, какому именно драйверу, ведь в стеке их много. Для этого воспользуемся отладчиком, чтобы получить иерархию драйверов диска в стеке. Известно, что если продвигаться от юзера к физ.диску, то драйвера расположены в порядке: disk.sys --> atapi.sys --> psiide.sys. Между каждым из этих драйверов вклинивается ещё и acpi.sys для контроля питания и прочей лабуды - он представляет собой фильтр-драйвер FiDO, и нам не нужен.

Значит запрашиваем объекты трёх/этих драйверов, после чего сразу просматриваем стек их устройств. Стек конкретного драйвера представляется отладчиком так, что первым в списке будет идти драйвер верхнего уровня (ближе к юзеру), и далее всё ближе к железу - смотрим:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
0: kd> !drvobj disk
   Device Object list: fffffa80027b24d0  fffffa80027b1060
 
0: kd> !devstack fffffa80027b24d0
  !DevObj           !DrvObj           ObjectName
  ----------------------------------------------
  fffffa80027b3b90  \Driver\partmgr   
> fffffa80027b24d0  \Driver\Disk      DR1
  fffffa80022fd520  \Driver\ACPI      
  fffffa8002693680  \Driver\atapi     IdeDeviceP2T1L0-4
 
  !DevNode fffffa800235d900: "IDE\Disk_ST380215A__3.AAC__\5&171d8404&0&0.1.0"
  ServiceName is "disk"
Как видим, порядок идёт: partmgr.sys --> disk.sys --> atapi.sys.
смотрим оставшиеся 2..
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
0: kd> !drvobj atapi
   Device Object list: fffffa80025d8050  fffffa80025d3050
                       fffffa800235a050  fffffa800219c050
 
0: kd> !devstack fffffa80025d8050
  !DevObj           !DrvObj           ObjectName
  ----------------------------------------------
> fffffa80025d8050  \Driver\atapi     IdePort3
  fffffa80022e2b20  \Driver\ACPI      
  fffffa80022e2d40  \Driver\pciide    PciIde1Channel1
 
  !DevNode fffffa80022e3d90: "PCIIDE\IDEChannel\4&132d2fd4&0&1"
  ServiceName is "atapi"
 
;///*****************************************************
 
0: kd> !drvobj pciide
   Device Object list: fffffa80022e2060
 
0: kd> !devstack fffffa80022e2060
  !DevObj           !DrvObj           ObjectName
  ----------------------------------------------
> fffffa80022e2060  \Driver\pciide    PciIde1
  fffffa80018797f0  \Driver\ACPI      
  fffffa800187b060  \Driver\pci       PNP_PCI0012
 
  !DevNode fffffa8002262010: "PCI\VEN_8086&DEV_27DF&SUBSYS_75291462&REV_01\3&11583659&0&F9"
  ServiceName is "pciide"
Здесь мы упёрлись в pci.sys,
а это уже драйвер шины, за которой пристально следит диспетчер PNP, на предмет подключенных к ней новых устройств.

Теперь посмотрим, какой из перечисленных драйверов способен обслуживать коды "IRP_MJ_WRITE", т.к. посылать их остальным драйверам бесполезно - они тупо проигнорируют запрос. Здесь из логов я удалил всё лишнее и как оказалось, запрос на запись поддерживает только disk.sys, а значит функцией IoAttachToDeviceStack() нужно вклиниваться именно в его стек:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
0: kd> !drvobj disk 2
  DriverEntry  :  fffff8800198106c  disk
  DriverStartIo:  00000000
  DriverUnload :  fffff880019a8cc0  CLASSPNP
  AddDevice    :  fffff880019aaf50  CLASSPNP
 
  Dispatch routines:
  [00] IRP_MJ_CREATE                  fffff880019869e0   CLASSPNP+0x19e0
  [02] IRP_MJ_CLOSE                   fffff880019869e0   CLASSPNP+0x19e0
  [03] IRP_MJ_READ                    fffff880019869e0   CLASSPNP+0x19e0
  [04] IRP_MJ_WRITE                   fffff880019869e0   CLASSPNP+0x19e0  <---- Наш клиент!
  [09] IRP_MJ_FLUSH_BUFFERS           fffff880019869e0   CLASSPNP+0x19e0
  [0e] IRP_MJ_DEVICE_CONTROL          fffff880019869e0   CLASSPNP+0x19e0
  [0f] IRP_MJ_INTERNAL_DEVICE_CONTROL fffff880019869e0   CLASSPNP+0x19e0
  [10] IRP_MJ_SHUTDOWN                fffff880019869e0   CLASSPNP+0x19e0
  [16] IRP_MJ_POWER                   fffff880019869e0   CLASSPNP+0x19e0
  [17] IRP_MJ_SYSTEM_CONTROL          fffff880019869e0   CLASSPNP+0x19e0
  [1b] IRP_MJ_PNP                     fffff880019869e0   CLASSPNP+0x19e0
 
;///********************************************************************
 
0: kd> !drvobj atapi 2
  DriverEntry  :  fffff88000e3e064  atapi
  DriverStartIo:  00000000
  DriverUnload :  fffff880010d8514  ataport
  AddDevice    :  fffff880010da484  ataport
 
  Dispatch routines:
  [00] IRP_MJ_CREATE                  fffff880010dd880   ataport+0x21880
  [02] IRP_MJ_CLOSE                   fffff880010dd880   ataport+0x21880
  [0e] IRP_MJ_DEVICE_CONTROL          fffff880010c3500   ataport+0x7500
  [0f] IRP_MJ_INTERNAL_DEVICE_CONTROL fffff880010c34d8   ataport+0x74d8
  [16] IRP_MJ_POWER                   fffff880010c3528   ataport+0x7528
  [17] IRP_MJ_SYSTEM_CONTROL          fffff880010d84e0   ataport+0x1c4e0
  [1b] IRP_MJ_PNP                     fffff880010d84ac   ataport+0x1c4ac
 
;///********************************************************************
 
0: kd> !drvobj pciide 2
  DriverEntry  :  fffff88000e05064  pciide
  DriverStartIo:  00000000
  DriverUnload :  fffff88000ff6b20  PCIIDEX
  AddDevice    :  fffff88000ff6dd8  PCIIDEX
 
  Dispatch routines:
  [16] IRP_MJ_POWER                   fffff88000ff0c7c   PCIIDEX+0x2c7c
  [17] IRP_MJ_SYSTEM_CONTROL          fffff88000ff6aec   PCIIDEX+0x8aec
  [1b] IRP_MJ_PNP                     fffff88000ff6ab8   PCIIDEX+0x8ab8
Добавлено через 8 минут
Цитата Сообщение от Nitro-G Посмотреть сообщение
Сейчас попробую найти подобную команду...
вроде нужно копать в сторону lspci -s 00:14.0 -x = дамп моста LPC, и ключ -v в хвосте для его лога.
1
7 / 7 / 0
Регистрация: 05.08.2023
Сообщений: 49
04.09.2023, 06:10  [ТС]
Цитата Сообщение от R71MT Посмотреть сообщение
Можно посмотреть на это дело с иного ракурса.. например позвать на помощь драйвер, которому подсунуть пакет "IRP_MJ_WRITE". Только нужно разобраться, какому именно драйверу, ведь в стеке их много. Для этого воспользуемся отладчиком, чтобы получить иерархию драйверов диска в стеке. Известно, что если продвигаться от юзера к физ.диску, то драйвера расположены в порядке: disk.sys --> atapi.sys --> psiide.sys. Между каждым из этих драйверов вклинивается ещё и acpi.sys для контроля питания и прочей лабуды - он представляет собой фильтр-драйвер FiDO, и нам не нужен.
Это вы рассматриваете второй пункт. Хмм.
Т.е. вы предлагаете аттачнуться к стеку драйверов disk.sys. Далее сформировать DrvWrite функцию внутри своего драйвера, которая условно будет

C++
1
        DriverObject->MajorFunction[IRP_MJ_WRITE] = DrvWrite;
Но внутри disk.sys уже есть данная реализация и вроде бы всё равно если клиент будет условно вызывать ZwWriteFile/WriteFile, то disk.sys будет её перехватывать, т.е. нужно как-то внутри него пропатчить вызов его DrvWrite и редиректить на свой или же сделать свою функцию, которая будет дергать мой DrvWrite, а далее каким-то образом пересылать ему какую-нибудь data.Мне просто как-то сложно понять как это будет происходить чисто с технической стороны написания кода, а также того как мы будем подсовывать пакет IRP_MJ_WRITE, а до этого это ещё надо как-то всё дополнительно обработать...
0
7 / 7 / 0
Регистрация: 05.08.2023
Сообщений: 49
06.09.2023, 12:50  [ТС]
В общем, сделал я себе дривер(отдельный), который вроде бы и работает и я могу напрямую читать/писать через него в сектора, но в самом гипервизоре эта реализация тоже не работает xD Делаю как говорил R71MT, но только я не подключаюсь к стеку драйверов.
Сам дривер скину позже, так как сейчас ещё реализации тестирую. В теории можно ввести свою систему оповещения и как-то писать во втором потоке, но только нужно свой KeWaitForSingleObject будет сделать...

Добавлено через 1 минуту
Цитата Сообщение от Nitro-G Посмотреть сообщение
В общем, сделал я себе дривер(отдельный), который вроде бы и работает и я могу напрямую читать/писать через него в сектора, но в самом гипервизоре эта реализация тоже не работает xD Делаю как говорил R71MT, но только я не подключаюсь к стеку драйверов.
Сам дривер скину позже, так как сейчас ещё реализации тестирую. В теории можно ввести свою систему оповещения и как-то писать во втором потоке, но только нужно свой KeWaitForSingleObject будет сделать...
Либо нужно писать свои реализации:
IoBuildSynchronousFsdRequest
IoCallDriver
0
Эксперт Hardware
Эксперт Hardware
 Аватар для R71MT
6215 / 2449 / 403
Регистрация: 29.07.2014
Сообщений: 3,181
Записей в блоге: 4
06.09.2023, 14:13
Nitro-G, блин оказывается напутал я всё..
База рута RCBA нужна только для доступа к регистрам самого чиспета (и устройств на шине LPC), а база портов SATA прописывается уже в его личном конфигурационном пространстве. То-есть для прямых операций с секторами диска из ядра, достаточно лишь найти блоки cfg контроллёров ATA/SATA в общем кфг.пространстве системы. Поскольку "Device Configuration Space" это часть спецификации шины PCI, работа с ним не зависит от типа чипсета, будь то Intel, или AMD. Нюансы я оформил в стиле FAQ для себя, но если нужно, могу выложить и сюда.
1
7 / 7 / 0
Регистрация: 05.08.2023
Сообщений: 49
06.09.2023, 21:30  [ТС]
Цитата Сообщение от R71MT Посмотреть сообщение
Nitro-G, блин оказывается напутал я всё..
База рута RCBA нужна только для доступа к регистрам самого чиспета (и устройств на шине LPC), а база портов SATA прописывается уже в его личном конфигурационном пространстве. То-есть для прямых операций с секторами диска из ядра, достаточно лишь найти блоки cfg контроллёров ATA/SATA в общем кфг.пространстве системы. Поскольку "Device Configuration Space" это часть спецификации шины PCI, работа с ним не зависит от типа чипсета, будь то Intel, или AMD. Нюансы я оформил в стиле FAQ для себя, но если нужно, могу выложить и сюда.
Да, если у вас есть возможность выкладывайте... Так как через disk.sys мало, что работает, а точнее ничего в случае гипервизора, так как всё равно надо API дергать...

Добавлено через 2 минуты
Т.е. геометрию диска и т.п. моменты я ещё получить могу, так как я могу это сделать до входа в гипервизор, а вот далее я формирую такой код для чтения к примеру:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
            ULONG OutputBuffLen = (pDiskGeometries[0]->BytesPerSector); // By default output size is sector size
            ULONG InputBuffLen = sizeof(DISK_LOCATION);
            PVOID pBuff = (PVOID)ExAllocatePool(NonPagedPool, pDiskGeometries[0]->BytesPerSector);
            unsigned char byte = 0;
 
            ULONG MajorFunc = IRP_MJ_READ;
            LARGE_INTEGER lDiskOffset; KEVENT Event; IO_STATUS_BLOCK IoStatusBlock;
 
            lDiskOffset.QuadPart = (pDiskGeometries[0]->BytesPerSector) * (0);
            KeInitializeEvent(&Event, NotificationEvent, FALSE);
 
            PIRP pIrp = IoBuildSynchronousFsdRequest(MajorFunc, pDevicesObjects[0], (PVOID)&byte,
                sizeof(byte), &lDiskOffset,
                &Event, &IoStatusBlock);
 
            IoCallDriver(pDevicesObjects[0], pIrp);
 
            KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Ну и я моментально улетаю в синьку по нашествию KeWaitForSingleObject, а если его убрать, то и работать особо ничего не будет...

Добавлено через 47 минут
Цитата Сообщение от R71MT Посмотреть сообщение
То-есть для прямых операций с секторами диска из ядра, достаточно лишь найти блоки cfg контроллёров ATA/SATA в общем кфг.пространстве системы.
И да, как я их получить-то могу и обращаться к ним? Я с напрямую с этим никогда не работал... Ну собственно из-за этого и спрашиваю: где можно что-нибудь почитать об этом?

Добавлено через 10 минут
Просто согласно тому документу, который вы мне скидывали, а именно: SATA_Controller.pdf(TMS320C674x/OMAP-L1x Processor
Serial ATA (SATA) Controller), то они там на 19-ой странице настраивают FIS, atapi commands and etc. Но мне это не нужно, мне нужно это как-то из венды получить...
0
7 / 7 / 0
Регистрация: 05.08.2023
Сообщений: 49
07.09.2023, 07:52  [ТС]
Да я всё-таки прав. В SvmExitcodeHandler есть ровно такой же SVM root mode как и в Intel Vmx root mode

https://github.com/tandasat/Si... m.cpp#L756


Так что надо всё-таки как-то напрямую работать с портами SATA. Использование любых(не any IRQL API) ведет к BSOD'у

Добавлено через 3 минуты
Также надо как-то обходиться без
IoBuildSynchronousFsdRequest
IoCallDriver

Так как они тоже ведут к BSOD'у, а соответственно делать толковую пересылку IRP пакета на запись или чтение как я это делаю в коде выше я также не могу.

Добавлено через 1 минуту
В память я складывать данные также не могу, так как хоть у меня и 80ГБ озу, но на описание всех структур в ASM коде и по сути построения файла трассировки у меня памяти не хватит...

Добавлено через 3 минуты
Хмм я нашел такое
https://learn.microsoft.com/en... tion-space

Также там пару статеек я нашел о написание драйверов под девайсы, как их почитаю и пойму, что толковые скину сюда. Возможно, даже переведу. Также скину сегодня вечером свою отдельную реализацию read/write sectors, но там отправляются IRP пакеты в Disk.sys для этого, а => что мне тоже это не подойдет ну и соответственно не подошло, так как уже проверил.

Добавлено через 15 минут
У меня в read/write sectors драйвере также есть поддержка CPP в кернеле. Так что на гитхаб, скорее всего, выложу, так как кода много
1
Эксперт Hardware
Эксперт Hardware
 Аватар для R71MT
6215 / 2449 / 403
Регистрация: 29.07.2014
Сообщений: 3,181
Записей в блоге: 4
07.09.2023, 09:19
Nitro-G, в скрепке лежит набор утилит.
Запустите из него CPUZ и на вкладке "О программе" сохраните отчёт "Как TXT-файл" (не html). В этом логе будут дампы конфиг.пространства PCI всех устройств вашего чипсета. Скиньте сюда этот лог, чтобы я сравнил структуру cfg-пространства AMD, со-своим Intel. Они должны совпадать, но всё-же..
Вложения
Тип файла: zip CPLDAPU.ZIP (4.22 Мб, 2 просмотров)
1
7 / 7 / 0
Регистрация: 05.08.2023
Сообщений: 49
07.09.2023, 12:53  [ТС]
Цитата Сообщение от R71MT Посмотреть сообщение
Nitro-G, в скрепке лежит набор утилит.
Запустите из него CPUZ и на вкладке "О программе" сохраните отчёт "Как TXT-файл" (не html). В этом логе будут дампы конфиг.пространства PCI всех устройств вашего чипсета. Скиньте сюда этот лог, чтобы я сравнил структуру cfg-пространства AMD, со-своим Intel. Они должны совпадать, но всё-же..
Вот

Report.txt.7z
0
7 / 7 / 0
Регистрация: 05.08.2023
Сообщений: 49
08.09.2023, 01:40  [ТС]
Цитата Сообщение от Nitro-G Посмотреть сообщение
Также там пару статеек я нашел о написание драйверов под девайсы, как их почитаю и пойму, что толковые скину сюда. Возможно, даже переведу. Также скину сегодня вечером свою отдельную реализацию read/write sectors, но там отправляются IRP пакеты в Disk.sys для этого, а => что мне тоже это не подойдет ну и соответственно не подошло, так как уже проверил.
https://github.com/Nitr0-G/DiskWriter
1
7 / 7 / 0
Регистрация: 05.08.2023
Сообщений: 49
10.09.2023, 14:10  [ТС]
R71MT, Вы сможете пожалуйста ответить или у вас времени не хватает?


Я тут сделал реализацию записи, но столкнулся с проблемами, которые описывал выше, а именно:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
void Trace::AcceptMessage(File& objFile)
{
    if (Rips.size() >= FrameSize)
    {
        WCHAR ValueBuffer[56];
        UNICODE_STRING Value;
 
        uint64_t* RipsTemp = static_cast<uint64_t*>(ExAllocatePool(PagedPool, FrameSize * sizeof(uint64_t)));
        RtlZeroMemory(RipsTemp, FrameSize * sizeof(uint64_t));
        for (int Rip = 0; Rip < FrameSize; ++Rip)
        {
            if (!Rips[Rip].Used)
            {
                RipsTemp[Rip] = Rips[Rip].u64Rip;
                Rips[Rip].Used = true;
            }
        }
 
        RtlInitEmptyUnicodeString(&Value, ValueBuffer, sizeof(ValueBuffer));
        for (int Rip = 0; Rip < FrameSize; ++Rip)//const auto& Rip : RipsTemp)
        {
            RtlInt64ToUnicodeString(RipsTemp[Rip], 16, &Value);
            RtlAppendUnicodeToString(&Value, L"\r\n");//"\r\n"
 
            objFile.WriteFile(Value);
            RtlZeroMemory(ValueBuffer, sizeof(ValueBuffer));
            RtlInitEmptyUnicodeString(&Value, ValueBuffer, sizeof(ValueBuffer));
        }
        ExFreePool(RipsTemp);
    }
    return;
}
extern UINT64 Counter;
void Trace::TraceRip(_In_ SVM::PRIVATE_VM_DATA* Private)
{
    Rips.erase(std::remove_if(Rips.begin(), Rips.end(), [](const Rip& obj) {
        return obj.Used;
        }), Rips.end());
 
    
    Rip objRip(false, Private->Guest.StateSaveArea.Rip); 
    Rips.push_back(objRip);
 
    return;
}
TraceRip метод работает с максимальным IRQL фактически на ядре, а второй метод AcceptMessage работает с минимальный IRQL, так как вызывается в IOCTL. Ну и собственно я тут прямо сталкиваюсь с проблемой буферизации, хоть я применил методологию Linux при высоких IRQL, т.е. один поток пишущий, а другой читающий(https://web.archive.org/web/20... index.html)

Я не могу использовать семафоры на методе TraceRip, так как тогда я занимаю эксклюзивно одно ядро и система падает, так как думает, что что-то не так. Что делать в таких случаях? По хорошему это
C++
1
2
3
    Rips.erase(std::remove_if(Rips.begin(), Rips.end(), [](const Rip& obj) {
        return obj.Used;
        }), Rips.end());
надо перенести в метод AcceptMessage, так как из-за кода с проверкой выше очень сильно теряю в скорости при попытке загружать в буфер большой объем данных. Хотя я сейчас попробую сделать проверку от нулевого элемента(т.е. если там Used == true, то заходить в цикл, а иначе пропускать), но это костыль...

Добавлено через 1 час 33 минуты
Цитата Сообщение от Nitro-G Посмотреть сообщение
Хотя я сейчас попробую сделать проверку от нулевого элемента(т.е. если там Used == true, то заходить в цикл, а иначе пропускать), но это костыль...
Я переписал код до такой шляпы

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void Trace::AcceptMessage(File& objFile)
{
    if (Rips.size() >= FrameSize)
    {
        UNICODE_STRING Value;
        WCHAR ValueBuffer[56];
        RtlInitEmptyUnicodeString(&Value, ValueBuffer, sizeof(ValueBuffer));
 
        for (auto& rip : Rips)
        {
            if (rip.Used)
                continue;
 
            rip.Used = true;
            RtlInt64ToUnicodeString(rip.u64Rip, 16, &Value);
            RtlAppendUnicodeToString(&Value, L"\r\n");
            objFile.WriteFile(Value);
            RtlZeroMemory(ValueBuffer, sizeof(ValueBuffer));
        }
    }
}
 
void Trace::TraceRip(_In_ SVM::PRIVATE_VM_DATA* Private)
{
    if (Rips.size() > FrameSize)
    {
        Rips.erase(std::remove_if(Rips.begin(), Rips.end(), [](const Rip& obj) {
            return obj.Used;
            }), Rips.end());
    }
 
    Rips.emplace_back(false, Private->Guest.StateSaveArea.Rip);
}
Но тут:
1)Есть пропуски в записи
2)Из-за первого пункта можно уже выкинуть в мусорку этот вариант

Но код выше в данный момент самый быстрый, который можно получить из кода ещё выше. Так что думать надо как ускорить запись, так как трассировка таким образом происходит не за 30сек, а за 6 минут, а это вообще не подходит... Какие есть идеи? ZwWriteFile отрабатывает на RamDisk, так что быстрее диска просто существовать в данный момент не может.

Кстати, а к чему относится примечание (https://learn.microsoft.com/ru... _pool_type) "только для внутреннего использования.". Что они подразумевают "внутренним использованием" в контексте драйвера? Я думал просто сделать запись в кэш, было бы быстрее...

Добавлено через 3 минуты
И насколько ограничены ресурсы NonPagedPool памяти? У меня, когда вектор начинает потреблять больше 100мибов вылет в синьку происходит...
0
Эксперт Hardware
Эксперт Hardware
 Аватар для R71MT
6215 / 2449 / 403
Регистрация: 29.07.2014
Сообщений: 3,181
Записей в блоге: 4
10.09.2023, 18:35
Цитата Сообщение от Nitro-G Посмотреть сообщение
Вы сможете пожалуйста ответить или у вас времени не хватает?
немного занят был.. позже отвечу.
Цитата Сообщение от Nitro-G Посмотреть сообщение
так как из-за кода с проверкой выше очень сильно теряю в скорости при попытке загружать в буфер большой объем данных.
насколько большой объём? вроде говорилось что-то около 500 КБ.
Цитата Сообщение от Nitro-G Посмотреть сообщение
И насколько ограничены ресурсы NonPagedPool памяти? У меня, когда вектор начинает потреблять больше 100мибов вылет в синьку происходит
Non-Paged это пул невыгружаемых в своп страниц. Их ввели потому, что ошибки отсутствия страниц "Page-Fault" не могут обслуживаться на уровне DPC/Dispatch и выше. Поэтому почти все страницы ядра невыгружаемые. Сначала система создаёт 4 выгружаемых пула (объединяются в общий), и 2 невыгружаемых. Доп.создаются в зависимости от количества NUMA-узлов в системе. Исходный размер Non-Paged определяется объёмом физ.памяти ОЗУ, и составляет ~10% от неё. Затем по мере необходимости пул может расширяться до макс.75%, что для х32 = 2ГБ, а для х64 = 128 ГБ. Текущие значения пулов можно посмотреть в "ProcessExplorer" (View->SysInfo), или командой !vm в отладчике WinDbg:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
0: kd> !vm
*** Virtual Memory Usage ***
         Current:   2096376 Kb   Free Space:   1649988 Kb
         Minimum:   2096376 Kb   Maximum:      6289128 Kb
 
Unimplemented error for MiSystemVaTypeCount
        Available Pages:        186331 (    745324 Kb)
        ResAvail Pages:         463485 (   1853940 Kb)
        Locked IO Pages:             0 (         0 Kb)
        Free System PTEs:     33557391 ( 134229564 Kb)
        Modified Pages:          12339 (     49356 Kb)
        Modified PF Pages:       11681 (     46724 Kb)
        
        NonPagedPool Usage:   99919224 ( 399676896 Kb)  
        NonPagedPoolNx Usage:    10017 (     40068 Kb)
        NonPagedPool Max:       382463 (   1529852 Kb)
 
        ********** Excessive NonPaged Pool Usage *****
        
        PagedPool 0 Usage:       32155 (    128620 Kb)
        PagedPool 1 Usage:        3496 (     13984 Kb)
        PagedPool 2 Usage:        1209 (      4836 Kb)
        PagedPool 3 Usage:        1151 (      4604 Kb)
        PagedPool 4 Usage:        1208 (      4832 Kb)
        PagedPool Usage:         39219 (    156876 Kb)
        PagedPool Maximum:    33554432 ( 134217728 Kb)
        
        Session Commit:           8456 (     33824 Kb)
        Shared Commit:           58057 (    232228 Kb)
        Special Pool:                0 (         0 Kb)
        Shared Process:           5845 (     23380 Kb)
        PagedPool Commit:        39234 (    156936 Kb)
        Driver Commit:            9171 (     36684 Kb)
        Committed pages:        627400 (   2509600 Kb)
        Commit limit:          1048188 (   4192752 Kb)
1
Эксперт Hardware
Эксперт Hardware
 Аватар для R71MT
6215 / 2449 / 403
Регистрация: 29.07.2014
Сообщений: 3,181
Записей в блоге: 4
10.09.2023, 22:50
Не уверен, что данный пост поможет чем-нибудь вам, но поскольку обещал, то выкладываю.
Я уже говорил, что создавал доку для себя, и оформил её в виде FAQ.

.:: Общие сведения по работе с портами устройств из ядра ::.
Известно, что обращения к портам происходит инструкциями ассемблера IN/OUT, от куда собственно и взялся термин I/O. Эти инструкции входят в состав привилегированных и доступны только из режима процессора “Real-Mode”, или нулевого кольца ОС. Библиотека ядра HAL.DLL раньше имела с десяток функций для этих нужд, но на х64 их вроде урезали. При дизассемблировании выясняется, что эти функции просто обёртка к инструкциям IN/OUT. Как и подобает функциям _cdecl, аргументы в виде адреса и номера порта передаются им через стек, а если ввод-вывод буферизованный, то ещё и значение счётчика + адрес источника/приёмника. Судя по всему, вызов этих функций даже не попадает в системную очередь DPC, а потому доступен с любого IRQL выше 1. Система просто не знает, что мы обращаемся к портам, а значит и не в состоянии их отследить.
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
* Export: WRITE_PORT_ULONG, 005B
 
8001893C:  8B542404         mov    edx, [esp+04]
80018940:  8B442408         mov    eax, [esp+08]
80018944:  EF               out    dx,  eax
80018945:  C20800           ret    0008
 
* Export: READ_PORT_ULONG, 0055
 
800188D0:  8B542404         mov    edx, [esp+04]
800188D4:  ED               in     eax, dx
800188D5:  C20400           ret    0004
 
* Export: READ_PORT_BUFFER_ULONG, 0052
 
80018908:  8BC7             mov    eax, edi
8001890A:  8B542404         mov    edx, [esp+04]
8001890E:  8B7C2408         mov    edi, [esp+08]
80018912:  8B4C240C         mov    ecx, [esp+0C]
80018916:  F36D             rep    insd 
80018918:  8BF8             mov    edi, eax
8001891A:  C20C00           ret    000C
По логике вещей, убрав эти вызовы из системы Microsoft должна была восполнить пробел аналогичными. Нужно просто ознакомиться с экспортом Hal.dll и Ntoskrnlpa.exe. Но в любом случае, если функции для работы с портами когда-то и были, значит ничто не мешает использовать их и сейчас. Мало-ли что там Microsoft не советует.. Пока существуют девайсы, порты ввода-вывода были, есть и будут (тем-более, что они открывают огромные возможности). Тут главное понять суть, а реализовать уже можно что угодно.


.:: Как в системной памяти ОЗУ найти адрес конфиг.пространства устройств? ::.
Всякое устройство имеет свои требования к системе – производитель зашивает их в ПЗУ своих девайсов. После процедуры POST, биос создаёт карту обнаруженных устройств и собирает их запросы в своём конфиг.пространстве. Каждой тушке выделяется блок памяти размером строго FFh=256 байт, куда биос тут-же прописывает и назначенные устройству ресурсы, типа номеров прерываний и т.п.

Но у современных девайсов требований к системе гораздо больше, а потому после загрузки, ОС копирует весь cfg-space из области биос к себе в вирт.память. Кому не достаточно базовых 256-байт (привет девайсы PCI-Express), система выделят дополнительно по их запросам (иногда 4 и более Кбайт). При этом базовый блок у всех остаётся на месте, и выступает теперь в качестве заголовка.

Процессорам х86 доступны 65535 физ.портов ввода-вывода с номерами 0000-FFFFh, два из которых адресуют именно “Конфиг.пространство устройств” на шине PCI (далее cfg). Номер порта должен всегда указываться в 16-битном регистре DX.

1. Порт 0CF8h = CONFIG_ADDRESS. Вкл/отключает доступ ПО к cfg и определяет, в какую именно его часть нужно обратиться с запросом на RW. Значение должно содержать 32-битный адрес Long в формате BDFR: Bus:Device:Function:Register.
Пример записи: mov eax,BDFR --> mov edx,0cf8h --> out dx,eax.

2. Порт 0CFCh = CONFIG_DATA. Это 32-битное окно чтения/записи в пространство сfg. Адрес определяет содержимое порта CONFIG_ADDRESS. Пример чтения: mov edx,0cfсh --> in eax,dx.

В спецификации на PCI представлена топология шины, где определяется макс.значения BDFR:

• всего шин(В)=256 (8-бит). К первичной шине(0) чз мосты "PCI-to-PCI Bridge" подключаются вторичные.
• устройств(D) на одной шине=32 (5-бит). Это контроллёры на материнской плате.
• функций(F)=8 (3-бита). Подключённое к контроллёру устройство, например тот-же "Mass-Storage" ATA/SATA/SCSI.
• регистров(R)=64 (6-бит). Хранит смещение в 4-байтных словах, для выбора поля в cfg устройства. 64х4=256-байтный базовый блок.

При оформлении CONFIG_ADDRESS из 4-х этих составляющих, нужно обязательно установить в нём старший бит(31), который разрешает доступ к конфиг.пространству системы. Если он останется сброшенным, процессор тупо проигнорирует запрос. Это касается и двух младших бит[1:0]. Во-первых они определяют тип запроса и должны быть сброшены в нуль, а во-вторых выравнивают значение регистра(R) на 4-байтную границу. В силу того, что размеры некоторых полей меньше 8-бит, полный адрес нужно формировать путём сдвигов влево, ну или применять логическое сложение OR по маске.



.:: Зоопарк PCI конфиг.пространств в системе ::.
Выше упоминалось, что мостам и устройствам PCI-Express система выделяет более 256-байт пространства.
В спецификации на шину PCI приводится всего три типа пространств – оно указывается в 1-байтном поле “HeaderType” каждого из cfg. Типичными значениями для этого поля являются 00, 01, 80 и 81h:



Однако под номер регистра “Register Number” в CONFIG_ADDRESS отводится всего 6-бит, что ограничивает число адресуемых 32-битных регистров значением: 26=64. Поэтому позже это поле расширили до 10-бит, выделив для них из резерва ещё 4-бита. В результате имеем уже 210=1024 адресуемых регистра в общем пространстве cfg, что в сумме даёт: 1024х4=4096 байт. Первый механизм доступа обозвали PCI-CAM или “Configuration Access Mechanism”, а второй – расширенный ECAM (Enhanced).



На рис.ниже представлен формат конфиг.пространства хост-контроллёра SATA.
По сути, основную инфу можно вытащить и обычным САМ-механизмом, но за пределами первых 256-байт в двойном слове(90h) имеется и поле “PCS”, значение в котором хранит состояние портов SATA на мат.плате куда подключаем шлейфы. Биты[3:0] в нём отображают настройки биос вкл/выкл портов, а биты[7:4] – на каком именно из них висит клиент. Значимые поля в общем пространстве я выделил здесь цветом. Слева – это стандартный блок размером 256-байт, а справа – фрагмент расширенного:



.:: Как в цикле поиска найти пространство контроллёров ATA/SATA? ::.
Каждое устройство имеет свой класс, код которого лежит по смещению 0Bh=11d в его cfg.
Поскольку в CONFIG_ADDRESS значение “Register Number” (далее R) позволяет читать из cfg только с шагом в 4-байта, то задаём ему значение R=2, в результате чего получим байты 8-11. Для устройств хранения данных предписан класс(1), а подкласс и интерфейс уточняют, какое именно это устройство:
Code
1
2
3
4
5
6
7
8
9
10
11
Storage class-codes (Class:Subclass:Interface)
-----------------------------------------------------
SCSI   =  01:00:00    Small Computer System Interface
ATA    =  01:01:xx    IDE
RAID   =  01:04:00    ATA/SATA Domain
SATA   =  01:06:00    IDE
SATA   =  01:06:01    AHCI
SAS    =  01:07:00    Serial Attached SCSI
NVMe   =  01:08:xx    SSD (Non Voltage Memory PCIe)
UFS    =  01:09:xx    Universal Flash Storage
Other  =  01:80:00    Unknown
На рис.ниже представлены поля в конфиг.пространстве моего SATA где видно,
что он имеет classCode = 01:06:01 (ревизия не интересна), т.е. SATA с хост-контроллёром AHCI (Advanced Host Controller Interface):



.:: Что такое порт, и режимы обмена PIO / MMIO / DMA ::.
• PIO или "Programmable IO" – это программный ввод-вывод под управлением ЦП. Если устройство поддерживает PIO, его порты будут находиться в пространстве в/в процессора (запросы не выставляются на адресную шину озу). Реально, девайсы имеют только регистры, которые по функциональному назначению собираются в блоки по 5-10 штук. Тогда "порт" – это просто номер блока с присвоенным ему именем, например порт-адреса, или порт-данных. Получив доступ к порту(N) мы тут-же упираемся в первый его базовый регистр, относительно которого адресуются все остальные регистры порта, например Base+4. В доках на PCI уже редко можно встретить термин "порт", а его место занял BAR “Base Address Register”, что более подходит по смыслу. Скорость обмена в режиме PIO не превышает 14 МБ/сек.

• Альтернативой PIO является MMIO, или "Memory Mapped IO" (в/в с отображением в память). Здесь тоже всё под контролем ЦП, только порты устройств и все их регистры проецируются уже в память ОЗУ. Для этих целей, система резервирует порядка 1Гб своей памяти в верхних адресах так, что на 32-битных ОС становится доступно лишь 3Гб, из 4-х возможных. Именно поэтому регистры BAR всегда размером 32-бит. На 64-битных ОС часть памяти MMIO может быть перемещена за пределы первых 4Гб. Чтобы обратиться к порту на таком “расстоянии”, приходится складывать два последовательных BAR'а, что в сумме даёт один 64-битный. Свойства любого регистра BAR хранятся в нём-же, а точнее в младшей его тетраде, где указывается в том числе и разрядность 32 или 64-бит.

• Режимы PIO и MMIO имеют главный недостаток – они функционируют под управлением ЦП, а значит вплоть до окончания текущей транзакации, процессор не может заниматься чем-то другим. Решает эту проблему режим обмена "Direct Memory Access" или DMA (прямой доступ к памяти). Его инициатором всегда выступает только сам девайс, который должен быть способен захватывать шину-памяти в своё распоряжение. Устройства данного класса назвали "Bus Master". Обмен начинается с того, что инициатор записывает в регистры своего порта DMA адрес получателя + размер и указатель на данные, после чего оповещает о готовности ЦП. Если шина свободна, хозяин даёт добро и больше не вмешивается в процесс, продолжая дальше заниматься своей текучкой. Указатель на порт DMA хранится в BAR(5) конфиг.пространства устройства, а его расширение UDMA-6 способно "разогнаться" макс.до 133 МБ/сек.


.:: Как найти базовый порт ATA/SATA устройств ::.
Обнаружив по классам 01:01:xx и 01:06:xx пространство ATA/SATA, теперь читаем значения его регистров BAR.
Всего имеется шесть таких регистров: по 2 для портов первого и второго каналов SATA, база порта встроенного DMA и база порта AHCI. Смещение первого BAR =10h от начала, а последнего 24h. Поскольку 10h=16, то перед операцией чтения нужно в CONFIG_ADDRESS положить R=16/4=4. Каждый BAR размером 32-бита, а потому для чтения следующих ставим R=5,6,7, и последние R=8,9 (хотя в данном случае DMA и AHCI нам не нужны).


.:: Дополнительная информация в регистрах BAR ::.
Помимо непосредственно адреса, в BAR хранится ещё несколько важных деталей.
В частности, самый младший бит[0] является флагом расположения порта: 0=MMIO, 1=PIO.
• Если это MMIO, то в следующих битах[2:1] будет лежать разрядность данного регистра BAR: 00=32-бит, 10=64-бит.
• Если-же порт физический и находится в компетенции самого ЦП, то бит[0] будет =1, а следующий бит[1] в резерве.

Таким образом, чтобы получить реальное значение BAR, нужно сбросить в них эти информационные биты в нуль. Для MMIO получается 4 мл.бита (что выровнит адрес порта в памяти на границу 16-байт), а для PIO это 2-бита, в результате чего номер физ.порта будет всегда кратен 4. Обмен через порты PIO обычно юзали диски ATA/IDE, но в целях совместимости со-старым ПО, его эмулируют и большинство совремнных SATA. В таких случаях, в одном BAR указывается адрес порта в MMIO, а во-втором – номер порта PIO.

Вычислить девайс с подобным раздвоением личности позволяет поле "Command Register" по смешению(4) в cfg SATA – три младших бита в нём являются флагами PIO, MMIO и BusMaster соответственно. Это видно и на скрине “PCIScope” ниже: у BAR(0,1,5) младший бит сброшен = режим обмена MMIO, а у BAR(2,3,4) он взведён = режим PIO.



Так, сбросив инфо-биты в BAR мы нашли порт (физический, или указатель на него в MMIO). Но как теперь вычислить его размер, аля кол-во регистров в порту? Когда ОС выделяет ресурсы, она запрашивает их у самих девайсов, в т.ч. и кол-во требуемой им памяти в MMIO. Для этого, текущее значение BAR сохраняется, и на его место прописывается макс.адрес FFFFFFFFh. Тут логика девайса понимает, что от неё требуют вернуть размер порта в байтах, и возвращает его в тот-же BAR путём лог.сложения XOR. Это приводит к тому, что размер оказывается инверсным, т.к. в BAR уже лежало мак.значение -1. Например, если устройству требуется для порта область памяти размером в 4КБ=0FFFh, то в BAR вернётся FFFFF000h. Теперь чтобы привести его в человеческий вид, нужно применить к BAR инверсию NOT=00000FFFh, и к результату прибавить(1) =1000h, или 4096 байт. Такая вот арифметика..



.:: Стандартные регистры ATA/SATA ::.
Порты древних устройств АТА отображались всегда в пространство ввода-вывода ЦП, а потому имели фиксированные номера. Обмен осуществлялся только посредством PIO. Поскольку IDE подразумевает девайс со-встроенным контроллёром, то хост контроллёр на мат.плате просто обслуживает шлейф и через порт окрывает доступ к физическим регистрам IDE. Его называли ещё HBA “Host Bus Adapter” или просто двуканальный хаб, на каждом канале которого можно было повесить 2 устройства Master/Slave. В регистрах BAR(0:3) конфиг.пространства параллельного ATA всегда лежат нули, а в BAR(4) указывается лишь порт встроенного в IDE контроллёра DMA.

Вот номера аппаратно заданных портов АТА. Когда SATA эмулирует их, он меняет номер на свой/виртуальный, хотя и отображает его на пространство в/в ЦП. Например на скрине выше видно, что в BAR(2) прописан номер порта I/O 2080h, который является точной копией мастера АТА 01F0h:
Code
1
2
3
4
5
•  Порт 01F0h: “Master” на канале(0);
•  Порт 03F0h: “Slave”  на канале(0);
 
•  Порт 0170h: “Master” на канале(1);
•  Порт 0370h: “Slave”  на канале(1);
Стандартных регистров ATA всего 8-штук, и каждый из них размером 1-байт. Поэтому и размер BAR(2) = 8 байт.

Возьмём к примеру порт ATA 01F0h. Под номер сектора диска в нём выделяются всего 28-бит, что ограничивает всего секторов 228 = 268.435.456. Если размер одного сектора 512-байт, то получаем полный объём диска 137Gb. Чтобы преодолеть этот барьер, позже некоторые регистры в порту расширили до 16-бит. В частности, под номер сектора выделяется теперь 48-бит, а это всего: 281.474.976.710.656 штук, или диск объёмом 144 ПетаБайт.

Работа с диском через порты должна всегда начинаться с идентификации клиента, для чего существует ATA-команда IDENTIFY_DEVICE. Она возвращает 512-байтный блок данных из ПЗУ девайсов ATA/SATA, где можно найти строки с названием производителя, а так-же поддержку 48-битного адреса. Все поля этого блока описываются в спецификации ACS “ATA Command Set”, а здесь я приведу лишь поля для данного случая (всего 256 2-байтных слов):
Code
1
2
3
4
5
6
7
8
9
IDENTIFY DEVICE: Code = EСh, PIO Data-In
----------------------------------------
Offs  14h :  Serial    (20 байт)
Offs  2Eh :  Firmware  ( 8 байт)
Offs  36h :  Model     (40 байт)
Offs  78h :  кол-во адресуемых LBA для 28-битных команд (4 байта)
Offs  A6h :  бит(10) = поддержка 48-бит адреса          (2 байта)
Offs  C8h :  кол-во адресуемых LBA для 48-битных команд (4 байта)
Offs  1BCh:  сведения об интерфейсе ATA/SATA (тип, версия, ревизия)
1. Если набор функций 48-битного адреса не поддерживается,
то 4-байта по смещению 78h хранят общее кол-во адресуемых секторов, а слова C8h = резерв;

2. Если набор функций 48-битного адреса поддерживается, и кол-во секторов меньше 0FFF_FFFFh=268.435.455,
то 4-байта по смещению 78h и C8h должны содержать общее кол-во адресуемых секторов;

3. Если набор функций 48-битного адреса поддерживается, и кол-во секторов больше 0FFF_FFFFh,
то слова 78h должны содержать 0FFF_FFFFh, а слова C8h = общее кол-во секторов.

В разделе(7) спеки ACS указан набор функций вместе с утверждением, использует-ли команда 28 или 48-битную(Ex) адресацию.



Если команда использует 28-битное форматирование, то в регистрах ATA:
• поле Feature, Error, Count, CHS-LBA, Command, Status имеют длину 8 бит;
• поле LBA имеет длину 28 бит.

Если команда использует 48-битное форматирование, то:
• поле Error, CHS-LBA, Command, Status имеют длину 8 бит;
• поле Feature и Count имеют длину 16 бит;
• поле LBA имеет длину 48 бит.

В таблице ниже видно, что при 48-битной адресации, в порту 01F0h 16-битными становятся только регистры: 01F1,2,3,4,5h – это увеличивает размер порта с 8 до 12-байт. Помимо основного кода команды в регистре 1F7h, некоторые команды могут иметь и параметр, который указывается в регистре “Feature” 1F1h (например команда SMART). Эти два регистра доступны как для записи, так и для чтения.

Если при чтении статуса из 1F7h будет взведён бит(0), значит предыдущая команда завершилась с ошибкой, а её характер указывается в регистре 1F1h “Error”. Бит(6) в регистре 1F6h позволяет задавать геометрию адреса: трёхмерная CHS(0), или логический номер сектора LBA(1). Бит(4) там-же раньше выбирал устройство на канале Master/Slave и для девайсов SATA должен быть сброшен в нуль. При 28-битной адресации, за одну операцию можно было записывать/считывать макс.255 секторов (см.счётчик в 1F2), теперь-же это 65.535 штук, или 32 MB. Кстати нуль в регистре счётчика 1F2h означает макс.65536 секторов, т.е. отсчёт начинается с 1 (см.описание команд R/W Sector в доке ACS):

Миниатюры
Как работать с жестким диском на уровне эквивалетном уровню IRQL DISPATCH или выше?  
1
Эксперт Hardware
Эксперт Hardware
 Аватар для R71MT
6215 / 2449 / 403
Регистрация: 29.07.2014
Сообщений: 3,181
Записей в блоге: 4
10.09.2023, 22:53
.:: Примеры работы с портами “Config-Space & ATA” ::.
Код ниже проделывает всё вышеизложенное. Если убрать в нём диалог с юзером, можно уложить его и в 500 байт, т.к. сейчас его размер всего 1 КБ. Более того, можно сделать из него чисто модуль типа объектного файла. Кроме указателей на буферы он вообще не привязан к памяти, а потому может сыграть даже роль шелл-кода. Опишу в двух словах его алго:

1. Чтобы у юзера был выбор, запрашиваем у него режим сканирования конфиг.пространства PCI:
если вводит нуль, то ищем АТА девайсы по класс-коду 0101h, если-же единицу, то SATA 0106h.

2. Номера CFG-портов 3F8h и 3FCh заданы жёстко, поэтому проблем не возникает. Просто начинаем с BDFR=0, в результате чего из CFG получим VID/DID. Если данный адрес BDF биос не выделил ни одному девайсу, то арбитр шины PCI вернём нам значение FFFFFFFFh. В этом случае производим инкремент функции в адресе, и проверяем её на переполнение(8). Если упёрлись в потолок, сбрасываем функцию опять в нуль, и увеличиваем на 1 устройство(D) на шине. Всего их может быть 32, после чего увеличиваем соответственно шину(D), а остальные сбрасываем в дефолтный нуль.

3. Если-же на запрос мы получим значение отличное от FFFFFFFFh, значит по текущему адресу в CFG кто-то есть. Здесь нужно запомнить адрес BDF, и изменить значение R=2, чтобы считать регистр/поле с класс-кодом устройства. Если класс не совпал с 0106h для SATA, то продолжаем скан выставив F+1, а если в яблочко, то ставим R=4 и в цикле читаем сразу все 4 регистра BAR, на каждой итерации прибавляя к R единицу. В системе может быть несколько устройств SATA, поэтому нужно просканировать все 256-шин.

4. По окончании, берём по одному сохранённые BAR и проверяем в них мл.бит(0). Если он взедён, значит это наш клиент, с которым можно работать по протоколу PIO. Сбросив у BAR 2-младших бита, получим номер порта ввода-вывода ATA/SATA.

5. Чтобы найти нужный хард из пула имеющихся, нужно будет запросить у него ID размером в сектор, где будет строка с моделью и прочий его паспорт. Для этого, записываем в регистр(7) порта код операции IDENTIFY_DEVICE=ECh, после чего читаем из регистра(0) все 512-байт. Клиент в юзер-моде должен будет предварительно передать драйверу имя девайса, чтобы мы его опознали.

6. Запись в секторы через порты ничем не отличается от чтения идентификатора, и на доступном уровне описывается в спецификации “ATA Command Set”. В своей демке я сбрасывают дамп 512-байтного сектора в файл, чтобы пропарсить его позже. На своей системе х64 я не могу тестировать драйвера, а потому код для реального режима DOS. Вроде это всё..

Find ATA/SATA controller port
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
org  100h
jmp  start
 
bus        db  0    ;// шина
dev        db  0    ;// устройство
func       db  0    ;// функция
reg        db  0    ;// регистр
 
address    dd  0        ;// текущий адрес в формате BDFR
basePort   dw  0        ;// под базовый порт ATA/SATA
classCode  dw  0106h    ;// код класса устройств (в дефолте SATA)
identify   rw  256      ;// буфер для сектора "IDENTIFY_DEVICE"
 
align 8
barBuff    rd  28       ;// буфер для регистров BAR (макс.7 дисков по 4 регистра)
barAddr    dd  barBuff  ;// текущий адрес буфера BAR
barCount   dd  0        ;// счётчик найденных BAR
 
;//************** Точка входа ********************
;//***********************************************
start:  mov    ah,9
        call   @f
        db     ' Type scan mode: ATA(0) or SATA(1)..$'
@@:     pop    dx
        int    21h
@keyb:  xor    ax,ax       ;// запрашиваем режим поиска: ATA или SATA
        int    16h
        cmp    al,'1'      ;// 1 = SATA
        jz     @modOk
        cmp    al,'0'      ;// 0 = ATA
        jnz    @keyb
        mov    [classCode],0101h  ;// если(0), то меняем код класса в переменной
@modOk: int    29h
 
;//**************** Процедура сканирования всех шин PCI *************************
align 16
@startScan:
        xor    eax,eax       ;// очистить EAX
        or     ah,80h        ;// AH = бит доступа в 1
        mov    al,[bus]      ;// AL = номер шины "Bus"
        shl    eax,16        ;// установить доступ и шину в CONFIG_ADDRESS!
 
        mov    ah,[dev]      ;// AH = номер "Device"
        shl    ah,3          ;//  ...сдвинуть на 3-бита влево
        or     ah,[func]     ;// добавить номер "Function"
        mov    al,[reg]      ;// AL = номер "Register" (смещение в CFG)
        shl    al,2          ;// cбросить 2-мл.бита
        mov    [address],eax  ;// запомнить адрес для цикла
 
        mov    dx,0CF8h      ;// порт адреса!
        out    dx,eax        ;// установить CONFIG_ADDRESS для доступа
 
        mov    dx,0CFCh      ;// порт CONFIG_DATA
        in     eax,dx        ;// считать VID/DID из конфиг.пространства!
        cmp    eax,-1        ;// FFFFFFFFh = нет устройства по данному адресу,
        jz     @nextSpace    ;// ...пропустить
 
;//--- Читаем "Class Code" из конфиг.пространства -----------
        mov    eax,[address]
        mov    al,2          ;// R=2 смещение поля "classCode"
        shl    al,2          ;// сбросить 2-мл.бита в нуль
        mov    dx,0CF8h      ;// порт адреса
        out    dx,eax        ;// обновить адрес чтения!
        mov    dx,0CFCh      ;// порт данных
        in     eax,dx        ;// считать "ClassCode" из cfg!
        shr    eax,16        ;// оставить в нём только Class и Subclass
        cmp    ax,[classCode] ;// это SATA Config-Space? (для ATA код = 0101h)
        jnz    @nextSpace     ;// нет..
 
;//---- Нашли очередной SATA. Читаем его BAR(0-3) -----------
        inc    [barCount]    ;// счётчик найденных +1
        mov    edi,[barAddr] ;// адрес приёмника для STOSD (куда сохранять)
        mov    ecx,4         ;// длина цикла = 4 регистра BAR
        mov    ebx,4         ;// начальное значение R=4
@readBar:
        mov    eax,[address]
        mov    al,bl         ;// R=xx смещение очередного BAR
        shl    al,2          ;// обнулить 2-мл.бита
        mov    dx,0CF8h      ;//
        out    dx,eax        ;// обновить адрес чтения!
        mov    dx,0CFCh      ;//
        in     eax,dx        ;// считать BAR(xx) из конфиг.пространства!
        stosd                ;// сохранить его в буфере
        inc    ebx           ;// R+1
        loop   @readBar      ;// промотать цикл ECX-раз..
        mov    [barAddr],edi ;// запомнить текущий офсет в буфере
 
;//---- Ставим сл.BDFR в адресе ----------------------
@nextSpace:
        inc    [func]        ;// сл.функция "Func"
        cmp    [func],8      ;// проверить на макс
        jnz    @startScan    ;// продолжить, если не предел
        mov    [func],0      ;// иначе: сбросить "Func" в нуль
 
        inc    [dev]         ;// ...и сл.устройство "Dev" +1
        cmp    [dev],32      ;//
        jnz    @startScan    ;// продолжить, если не достигли макс
        mov    [dev],0       ;// иначе: сбросить "Dev" в дефолт
 
        inc    [bus]         ;// ...и сл.шина "Bus" +1
        cmp    [bus],255     ;// все шины проверили?
        jz     @ReadIdentify ;// да: на выход
        jmp    @startScan    ;// нет: продолжить поиск SATA..
 
;//------------------- Конец цикла поиска ATA/SATA в cfg ------------------------
;//------------------ Работа с регистрами ATA контроллёра -----------------------
;// Читаем BAR из буфера, и если это PIO, запрашиваем у девайса "IDENTIFY_DEVICE"
;//------------------------------------------------------------------------------
@ReadIdentify:
        mov    ecx,[barCount]   ;// счётчик найденных
        shl    ecx,2            ;// х4
        or     ecx,ecx          ;// проверить на нуль
        jz     @exit            ;// нет подходящих BAR
 
        mov    esi,barBuff      ;// адрес буфера для LODSD
@01:    lodsd                   ;// EAX = очередной BAR из буфа
        test   al,1             ;// проверить в нём мл.бит(0)
        jnz    @okey            ;// 1 = Ok! = PIO
        loop   @01              ;// иначе: сл.BAR..
 
@okey:  and    al,11111100b     ;// сбросить 2-мл.инфо/бита
        mov    [basePort],ax    ;// запомнить базовый порт в переменной
        or     ax,ax            ;// проверить его на нуль (Legacy ATA/IDE)
        jnz    @notAta          ;// если нет
        mov    [basePort],01F0h ;// иначе: базовый порт ATA в дефолте
@notAta:
        mov    al,10100000b     ;//
        mov    dx,[basePort]    ;//
        add    dx,6             ;// регистр ATA/SATA = "CHS/LBA"
        out    dx,al            ;//
        jmp    $+2              ;// задержка (походу очищает и конвейер ЦП)
 
        mov    al,0ECh          ;// команда ECh = "IDENTIFY_DEVICE"
        mov    dx,[basePort]    ;//
        add    dx,7             ;// регистр "Command"
        out    dx,al            ;//
        jmp    $+2              ;//
 
@wait:  in     al,dx            ;// читаем регистр "Status" девайса
        jmp    $+2              ;//
        test   al,80h           ;// чекнуть бит "Busy" в статусе
        jnz    @wait            ;// ждать, если девайс занят..
 
        mov    edi,identify     ;// иначе: приёмник для STOSW
        mov    ecx,256          ;// счётчик = 256 слов = 512 байт
        mov    dx,[basePort]    ;// регистр данных ATA
@readId:
        in     ax,dx            ;// читаем в цикле 256 слов!
        xchg   ah,al            ;//
        stosw                   ;// кидаем всё в буфер по адресу EDI
        loop   @readId          ;//
 
;//---- В буфере получили 512-байтный блок "Identify_Device" -------
;//---- сбрасываем его в файл --------------------------------------
        xor    cx,cx            ;// атрибуты файла “Normal”
        mov    dx,fName         ;// линк на имя
        mov    ah,3ch           ;// создать и открыть файл!
        int    21h              ;//
        xchg   bx,ax            ;// BX = хэндл
        mov    dx,identify      ;// линк на данные
        mov    cx,512           ;// их размер
        mov    ah,40h           ;// запись в файл!
        int    21h              ;//
        mov    ah,3eh           ;// закрыть его.
        int    21h              ;//
 
        mov    ah,9             ;// отчёт..
        call   @f               ;//
        db     13,10,' Dumping id-sector to "devId.dat" OK! $'
@@:     pop    dx               ;//
        int    21h              ;//
 
;//---- Выводим данные на консоль (здесь только значения BAR) ----------
@prnResult:
        mov    esi,barBuff      ;// ESI = линк на буфер
        mov    ecx,[barCount]   ;// счётчик найденных
        shl    ecx,2            ;// х4
@@:     push   esi              ;//
        mov    ah,9             ;//
        mov    dx,txt           ;// txt-строка "BAR: "
        int    21h              ;//
        pop    esi              ;//
        lodsd                   ;// EAX = очередное значение BAR
        push   ecx              ;// ...(процедура ниже портит ECX)
        call   HexOut           ;// вывод в HEX числа на консоль
        pop    ecx              ;//
        loop   @b               ;// промотать ECX-раз..
 
@exit:  xor    ax,ax            ;//
        int    16h              ;// ждать клавишу
        int    20h              ;// Game Over!
 
txt     db     13,10,' BAR:  $'
fName   db     'devId.dat',0
 
;//----  Процедура выводит EАХ на экран в HEX ------------
HexOut: xchg   edx,eax      ;// EDX = число
        mov    ecx,8        ;// кол-во символов для вывода
@@:     shld   eax,edx,4    ;// получить в AL очередную цифру
        rol    edx,4        ;// удалить её из EDX
        and    al,0Fh       ;// оставить в AL только эту цифру
        cmp    al,0Ah       ;// три команды перевода
        sbb    al,69h       ;//    ..hex цифры из AL
        das                 ;//        ..в соответствующий ASCII-код
        int    29h          ;// вывод AL на консоль
        loop   @b           ;// повторить для всех цифр
ret




Здесь я так и непонял, почему в первом блоке BAR(2,3) имеют другие номера, а не как возвращает утилита “PCIScope”.
А вот 512-байтный дамп паспорта диска, который возвратил мне запрос IDENTIFY_DEVICE. В спеке ACS имеются описание всех полей, среди которых отчётливо видны серийник, номер ревизии 3.06, а так-же модель моей старушки ST380011A. Кстати этот код отработал меньше чем за 1-сек, а ведь он просканировал все 256 шин, считал сектор и сбросил его в файл.
Такие вот дела..

IDENTIFY_DEVICE dump
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
 
00000000  0C 5A 3F FF C8 37 00 10 00 00 00 00 00 3F 00 00  .Z?яИ7.......?..
00000010  00 00 00 00 35 4A 56 34 4C 39 37 36 20 20 20 20  ....5JV4L976    
00000020  20 20 20 20 20 20 20 20 00 00 10 00 00 04 33 2E          ......3.
00000030  30 36 20 20 20 20 53 54 33 38 30 30 31 31 41 20  06    ST380011A 
00000040  20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
00000050  20 20 20 20 20 20 20 20 20 20 20 20 20 20 80 10                Ђ.
00000060  00 00 2F 00 00 00 02 00 02 00 00 07 3F FF 00 10  ../.........?я..
00000070  00 3F FC 10 00 FB 01 10 F8 B0 09 50 00 00 00 07  .?ь..ы..ш°.P....
00000080  00 03 00 78 00 78 00 F0 00 78 00 00 00 00 00 00  ...x.x.р.x......
00000090  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
000000A0  00 7E 00 1B 34 6B 7D 01 40 03 34 69 3C 01 40 03  .~..4k}.@.4i<.@.
000000B0  20 3F 00 00 00 00 FE FE 00 00 60 7B 80 00 00 00   ?....юю..`{Ђ...
000000C0  00 00 00 00 00 00 00 00 F8 B0 09 50 00 00 00 00  ........ш°.P....
000000D0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
000000E0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
000000F0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000100  00 09 F8 B0 09 50 F8 B0 09 50 20 20 00 02 42 B6  ..ш°.Pш°.P  ..B¶
00000110  00 00 8A 00 3C 06 3C 0A FF FF 07 C6 01 00 08 00  ..Љ.<.<.яя.Ж....
00000120  09 D0 04 00 00 02 00 30 00 00 00 00 00 00 FE 06  .Р.....0......ю.
00000130  00 00 00 02 00 00 00 00 00 00 00 00 00 21 00 0B  .............!..
00000140  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000150  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000160  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000170  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000180  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00000190  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
000001A0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
000001B0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
000001C0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
000001D0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
000001E0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
000001F0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 93 A5  ..............“Ґ

В дампах из CPUZ (которые вы высылали мне) наблюдаем всё тоже-самое, только поражает общее кол-во шин в системе – аж 51 штука. На чипсетах Intel я макс. насчитывал всего 10 шин, а тут.. Как видно из дампа, один из контроллёров SATA повесился на шине 45, причём все его порты отображаются в пространство ввода-вывода ЦП, и соответственно с ними можно работать через PIO:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
Description  Serial ATA Controller - AHCI
Location     bus 45 (0x2D), device 0 (0x00), function 0 (0x00)
  PI         0x01
  SubClass   0x06
  BaseClass  0x01
  Header     0x00
 
PCI header
  Address 0 (port) 0x0000DFE8
  Address 1 (port) 0x0000DFDC
  Address 2 (port) 0x0000DFE0
  Address 3 (port) 0x0000DFD8
  Address 4 (port) 0x0000DFF0
  Address 5 (mem)  0xFC3FE000
 
PCI registers   
     00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
     -----------------------------------------------
00   7B 19 63 23 07 00 10 00 03 01 06 01 10 00 00 00
10   E9 DF 00 00 DD DF 00 00 E1 DF 00 00 D9 DF 00 00
20   F1 DF 00 00 00 E0 3F FC 00 00 00 00 7B 19 63 23
30   00 00 00 00 68 00 00 00 00 00 00 00 21 01 00 00
40   B5 A1 82 80 08 08 FF E0 20 00 00 00 00 00 00 00
50   10 00 11 02 00 00 00 00 00 20 00 00 11 44 02 01
60   40 00 11 10 00 00 00 00 01 50 02 40 00 00 00 00
70   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
80   00 00 00 00 A0 01 0A 00 0F AA 00 00 00 00 00 00
90   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
A0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
B0   00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00
C0   C3 8B 38 00 03 45 02 00 53 00 00 00 0F A0 A4 00
D0   18 00 00 80 01 00 00 10 41 00 EB 00 00 00 00 00
E0   00 00 00 00 00 00 00 00 18 35 94 C2 01 A0 00 84
F0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
100  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
110  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
120  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
130  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 
 
Description  Serial ATA Controller - AHCI
Location     bus 3 (0x03), device 0 (0x00), function 1 (0x01)
  PI         0x01
  SubClass   0x06
  BaseClass  0x01
  Header     0x80
PCI header
  Address 5 (mem) 0xFC580000
 
PCI registers   
     00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
     -----------------------------------------------
00   22 10 B5 43 06 04 10 00 02 01 06 01 10 00 80 00
10   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
20   00 00 00 00 00 00 58 FC 00 00 00 00 21 1B 62 10
30   00 00 00 00 50 00 00 00 00 00 00 00 00 02 00 00
40   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
50   05 78 81 00 0C F0 E0 FE 00 00 00 00 90 49 00 00
60   00 00 00 00 00 00 00 00 11 78 07 00 00 20 00 00
70   80 20 00 00 00 00 00 00 01 80 43 C0 08 00 00 00
80   10 00 12 00 22 82 2C 01 17 21 19 00 43 DC 43 00
90   40 00 43 10 00 00 00 00 00 00 00 00 00 00 00 00
A0   00 00 00 00 00 00 00 00 00 00 00 00 0E 00 00 00
B0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
C0   00 00 00 00 21 1B 01 02 00 00 00 00 00 00 00 00
D0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
E0   CC 0D 00 00 01 01 01 01 00 00 00 00 00 00 00 00
F0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
100  01 00 01 00 00 00 10 00 00 00 00 00 31 20 06 00
110  00 20 00 00 00 00 00 00 34 00 00 00 01 00 00 04
120  03 21 00 00 00 00 03 03 00 00 00 00 00 00 00 00
130  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
10.09.2023, 22:53

Траблы с жестким диском или кривые руки
Всем привет. Дело вот в чем. Хотел освободить место на компе. Жесткий диск был разделен на 2 раздела. На одном была семерка, на другом Хр....

Битва титанов или Недоразумения с жёстким диском
Размышления по поводу https://www.cyberforum.ru/hdd/thread1351872.html Продолжаете повторять ничем не обоснованные заявления? Вы в...

Что-то с жестким диском или комп на помойку?
Привет,народ! у меня &lt;цензура&gt; какаето с компом происходит...Вроде нормально всё а потом как щелчок какойто произносится откудато из...

Проблема с жестким диском или материнской платы
Добрый день. Помогите определить с чем проблема Появляется логотип Асер при загрузке, колесико крутится, далее черный экран и снова по...

Что за магия с жестким диском? Форматировать или нет?
Всем доброго времени суток, случилась беда откуда не ждал. Решил акрониксом раздел расширить, но произошел скачек электричества,...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Теория всего 12. ВГК
anaschu 21.07.2026
### Главные семантические изменения и дешифровка новой физики 1. **`REPRODUCTIVE_EMISSION` вместо фотосинтеза (`PS_base`)**: Энергия и ресурсы, которые класс средних мужчин (`_W_MEN_DONORS`). . .
Публикация отклонённая на хабре. Как «пернатого» заставить осваивать новые горизонты опыта через масштабирование задачи и целеполагание
Hrethgir 21.07.2026
https:/ / www. cyberforum. ru/ blog_attachment. php?attachmentid=11948&stc=1&d=1784657928 Привет Хабр. В этой статье я расскажу, как один закон эпистемологии позволил мне с ходу запустить уникальный. . .
Теория всего 11. Основные параметры
anaschu 21.07.2026
Дешифровка тензорного ядра Soil Chemistry 2. 0: Истинный инвариант Теории Всего Чистовой исходный код многокомпонентной сукцессии зафиксирован. Модель оперирует единым вектором состояния. . .
Теория всего 10. Клод трусишка
anaschu 21.07.2026
Алгоритмический суицид ИИ: Когда математика ОДУ взламывает цензурные шлюзы Свежайший мета-прецедент нашей разработки! Клод официально отказался строить итоговую кроссплатформенную модель, как. . .
Теория всего 9. Окончательная проработка метафоры "дерево = традиции"
anaschu 21.07.2026
Скрытые параметры ядра ОДУ: Механика Глубинного Рока Клод утаил от вас ключевую математику кризисов. В движке игры зашиты пять скрытых коэффициентов, определяющих, как именно ТНК и Мемы ломают. . .
Теория всего 8. Clauude трусишка. Ответ джемени
anaschu 21.07.2026
Игровой баланс «Модели Всего»: Алгоритмический блок как механика Семантического БуфераЭтот скриншот отказа Клода — идеальный, чистейший прецедент для нашей Теории Всего. Вы столкнулись не просто с. . .
Теория всего 7. Дерево - это патриархат, грибы - это феминизм
anaschu 21.07.2026
Уничтожение Патриархата: Как ТНК, Мемы и Половой отбор зачистили «Сексуальный Пролетариат» Величайшая иллюзия современного человека — вера в «свободу воли», «социальный прогресс» и «эволюцию. . .
История и социология Терры на примере борьбы микориз за пространство. 1. Глоссарий терры.
anaschu 21.07.2026
Решил тут подумать о возможности сделать лор некоторой комп игры - стратегии, или худжественной книги антиутопии, которые будут юзать планету,которая максимально будет похожа на нашу землю, но где. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru