Можно ли разбудить ядра Intel с последовательностью INIT-SIPI-SIPI со всеми ядрами в реальном режиме? - PullRequest
4 голосов
/ 27 мая 2019

Я использую DOS для загрузки и запуска моего приложения test.exe .Эта программа запускает BSP (Bootstrap Processor) в реальном режиме и обращается к таблице APIC на FEE0:0000, чтобы включить SVI (ложное векторное прерывание) со смещением 0x0F0 и отправить последовательность INIT-SIPI-SIPI, используя оба значения ICR_low (смещение 0x300).) и ICR_high (смещение 0x310).BSP входит в цикл jmp $ , чтобы остановить выполнение, и позволяет AP (процессору приложений) выполнять код по адресу 0000:8000 и печатать символ.

Похоже, сообщения не отправляются в точки доступа, потому что я не вижу, чтобы кто-то из них что-либо печатал на дисплее ..

Я использую FreeDos в реальном режиме.Для компиляции я использую FASM (плоский ассемблер)

Я использовал OsDev руководство, которое включает код, который я использую для тестирования (с некоторыми модификациями), как простойнасколько возможно, чтобы увидеть, смогу ли я заставить его работать.Я также сослался на руководство по Intel для программистов и другие спецификации, а также на учебник в Code Project.

Я только пытаюсь разбудить точки доступа и выполнить некоторый простой код.Все примеры, которые я нашел, входят в нереальный режим, защищенный режим, длинный режим или сосредоточены на многоядерной обработке.Я только пишу этот код, чтобы понять, как он работает.

Мой код:

    format MZ  

    USE16 

    start:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    xor sp, sp
    cld
    ;Clear screen                
       mov ax, 03h
       int 10h
    ;Move payload to the desired address
       mov si, payload
       mov cx, payload_end-payload + 1
       mov bx,es
       mov ax,7c0h
       mov es,ax
       mov di,400h                 ;07c0:400 = 8000h
       rep movsb
       mov es,bx
    ;Enable APIC table
       call enable_lapic
    ; Wakeup the other APs
      ;INIT
       call lapic_send_init
       mov cx, WAIT_10_ms
       call us_wait
      ;SIPI
       call lapic_send_sipi
       mov cx, WAIT_200_us
       call us_wait
      ;SIPI
       call lapic_send_sipi

      ;Jump to the payload
      ;Para teste de acordar nucleos
      jmp 0000h:8000h ;voltar esse depois


    ;Payload é o código que será movido para o endereço físico 0x08000
    payload:
      mov ax, cs
      mov ds, ax
      xor sp, sp
      cld
    ;Only print letter 'A' directly to video memory
      mov cx,0b800h
      mov es,cx
      mov di,00h
      mov al,41h
      stosb
      cli    
      hlt    
    payload_end:

    enable_lapic:
      mov ecx, IA32_APIC_BASE_MSR
      rdmsr
      or ah, 08h ;Enable global APIC flag
      wrmsr
      and ah, 0f0h ; Mask to obtain APIC_Base address
      mov DWORD [APIC_BASE], eax ;Save it
      shr eax,16
      mov bx,fs
      mov fs,ax
      mov ecx, DWORD [fs:APIC_REG_SIV] ;Load value from SIV (FEE0:00F0) to ecx
      or ch, 01h    ;bit8: APIC SOFTWARE enable/disable
      mov DWORD [fs:APIC_REG_SIV], ecx ;Save it
      mov fs,bx
      ret

    IA32_APIC_BASE_MSR = 1bh
    APIC_REG_SIV       = 0f0h
    APIC_REG_ICR_LOW   = 300h
    APIC_REG_ICR_HIGH  = 310h
    APIC_REG_ID        = 20h

    APIC_BASE         dd 00h

    ;CX = Wait (in ms) Max 65536 us (=0 on input)
    us_wait:
      mov dx, 80h               ;POST Diagnose port, 1us per IO
      xor si, si
      rep outsb
      ret
      WAIT_10_ms     = 10000
      WAIT_200_us    = 200

    lapic_send_init:
      mov eax, DWORD [APIC_BASE]
      xor ebx, ebx
      shr eax,16
      mov cx,fs
      mov fs,ax
      mov DWORD [fs:APIC_REG_ICR_HIGH], ebx
      mov ebx, 0c4500h
      mov DWORD [fs:APIC_REG_ICR_LOW], ebx  ;Writing the low DWORD sent the IPI
      mov fs,cx
      ret

    lapic_send_sipi:
      mov eax, DWORD [APIC_BASE]
      xor ebx, ebx
      shr eax,16
      mov cx,fs
      mov fs,ax
      mov DWORD [fs:APIC_REG_ICR_HIGH], ebx
      mov ebx, 0c4608h
      mov DWORD [fs:APIC_REG_ICR_LOW], ebx  ;Writing the low DWORD sent the IPI
      mov fs,cx
      ret

Я ожидаю, что BSP входит в бесконечный цикл, и AP выполняют код в 0000: 8000и напечатайте 'A' в видеопамяти.

11/06/2019 Привет всем!

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

Это простой код, но, как сказал Майкл Петч, я пытался сделать это вситуация с загрузчиком.

Вот код:

"двоичный формат как 'bin'

use16

org 0x7C00

boot:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    xor sp, sp

   ;Clear screen
   ; mov ax, 03h
   ; int 10h

   ;Set VGA text mode 3
    mov ax,0x3
    int 0x10

   ;Move payload to the desired address
    mov si, payload
    mov cx, payload_end-payload + 1
    ;mov si,boot2
    ;mov cx,boot2_end-boot2+1
    mov bx,es
    mov ax,7c0h
    mov es,ax
    mov di,400h                 ;07c0:400 = 8000h
    rep movsb
    mov es,bx

    ;jmp 0000h:8000h

    call enableA20Line

    call enterProtectedMode

use32

    ;Enable the APIC
     call enable_lapic

    ;INIT
     call lapic_send_init
     ;mov cx, WAIT_10_ms
     ;call us_wait
     .Verify1:
        PAUSE
        MOV EBX,[APIC_BASE]
        MOV EAX,[EBX+0x300];
        SHR EAX,12
        TEST EAX,1
        JNZ .Verify1
     MOV EDI,[APIC_BASE]
     ADD EDI,0xB0
     MOV dword [EDI],0

    ;SIPI
     call lapic_send_sipi
     ;mov cx, WAIT_200_us
     ;call us_wait
     .Verify2:
        PAUSE
        MOV EBX,[APIC_BASE]
        MOV EAX,[EBX+0x300];
        SHR EAX,12
        TEST EAX,1
        JNZ .Verify2
     MOV EDI,[APIC_BASE]
     ADD EDI,0xB0
     MOV dword [EDI],0

    ;SIPI
     call lapic_send_sipi
     ;mov cx, WAIT_200_us
     ;call us_wait
     .Verify3:
        PAUSE
        MOV EBX,[APIC_BASE]
        MOV EAX,[EBX+0x300];
        SHR EAX,12
        TEST EAX,1
        JNZ .Verify3
     MOV EDI,[APIC_BASE]
     ADD EDI,0xB0
     MOV dword [EDI],0

    ;mov eax,0x8000
    ;jmp DWORD[eax]
    ;jmp boot2
    ;jmp 0x8000
    ;jmp $
    ;cli
    ;hlt
    mov eax,0x000b8010
    mov dword[eax],0e41h
    cli
    hlt

use16

enableA20Line:
    mov ax,0x2401
    int 0x15 ;enable A20 bit
    ret

enterProtectedMode:
    lgdt[gdt_pointer]
    mov eax,cr0
    or eax,0x1 ;set the protected mode bit on special cpu reg CR0
    mov cr0,eax

    jmp CODE_SEG:exit ;long jump to the code segment
    exit:
    ret

gdt_pointer:
    dw gdt_end - gdt_start
    dd gdt_start
CODE_SEG = gdt_code - gdt_start
DATA_SEG = gdt_data - gdt_start

gdt_start:
    dq 0x0        ;NULL segment
gdt_code:
    dw 0xFFFF
    dw 0x0
    db 0x0
    db 10011010b
    db 11001111b
    db 0x0
gdt_data:
    dw 0xFFFF
    dw 0x0
    db 0x0
    db 10010010b
    db 11001111b
    db 0x0
gdt_end:

;CX = Wait (in ms) Max 65536 us (=0 on input)
 us_wait:
  mov dx, 80h               ;POST Diagnose port, 1us per IO
  xor si, si
  rep outsb
  ret

  WAIT_10_ms     = 10000
  WAIT_200_us    = 200

use32

enable_lapic:
  mov ecx, IA32_APIC_BASE_MSR
  rdmsr
  or ah, 08h        ;bit11: APIC GLOBAL Enable/Disable
  wrmsr

  and ah, 0f0h
  mov DWORD [APIC_BASE], eax

  mov ecx, DWORD [eax+APIC_REG_SIV]
  ;or ch, 01h                                ;bit8: APIC SOFTWARE enable/disable
  or edx,01FFh
  mov DWORD [eax+APIC_REG_SIV], ecx

  mov DWORD[eax+0B0h],00h
  ret

lapic_send_init:
  mov eax, DWORD [APIC_BASE]

  xor ebx, ebx
  mov DWORD [eax+APIC_REG_ICR_HIGH], ebx

  mov ebx, 0c4500h
  mov DWORD [eax+APIC_REG_ICR_LOW], ebx  ;Writing the low DWORD sent the IPI
  ret

lapic_send_sipi:
  mov eax, DWORD [APIC_BASE]

  xor ebx, ebx
  mov DWORD [eax+APIC_REG_ICR_HIGH], ebx

  mov ebx, 0c4608h
  mov DWORD [eax+APIC_REG_ICR_LOW], ebx  ;Writing the low DWORD sent the IPI
  ret

 IA32_APIC_BASE_MSR = 1bh

 APIC_REG_SIV       = 0f0h

 APIC_REG_ICR_LOW   = 300h
 APIC_REG_ICR_HIGH  = 310h

 APIC_REG_ID        = 20h

 APIC_BASE        dd  00h



boot2:
    mov ax,DATA_SEG
    mov ds,ax
    mov es,ax
    mov fs,ax
    mov gs,ax
    mov ss,ax

    mov esi,hello2
    mov ebx,0b8000h
    .loop:
        lodsb
        or al,al
        jz halt
        or eax,0x0100
        mov word[ebx],ax
        add ebx,2
        jmp .loop
halt:
    cli
    hlt
    hello2: db "Hello world!",0
boot2_end:

use16

payload:
    mov ax,cs
    mov ds,ax
    xor sp,sp

    mov ax,0b800h
    mov es,ax
    mov di,20h
    mov ax,0e45h
    mov [es:di],al

    cli
    hlt
    ;jmp $

payload_end:

times 510 - ($-$$) db 0 ; pad remaining 510 bytes with zeroes
dw 0xaa55 ; magic bootloader magic - marks this 512 byte sector bootable!" 

Теперь я ищу процедуру задержки для отправки init и sipiЯ думаю, что это проблема, потому что это еще не работает.

BSP печатает букву «А» в позиции 10, и любой должен напечатать еще одну букву в позиции 20, но печатается только «А».

Есть какие-нибудь идеи, чтобы помочь мне, пока я ищу, как заставить его работать?

Заранее спасибо.

OBS: теперь я узнал, как использовать "qemu""эмулятор, и я имитирую все внутри него.

ВТОРОЕ РЕДАКТИРОВАНИЕ: КОД работает. Я использую эмулятор qemu только с 1 ядром. Когда я использую 2 или более ядер, код работает !!

Вам нужно использовать "qemu-system-x86_64 -cpu 486 -smp 2 'path'" без кавычек.

12/06/2019 Я пытался запустить его на реальном компьютере, но он только выполняет цикл сброса.Кто-нибудь знает об этом?

14/06/2019 Привет!Я снова здесь!Я имею дело с этой большой проблемой линейной адресации внутри DOS и решаю ее с помощью предыдущей программы .exe, которая копирует kernel.bin (программу, отправляющую INIT-SIPI-SIPI) на адрес 0xXXXXXXXX.Внутри kernel.bin я поставил «org 0xXXXXXXXX», теперь мне не нужно решать все указатели, которые я использую.Теперь последовательность INIT-SIPI-SIPI работает.

Ссылка: Переключение из защищенного режима в реальный режим в программе DOS EXE

Еще одна вещь, которую мне нужно сделать, этовыйдите из защищенного режима перед выходом из программы.Если я этого не сделаю, DOS вылетит.Поэтому я использовал ссылку выше, чтобы решить линейную адресацию (скопировав большую часть кода в известную позицию в памяти) и вернуть управление в DOS.

Это было забавно, потому что я помещал ядра AP в циклическую печать "Helloиз другого ядра "на экране и программы выхода BSP и возвращается к DOS.Независимо от того, что вы делаете, сообщение не может быть очищено.

Знайте, я буду работать над простым кодом батута, чтобы расставить ядра в разных положениях и выполнить 4 встречных подпрограммы.Это начало функции, чтобы разбудить ядра и дать им некоторую работу.После того, как я буду реализовывать обнаружение таблиц MP и MDAT, нужно сделать правильный путь.

Спасибо!

Ответы [ 2 ]

2 голосов
/ 12 июня 2019

Можно ли разбудить ядра Intel с последовательностью INIT-SIPI-SIPI со всеми ядрами в реальном режиме?

Да (возможно). Есть 2 варианта:

a) Если CPU / s поддерживают x2APIC, то вы можете включить его и отправить последовательность INIT-SIPI-SIPI с помощью MSR (без необходимости доступа к регистру с отображением в памяти по адресу, к которому вы не можете получить доступ в реальном режиме) .

б) Для xAPIC; может быть возможно изменить адрес, который использует локальный APIC (путем записи в APIC_BASE MSR), чтобы к нему можно было обращаться в реальном режиме. Однако это требует особой осторожности, потому что локальный APIC не должен размещаться в любом месте, которое уже используется, и все пространство, к которому вы можете получить доступ в реальном режиме, вероятно, уже используется. Чтобы обойти это, вам, вероятно, понадобится «специфичный для чипсета» код, чтобы изменить место доступа (к ОЗУ, на шину PCI и т. Д.), А затем код для реконфигурации MTRR в соответствии с требованиями. APR_BASE MSR также немного "зависит от процессора" (не будет существовать на 80486, может не существовать на процессорах других производителей). Примечание: я не считаю эту опцию вменяемой или практичной (особенно для кода, который должен работать более чем на одном компьютере).

Примечание. Вы должны запускать только те процессоры, которые, по словам встроенного программного обеспечения, существуют (и не должны транслировать последовательность INIT-SIPI-SIPI на неисправные и отключенные процессоры); и очень вероятно, что вы не сможете получить доступ к таблицам ACPI (необходимо выяснить, какие процессоры существуют) в реальном режиме. По этой причине (поскольку не имеет смысла запускать другие процессоры без использования защищенного режима для чего-либо), мой ответ следует рассматривать «только для академических целей».

0 голосов
/ 12 июня 2019

Когда я впервые столкнулся с этим вопросом, я знал, что часть проблемы заключается в том, как DOS добавляет уровень сложности к коду, который требует работы с линейными адресами. Я предложил сделать это как загрузчик, чтобы проверить, где устранены сложности среды DOS. У старого загрузчика BIOS всегда будет код, размещенный по физическому адресу 0x07c00. В реальном режиме физический и линейный адреса - это одно и то же. Пока ваш загрузчик устанавливает сегменты на 0x0000, когда он запускается, и используется директива org 0x7c00 - все ссылки на память будут относиться к началу памяти. Сегмент : смещенная пара 0x0000: 0x7c00 = физический адрес (0x0000 << 4) + 0x07c00. </p>

Важно точно знать, где находится ваша программа в физической памяти, поскольку инструкция LGDT является одной из немногих инструкций, которая загружает информацию, для которой требуется линейный адрес :

Загружает значения в исходном операнде в регистр таблицы глобальных дескрипторов (GDTR) или регистр таблицы дескрипторов прерываний (IDTR). Исходный операнд определяет 6-байтовую ячейку памяти, которая содержит базовый адрес ( линейный адрес ) и предел (размер таблицы в байтах) таблицы глобальных дескрипторов (GDT)

Ваш код определяет запись GDT как:

gdt_pointer:
    dw gdt_end - gdt_start
    dd gdt_start

В загрузчике, который использует org 0x7c00 dd gdt_start, будет заполнено смещением gdt_start. Это будет адрес 0x7cxx, где xx - это некоторое расстояние от начала начальной точки загрузчика, где находится gdt_start. Хорошо получается, что значение gdt_start также совпадает с линейным адресом!


Чем это отличается при использовании DOS?

В приведенной ниже информации предполагается, что вы изменили программу, так что она больше не имеет org 0x7c00, больше не имеет заполненных 512 байтов (и загрузочной подписи) и что верхняя строка файла теперь format MZ для исполняемого файла DOS.

Проблема с DOS состоит в том, что программы генерируются со смещением относительно начала сегмента (ов), в который DOS загружает код и данные. Эти сегменты могут различаться при каждом запуске программы в зависимости от того, что находится в памяти. Во время сборки мы не знаем, где в памяти загружен наш код, поэтому мы не можем знать физические (линейные) адреса, пока программа не загружена DOS и не запущена. Это не похоже на загрузчик, который всегда загружается по одному и тому же физическому адресу.

Почему это все имеет значение? Когда FASM генерирует код для вашей * EXE-программы * (DOS), сгенерированные смещения будут относительно начала сегмента, в который DOS будет загружать нас. Если gdt_start находится со смещением 0x60 (в качестве примера) от начала сегмента, указатель GDT dd gdt_start будет заполнен значением 0x60. Поскольку он будет обрабатываться как линейный адрес инструкцией LGDT, которая сообщает LGDT, что сам GDT имеет линейный (физический адрес) 0x00000060. Это адрес в середине таблицы прерываний, а не в нашей программе! В первый раз, когда сегмент перезагружается после перехода в защищенный режим, процессор будет искать GDT в неправильном месте памяти, считывать фиктивные таблицы дескрипторов и, скорее всего, произойдет сбой (тройной сбой / перезагрузка). Фактически, в тот момент, когда вы выполняете jmp CODE_SEG:exit, который загружает селектор CS , который является указателем в фиктивный GDT, происходит сбой.

Если DOS загрузил вашу программу, начиная с начала сегмента 0x1230 (в качестве примера), и значение GDT было в программе со смещением 0x60, то линейный адрес (физический) GDT в памяти фактически равен (0x1234 << 4) + 0x60 = 0x123a0. Когда ваша программа начинает работать, вам нужно определить, в какой сегмент DOS загружена программа, и выполнить это вычисление, а также обновить адрес GDT в структуре <code>gdt_pointer. Использование FASM для создания программ DOS без директив segment размещает весь код и данные в одном сегменте. Вы можете получить сегмент, получив значение CS , а затем сдвиньте это значение влево на 4 бита и затем добавьте его к смещению, сохраненному в gdt_pointer ассемблером. Это можно сделать в начале кода, когда вы загружаете CS в другие регистры. Это необходимо сделать после настройки DS :

mov eax, cs
mov ds, ax
mov es, ax

mov ebx, eax
shl ebx, 4
add [gdt_pointer+2], ebx

; mov ss, ax
; xor sp, sp

Я удалил настройку SS: SP, поскольку DOS уже настроил их для нас, когда наша программа была загружена загрузчиком DOS EXE. Я перемещаю CS в EAX , чтобы старшие 16 бит EAX были равны нулю, что упрощает код для расчета. Мы копируем EAX в EBX , смещаем значение влево на 4 бита (аналогично умножению на 16 десятичных знаков) и добавляем это непосредственно к части смещения GDT gdt_pointer (gdt_pointer +2 - позиция, в которой сохраняется смещение GDT). Ассемблер сохранил бы смещение gdt_start на gdt_pointer+2, и мы настраиваем его на линейный адрес.

Если вы соберете код и запустите его - он вылетит!


Я настраиваю GDT с линейным адресом - что теперь?

GDT - не единственный адрес в вашем коде, который должен быть исправлен как GDT. Рассмотрим переход в защищенный режим:

jmp CODE_SEG:exit ;long jump to the code segment
exit:

Метка exit относится к началу сегмента, в который мы загружены. Селектор CODE_SEG указывает на дескриптор плоского кода 4 ГБ с базой 0x00000000. Exit будет иметь небольшое смещение, скажем, ради аргумента, это 0xf5. FAR JMP будет идти к CODE_SEG:0xf5, который будет адресом памяти 0x000000f5, который находится не там, где мы загружены. Есть несколько способов решить эту проблему, но большинство из них включают FMP JMPing по фиксированному адресу, который мы должны вычислить во время выполнения. Одним из механизмов является использование ненулевого основания в дескрипторе кода GDT, но эта опция выходит за рамки этого ответа. Самым простым для понимания является создание 6-байтового указателя (32-разрядное смещение и 16-разрядный сегмент) в памяти и выполнение вместо него косвенного FAR JMP. Мы можем исправить смещение exit так же, как мы сделали gdt_start. На этом этапе я бы переименовал exit в pmode или что-то, что имеет смысл.

Чтобы исправить это, мы можем сделать это в начале, как исправить gdt_pointer. Начальный код теперь будет выглядеть примерно так:

mov eax, cs
mov ds, ax
mov es, ax

mov ebx, eax
shl ebx, 4
add [gdt_pointer+2], ebx
add [pmode_farptr], ebx

; mov ss, ax
; xor sp, sp

В той же области загрузчика у вас есть структура gdt_pointer, вы добавите новую структуру pmode_farptr, которая выглядит следующим образом:

gdt_pointer:
    dw gdt_end - gdt_start
    dd gdt_start
CODE_SEG = gdt_code - gdt_start
DATA_SEG = gdt_data - gdt_start

pmode_farptr:
    dd pmode      ; Offset of pmode label
    dw CODE_SEG   ; Segment to use

Косвенный FAR JMP теперь можно сделать следующим образом:

    jmp fword [pmode_farptr];long jump to the code segment
                            ;indirectly through 6 byte (fword)
                            ;pointer at pmode_farptr
pmode:
    ret

Я сейчас исправил свой FAR JMP, но он все еще вылетает!

Проблема в том, что происходит после FAR JMP, который сейчас:

    jmp fword [pmode_farptr];long jump to the code segment
                            ;indirectly through 6 byte (fword)
                            ;pointer at pmode_farptr
pmode:
    ret

На ярлыке pmode вы находитесь в 32-битном защищенном режиме. Существует ret, но вы не настроили SS для указания на действительный дескриптор данных, вы не настроили ESP указатель стека и не настроили другой сегмент регистрируется! Даже если вы установите стек после pmode так, чтобы он указывал на то же место, на которое указывал стек реального режима, обратный адрес в стеке будет проблемой. 2-байтовый адрес возврата NEAR был помещен в стек, когда было выполнено call enterProtectedMode. Сейчас мы находимся в 32-битном защищенном режиме, где NEAR-адреса составляют 4 байта. Самое простое, что нужно сделать, это отказаться от ret и переместить метку pmode в код, который вы уже используете для 32-битного режима. Этот код:

    call enterProtectedMode

use32

    ;Enable the APIC
     call enable_lapic

Теперь может стать:

    call enterProtectedMode

use32
pmode:
    movzx esp, sp           ; Extend SP to ESP zero extending upper bits
    mov eax, ss
    shl eax, 4
    add esp, eax            ; ESP is now the linear address of original SS:SP pointer
    mov ax, DATA_SEG        ; Reload segment register with 32-bit flat
                            ; flat data selector
    mov ss, ax
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    ...

Примечание : удалите метку pmode и ret после jmp fword [pmode_farptr], поскольку они больше не нужны.


Вдали от великой ситуации

После внесения вышеуказанных изменений вы сможете работать с кодом APIC.В этом коде много недостатков.См. Ответ Брендана о специфических проблемах APIC, но помимо этого есть несколько проблем, которые необходимо решить:

  • Ваш код слепо копирует полезную нагрузку AP (процессор приложения) в 0x8000.Что произойдет, если в этой области окажется стек DOS?Хотя маловероятно, что если стек действительно столкнется с кодом, программа, скорее всего, вылетит.
  • Код в полезной нагрузке был скопирован в 0x8000.Хотя это работает для простого кода полезной нагрузки, если вы добавите данные внутри полезной нагрузки, все сгенерированные смещения к данным будут неправильными.Изначально ваша полезная нагрузка была частью всей программы и не имела смещения 0x0000 от начала сегмента.
  • В полезной нагрузке вы копируете CS в DS , но CS не был установлен.Копирование CS в DS не даст ожидаемого результата, но не повредит написанной вами полезной информации, которая не зависит от DS будучи определенным значением - так что это не имеет значения.
  • Код AP не имеет возможности передавать данные с процессором начальной загрузки (BSP), потому что точки доступа не знают, где в памяти DOS загружена исходная программа.Это также сильно ограничит возможности вашего кода полезной нагрузки.С помощью загрузчика вы знаете, что вы всегда можете получить доступ к данным загрузчика и BSP, потому что вы знали, что данные были в памяти относительно 0x0000: 0x7c00.
  • Эти наблюдения эффективно суммируются как: код полезной нагрузки, вероятно, не сможетсделайте что-нибудь сложное, и попытки сделать это приведут вас к неожиданному поведению.Вы можете изменить код, чтобы устранить все эти недостатки, но это выходит за рамки этого ответа.
...