2017-07-08 3 views
1

je ne peux pas trouver un moyen de se déplacer le code d'un endroit à un autre dans la mémoireinstruction de déplacement de EAX à registre es donne erreur

donc je mets une façon quelque chose comme ça, mais ça ne fonctionne pas

extern _transfer_code_segment 

extern _kernel_segment 

    extern _kernel_reloc 


extern _kernel_reloc_segment 

    extern _kernel_para_size 


    section .text16 



    global transfer_to_kernel 




transfer_to_kernel: 



    ;cld 

    ; 
    ; Turn off interrupts -- the stack gets destroyed during this routine. 
    ; kernel must set up its own stack. 
    ; 
    ;cli 
    ; stack for only for this function 

    push ebp 
    mov ebp, esp 








    mov eax, _kernel_segment    ; source segment 
    mov ebx, _kernel_reloc_segment  ; dest segment 
    mov ecx, _kernel_para_size 

.loop: 



    ; XXX: Will changing the segment registers this many times have 
    ; acceptable performance? 


    mov ds, eax ;this the place where the error 
    mov es, ebx ; this to 
    xor esi, esi 
    xor edi, edi 
    movsd 
    movsd 
    movsd 
    movsd 
    inc eax 
    inc ebx 
    dec ecx 
    jnz .loop 



    leave 
    ret 

n'ont pas d'autre façon de le faire ou comment puis-je résoudre ce problème

Répondre

1

Cela aura une performance horrible. Agner Fog dit mov sr, r a un débit par cycle de 13 sur Nehalem, et je suppose que, si quelque chose est pire sur les processeurs plus récents depuis la segmentation est obsolète. Agner a cessé de tester mov de/à partir de la performance du registre de segment après Nehalem. Faites-vous cela pour vous permettre de copier plus de 64 Ko au total? Si c'est le cas, copiez au moins 64 Ko avant de changer un registre de segment.Je pense que vous pouvez utiliser des modes d'adressage 32 bits pour éviter de jouer avec des segments, mais implicitement les segments que vous définissez en mode 16 bits ont une "limite" de 64k. (ie mov eax, [esi] est encodable en mode 16 bits, avec un préfixe de taille d'opérande et de taille d'adresse Mais avec une valeur en esi de plus de 0xFFFF, je pense qu'il y aurait un défaut de violation de la limite ds.) Le lien osdev ci-dessous pour plus. Comme le dit Cody, utilisez rep movsd pour laisser la CPU utiliser un microcode optimisé memcpy. (or rep movsb, but only on CPUs with the ERMSB feature. Dans la pratique, most CPUs that support ERMSB give the same performance benefit for rep movsd too, donc il est probablement plus facile à utiliser juste toujours rep movsd. Mais Ivybridge peut-être pas.) Il est beaucoup plus rapide quemovsd séparées instructions (qui sont plus lents que mov séparés charges/magasins). Une boucle avec des charges/magasins vectoriels SSE 16B peut aller presque aussi vite que rep movsd sur certaines CPU, mais vous ne pouvez pas utiliser AVX pour les vecteurs 32B en mode 16 bits.


Une autre option pour les grandes copies: énorme en mode irréel

En mode protégé 32 bits, les valeurs que vous mettez dans les segments sont des descripteurs, pas la base du segment réel lui-même. mov es, ax déclenche le processeur pour utiliser la valeur comme un index dans le GDT ou LDT et obtenir la base/limite de segment à partir de là. Si vous faites cela en mode 32 bits, puis revenez en mode 16 bits, vous êtes dans un mode irréel énorme avec des segments qui peuvent être supérieurs à 64k. La base/limite/permissions du segment reste en mémoire cache jusqu'à ce que quelque chose écrit un registre de segment en mode 16 bits et le remette à 16*seg habituel avec une limite de 64k. (Si je le décris correctement). Voir http://wiki.osdev.org/Unreal_Mode pour plus.

Ensuite, vous pouvez utiliser rep movsd en mode 16 bits avec des préfixes de taille d'opérande et de taille d'adresse afin de pouvoir copier plus de 64 Ko en une fois.

Cela fonctionne bien pour ds et es, mais interrupts will set cs:ip, donc ce n'est pas pratique pour un grand espace d'adressage de code à plat, juste des données.

+0

merci l'homme, grand mode réel aidé mais quand je reviens de la fonction il ne revient pas – sakura

+0

@sakura: J'ai presque intérêt zéro dans le mode 16 bits obsolète. Je continue à lire à ce sujet dans les questions SO, ce qui est la seule raison pour laquelle j'ai pu écrire cette réponse (heureux que cela a aidé, BTW). Tout ce que je peux recommander est d'avoir un bon débogueur, par exemple. celui intégré à BOCHS, de sorte que vous pouvez faire un seul pas sur votre noyau. –

+1

Après plus de détails sur cette question sont devenus clairs, je ne peux pas m'empêcher de me demander pourquoi vous ne passez pas simplement en mode protégé 32 bits avant de faire cette copie. Vraisemblablement, c'est ce que le noyau va finalement faire de toute façon. Pourquoi voudriez-vous revenir en mode réel? Même en "mode irréel", autant que je sache, vous ne pouvez pas dépasser 1 Mo, donc ce n'est pas vraiment une solution miracle. @sakura –

3

Les registres de segment sont tous en taille 16 bits. Comparez cela aux registres e?x, qui ont une taille de 32 bits. Évidemment, ces deux choses n'ont pas la même taille, ce qui incite votre assembleur à générer une erreur de "non-concordance de taille d'opérande" - les tailles des deux opérandes ne correspondent pas.

On peut supposer que vous voulez initialiser le registre de segment avec les 16 bits inférieurs du registre, de sorte que vous feriez quelque chose comme:

mov ds, ax 
mov es, bx 

En outre, non, vous n'avez pas réellement besoin d'initialiser le segment enregistre à chaque itération de la boucle. Ce que vous faites maintenant est d'incrémenter le segment et de forcer le décalage à 0, puis de copier 4 DWORD. Ce que vous devriez faire est de laisser le segment seul et d'incrémenter le offset (que l'instruction MOVSD fait implicitement).

mov eax, _kernel_segment    ; TODO: see why these segment values are not 
    mov ebx, _kernel_reloc_segment  ;  already stored as 16 bit values 
    mov ecx, _kernel_para_size 

    mov ds, ax 
    mov es, bx 

    xor esi, esi 
    xor edi, edi 

.loop: 

    movsd 
    movsd 
    movsd 
    movsd 

    dec ecx 
    jnz .loop 

Mais notez que l'ajout du REP prefix à l'instruction MOVSD vous permettra de le faire encore plus efficacement. Cela fait MOVSD un total de ECX fois. Par exemple:

mov ds, ax 
mov es, bx 
xor esi, esi 
xor edi, edi 
shl ecx, 2   ; adjust size since we're doing 1 MOVSD for each ECX, rather than 4 
rep movsd 

Un peu contre-intuitive, si votre processeur implémente the ERMSB optimization (Intel Ivy Bridge et versions ultérieures), REP MOVSB peut effectivement être plus rapide que REP MOVSD, pour que vous puissiez faire:

mov ds, ax 
mov es, bx 
xor esi, esi 
xor edi, edi 
shl ecx, 4 
rep movsb 

Enfin, Bien que vous ayez commenté l'instruction CLD dans votre code, vous devez l'avoir afin de vous assurer que les mouvements se déroulent conformément au plan. Vous ne pouvez pas compter sur le drapeau de direction ayant une valeur particulière; vous devez l'initialiser vous-même à la valeur que vous voulez.

(Une autre alternative serait de diffuser des instructions SIMD ou même des mémoires à virgule flottante, dont aucune ne se soucierait de l'indicateur de direction, ce qui présente l'avantage d'augmenter la bande passante de copie mémoire car bit, ou des copies plus grandes à la fois, mais présente d'autres inconvénients.En cas de noyau, je m'en tiens à MOVSD/MOVSB sauf si vous pouvez prouver n'est pas un goulot d'étranglement significatif et/ou vous voulez avoir des chemins optimisés pour différents processeurs.)

+0

La valeur de _kernel_segment est une adresse 32 bits, donc si j'utilise des 16 bits inférieurs comme vous dans "mov ds, ax" cela poserait un problème – sakura

+0

Pourquoi est-ce une valeur de 32 bits? Les segments sont seulement 16 bits. Eh bien, je suppose que vous devrez faire l'arithmétique des segments vous-même; voir: http://thestarman.pcministry.com/asm/debug/Segments.html –

+0

Bien que légitimes, les valeurs 32 bits 'esi, edi et ecx' sont sans conséquence. Même si vous avez défini ECX sur 1FC00, disons que les pointeurs se contenteraient de parcourir toutes les itérations 65536 ou 32768 si vous déplacez des mots ou 16384 si vous déplacez dwords. Il n'y a aucun moyen de contourner ceci à moins qu'il y ait une certaine logique pour ajuster les registres de segment en conséquence. –