J'essaie de comprendre s'il est possible d'exécuter une machine virtuelle Linux dont la RAM n'est sauvegardée que par une seule page physique.
Pour simuler cela, j'ai modifié le gestionnaire de défaut de page imbriqué dans KVM pour supprimer le bit actuel de toutes les entrées de table de page imbriquée (NPT), à l'exception de celui correspondant au défaut de page actuellement traité.
En essayant de démarrer un invité Linux, j'ai observé que les instructions d'assemblage qui utilisent des opérandes de mémoire, comme
add [rbp+0x820DDA], ebp
conduire à une boucle de défaut de page jusqu'à ce que je restaure le bit actuel pour la page contenant l'instruction ainsi que pour la page référencée dans l'opérande (dans cet exemple [rbp+0x820DDA]
).
Je me demande pourquoi c'est le cas. Le CPU ne doit-il pas accéder aux pages mémoire de manière séquentielle, c'est-à-dire d'abord lire l'instruction puis accéder à l'opérande mémoire? Ou x86 nécessite-t-il que la page d'instructions ainsi que toutes les pages d'opérandes soient accessibles en même temps?
Je teste sur AMD Zen 1.
Réponses:
Oui, ils nécessitent le code machine et tous les opérandes de mémoire.
Oui, c'est logiquement ce qui se passe, mais une exception de défaut de page interrompt ce processus en 2 étapes et annule toute progression. Le CPU n'a aucun moyen de se rappeler de quelle instruction il s'agissait au milieu d'un défaut de page.
Lorsqu'un gestionnaire de défauts de page revient après avoir traité un défaut de page valide, RIP = l'adresse de l'instruction défaillante, de sorte que le processeur réessaye de l'exécuter à partir de zéro .
Il serait légal pour le système d'exploitation de modifier le code machine de l'instruction défaillante et de s'attendre à ce qu'il exécute une instruction différente à
iret
partir du gestionnaire de défaut de page (ou de toute autre exception ou gestionnaire d'interruption). Donc AFAIK, il est architecturalement nécessaire que le CPU refasse la récupération de code de CS: RIP dans le cas dont vous parlez. (En supposant qu'il retourne même au CS: RIP défaillant au lieu de planifier un autre processus en attendant le disque sur une erreur de page matérielle, ou en délivrant un SIGSEGV à un gestionnaire de signal sur une erreur de page non valide.)Il est probablement également architecturalement requis pour l'entrée / sortie de l'hyperviseur. Et même si ce n'est pas explicitement interdit sur le papier, ce n'est pas ainsi que les CPU fonctionnent.
@torek commente que certains microprocesseurs (CISC) décodent partiellement les instructions et vident l'état du micro-enregistrement sur une erreur de page , mais x86 n'est pas comme ça.
Quelques instructions sont interruptibles et peuvent faire des progrès partiels, comme
rep movs
(memcpy dans une boîte) et d'autres instructions de chaîne, ou rassembler des magasins de charges / scatter. Mais le seul mécanisme est la mise à jour des registres architecturaux comme RCX / RSI / RDI pour les opérations de chaîne, ou les registres de destination et de masque pour les regroupements (par exemple, manuel pour AVX2vpgatherdd
). Ne pas conserver l'opcode / décoder entraîne un registre interne caché et le redémarrer après iret à partir d'un gestionnaire de défauts de page. Ce sont des instructions qui effectuent plusieurs accès aux données distincts.Gardez également à l'esprit que x86 (comme la plupart des ISA) garantit que les instructions sont atomiques wrt. interruptions / exceptions: elles se produisent entièrement, ou ne se produisent pas du tout, avant une interruption. Interrompre une instruction d'assemblage pendant son fonctionnement . Ainsi, par exemple, il
add [mem], reg
serait nécessaire de rejeter la charge si la partie de stockage était défaillante, même sanslock
préfixe.Le pire des cas, le nombre de pages d'espace utilisateur invité présentes pour faire avancer le dossier peut être de 6 (plus des sous-arborescences de table de pages du noyau invité distinctes pour chacune):
movsq
oumovsw
instruction de 2 octets couvrant une limite de page, donc les deux pages sont nécessaires pour qu'elle décode.[rsi]
également un partage de page[rdi]
également un fractionnement de pageSi l'une de ces 6 pages fait défaut, nous revenons à la case départ.
rep movsd
est également une instruction de 2 octets, et faire des progrès sur une étape aurait la même exigence. Des cas similaires commepush [mem]
oupop [mem]
pourraient être construits avec une pile mal alignée.Une des raisons (ou avantages secondaires) pour / de rendre les charges de regroupement / magasins de dispersion "interruptibles" (en mettant à jour le vecteur de masque avec leur progression) est d'éviter d'augmenter cette empreinte minimale pour exécuter une seule instruction. Également pour améliorer l'efficacité de la gestion de plusieurs défauts lors d'une collecte ou d'une diffusion.
@Brandon souligne dans les commentaires qu'un invité aura besoin de ses tables de pages en mémoire , et les fractionnements de page de l'espace utilisateur peuvent également être des fractionnements de 1 Go, de sorte que les deux côtés se trouvent dans des sous-arborescences différentes du PML4 de niveau supérieur. La marche de page HW devra toucher toutes ces pages de table de pages d'invité pour progresser. Une situation aussi pathologique ne se produira probablement pas par hasard.
Le TLB (et les internes du marcheur de page) sont autorisés à mettre en cache certaines des données de la table de pages, et ne sont pas tenus de redémarrer le cheminement de page à partir de zéro, sauf si le système d'exploitation l'a fait
invlpg
ou a défini un nouveau répertoire de page de niveau supérieur CR3. Aucun de ces éléments n'est nécessaire lors du changement d'une page de non présent à présent; x86 sur papier garantit qu'il n'est pas nécessaire (donc la "mise en cache négative" des PTE non présents n'est pas autorisée, du moins invisible pour le logiciel). Ainsi, le processeur peut ne pas VMexit même si certaines des pages de table de pages physiques invitées ne sont pas réellement présentes.Les compteurs de performance PMU peuvent être activés et configurés de telle sorte que l'instruction nécessite également un événement perf pour une écriture dans un tampon PEBS pour cette instruction. Avec un masque de compteur configuré pour ne compter que les instructions de l'espace utilisateur, pas le noyau, il se pourrait bien qu'il continue d'essayer de déborder le compteur et de stocker un échantillon dans le tampon chaque fois que vous revenez dans l'espace utilisateur, produisant un défaut de page.
la source
push dword [foo
" (ou même justecall [foo]
) avec tout ce qui est mal aligné sur "la limite de la table du pointeur du répertoire de pages" (en ajoutant jusqu'à 6 pages, 6 tables de pages, 6 répertoires de pages, 6 PDPT et un PML4); avec la fonction «échantillonnage basé sur des événements précis avec tampon PEBS» du processeur activée et configurée de telle sorte que lespush
données de surveillance des performances soient ajoutées au tampon PEBS. Pour un conservateur "minimum de pages fournies par l'hôte afin que l'invité puisse progresser dans les cas pathologiques", je voudrais au moins 16 pages.EIP
. Il y a donc une question logique de suivi. Quel est le nombre minimum de pages nécessaires, en supposant un schéma de correctif d'instructions intelligent? Par exemple, copiez la valeur non alignée dans un tampon de travail aligné, émulez l'instruction et IRET dans l'instruction suivante.iret
instructions du système d'exploitation doit également être en mémoire. Il s'agit d'une instruction d'un octet, donc d'une page supplémentaire. L'adresse d'interruption du gestionnaire de défauts de page doit également être en mémoire, mais il peut s'agir de la même page que ci-dessus.