Que contient l'adresse physique 0 sous Linux x86?

12

Je ne sais pas si cette question doit aller ici ou dans reverseengineering.stackexchange.com

Citant de wikipedia :

Dans le processeur 8086, la table d'interruption est appelée IVT (table de vecteur d'interruption). L'IVT réside toujours au même emplacement dans la mémoire, allant de 0x0000 à 0x03ff, et se compose de 256 pointeurs éloignés en mode réel à quatre octets (256 × 4 = 1024 octets de mémoire).

C'est ce que je trouve dans le moniteur qemu:

(qemu) xp/128xw 0
0000000000000000: 0xf000ff53 0xf000ff53 0xf000e2c3 0xf000ff53
0000000000000010: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000020: 0xf000fea5 0xf000e987 0xf000d62c 0xf000d62c
0000000000000030: 0xf000d62c 0xf000d62c 0xf000ef57 0xf000d62c
0000000000000040: 0xc0005526 0xf000f84d 0xf000f841 0xf000e3fe
0000000000000050: 0xf000e739 0xf000f859 0xf000e82e 0xf000efd2
0000000000000060: 0xf000d648 0xf000e6f2 0xf000fe6e 0xf000ff53
0000000000000070: 0xf000ff53 0xf000ff53 0xf0006aa4 0xc0008930
0000000000000080: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000090: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000000a0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000000b0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000000c0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000000d0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000000e0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000000f0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000100: 0xf000ec59 0xf000ff53 0xf000ff53 0xc0006730
0000000000000110: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000120: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000130: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000140: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000150: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000160: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000170: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
0000000000000180: 0x00000000 0x00000000 0x00000000 0x00000000
0000000000000190: 0x00000000 0x00000000 0x00000000 0xf000ff53
00000000000001a0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000001b0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53
00000000000001c0: 0xf000d611 0xf000ec4e 0xf000ec4e 0xf000ec4e
00000000000001d0: 0xf000d61a 0xf000d623 0xf000d608 0xf000ec4e
00000000000001e0: 0xf000ff53 0x00000000 0xf000ff53 0xf000ff53
00000000000001f0: 0xf000ff53 0xf000ff53 0xf000ff53 0xf000ff53

Je ne sais pas quoi faire de ces valeurs. Il ne ressemble pas à une table de descripteurs d'interruption (le déréférencement de ces valeurs donne tous les nulls). Alors qu'est-ce que je regarde ici?

rhodeo
la source

Réponses:

9

Tout ce que votre micrologiciel a laissé.

Sur un système moderne idéal, le processeur ne passe jamais du tout en mode réel, comme je l'ai expliqué dans cette Q&R SU intitulée: Dans quel mode les PC à puce Intel 64 bits modernes exécutent-ils le secteur de démarrage? , le premier Kio de mémoire physique est aussi hors de propos que Johan Myréen a prétendu être dans une autre réponse ici. Mais de nombreux firmwares modernes prennent (encore) en charge la compatibilité , ce qui signifie que

  • ils peuvent revenir en arrière (oui, en arrière , étant donné qu'ils sont passés directement du mode irréel au mode protégé) du mode protégé au mode réel afin d'exécuter des logiciels système écrits pour le mode réel, tels que les anciens programmes de démarrage PC / AT dans MBR et VBR; et
  • ils fournissent les anciennes API de micrologiciel en mode réel et configurent toutes les structures de données pour ces API, sur lesquelles s'appuient les logiciels système susmentionnés.

L'une de ces structures de données est le mode réel IVT. Les anciennes API de micrologiciel en mode réel sont basées sur des intinstructions, et l'IVT en mode réel est rempli par le micrologiciel dans le cadre de son initialisation avec des pointeurs vers les diverses routines de gestion du micrologiciel pour ces instructions.

Les logiciels du système en mode protégé n'ont pas besoin des anciennes API de micrologiciel en mode réel et n'exécutent jamais le processeur en mode réel, de sorte que l'IVT en mode réel dans le premier 1 Ko de mémoire physique n'est pas utilisé. (Le mode protégé v8086 n'adresse pas l'adresse physique 00000000 et plus, souvenez-vous. Il adresse les adresses logiques 00000000 et plus, qui sont traduites par des tableaux de pages.) Dans les systèmes EFI modernes, le micrologiciel remet une carte mémoire de mémoire physique au système d'exploitation bootstrap, en lui indiquant quelles parties sont réservées au micrologiciel à ses propres fins d'API en mode protégé, et quelles parties le système d'exploitation est libre d'aller de l'avant et d'utiliser pour son pool de mémoire physique. En théorie, la première page de mémoire physique peut appartenir à cette dernière catégorie.

En pratique, premièrement, les firmwares marquent souvent la première page de la mémoire physique comme "code des services de démarrage", ce qui signifie qu'un système d'exploitation peut le revendiquer et simplement l'utiliser et faire partie de son pool de mémoire physique, mais seulement après le démarrage. les services horaires du micrologiciel EFI ont été arrêtés par le système d'exploitation et le micrologiciel a été réduit à fournir ses services d'exécution uniquement. Un exemple de cela peut être vu dans le journal du noyau Linux (avec l' add_efi_memmapoption) montré par Finnbarr P. Murphy:

[0.000000] efi: mem00: type = 3, attr = 0xf, range = [0x0000000000000000-0x0000000000001000) (0 Mo)
qui xe décode avec un autre programme sous une forme plus lisible par l'homme comme:

[# 00] Type: EfiBootServicesCode Attr: 0xF
      Phys: 0000000000000000-0000000000001000
      Virt: 0000000000000000-0000000000001000

Dans la pratique, deuxièmement, Linux ignore explicitement cette plage de mémoire physique même si le micrologiciel dit qu'il peut aller de l'avant et l'utiliser. Vous constaterez que sur les firmwares EFI et non EFI, une fois que Linux a la carte de mémoire physique, il la corrige ( dans une fonction nomméetrim_bios_range ), ce qui entraîne des messages de journal du noyau tels que:

[0.000000] e820: mise à jour [mem 0x00000000-0x00000fff] utilisable ==> réservé

Ce n'est pas tant pour faire face aux firmwares EFI modernes, où le mode réel IVT ne fait pas partie de l'API du firmware, mais pour faire face aux anciens firmwares PC98, où il fait partie de l'API du firmware mais les firmwares le signalent (via cette même API) en tant que mémoire physique disponible pour être écrasée par le système d'exploitation.

Ainsi, bien qu'en théorie, cette plage de mémoire physique puisse contenir du code ou des données arbitraires, en fonction des besoins momentanés des allocateurs de mémoire du noyau et de la mémoire virtuelle paginée à la demande; en pratique, Linux ne le laisse pas tel que le firmware l'a configuré à l'origine.

Et sur votre système, le micrologiciel l'avait rempli d'entrées IVT en mode réel. Les entrées IVT en mode réel ne sont que des pointeurs éloignés de 16:16, bien sûr, et si vous regardez votre mémoire à l'aide d'un vidage hexadécimal de 2 octets, vous pouvez réellement voir cela assez clairement. Quelques exemples:

  • La plupart de vos entrées IVT pointent vers F000: FF53, une adresse dans la zone ROM du micrologiciel en mode réel. C'est probablement une routine factice qui ne fait rien de plus qu'un iret.
  • L'entrée IVT 1E pointe vers F000: 6AA4, une table dans cette même zone ROM.
  • L'entrée IVT 1F pointe vers C000: 8930, un tableau dans la zone du micrologiciel de la ROM vidéo en mode réel.
  • L'entrée IVT 43 pointe vers C000: 6730, un autre tableau dans la zone du firmware de la ROM vidéo en mode réel.

Lectures complémentaires

JdeBP
la source
Non, je veux dire ce que j'ai écrit. Intel Architecture Software Developers 'Manual volume 3 chapitre 20 § 2.
JdeBP
Eh bien, vous avez maintenant, parce que c'est; comme l'explique la première phrase de cette section. J'en soupçonne que la non-reconnaissance de l'abréviation commune "v8086" est une sorte de shibboleth. (-:
JdeBP
Vous devez apprendre à lire les noms attributifs. Ou bien apprenez à vivre sans soupe aux champignons.
JdeBP
7

L'architecture du processeur 8086 d'origine (implémentée en mode réel dans les processeurs 80286+) n'a aucune pertinence pour Linux, qui fonctionne en mode protégé. Il n'y a pas de table de vecteurs d'interruption à l'adresse physique 0, mais une table de descripteurs d'interruption contenant des descripteurs d'interruption est utilisée. L'IDT peut être situé n'importe où dans la mémoire.

Le noyau Linux obtient une carte de mémoire physique du micrologiciel (BIOS ou EFI) qui indique quels cadres de page de mémoire physique sont utilisables et lesquels sont réservés ou non présents. La plage de cadres de page utilisables n'est pas contiguë, mais comporte généralement d'énormes trous. Traditionnellement, le noyau Linux x86 a ignoré le début de la mémoire physique, même si elle est marquée comme utilisable. Ainsi, l'adresse physique 0 n'est pas utilisée par le noyau Linux.

Johan Myréen
la source
C'est logique. Une idée du contenu restant de cette page inutilisée?
rhodeo
La recherche sur Google 53 ffrévèle qu'il s'agit très probablement d'une table de vecteurs d'interruption en mode réel 8086 configurée par le micrologiciel ou un chargeur de démarrage.
Johan Myréen
4

Mémoire de vidage

Voici une autre façon de vider le contenu de la mémoire à l'intérieur du système par rapport à le faire en externe:

$ head /dev/mem | hexdump -C
00000000  53 ff 00 f0 53 ff 00 f0  53 ff 00 f0 53 ff 00 f0  |S...S...S...S...|
00000010  53 ff 00 f0 53 ff 00 f0  cc e9 00 f0 53 ff 00 f0  |S...S.......S...|
00000020  a5 fe 00 f0 87 e9 00 f0  53 ff 00 f0 46 e7 00 f0  |........S...F...|
00000030  46 e7 00 f0 46 e7 00 f0  57 ef 00 f0 53 ff 00 f0  |F...F...W...S...|
00000040  22 00 00 c0 4d f8 00 f0  41 f8 00 f0 fe e3 00 f0  |"...M...A.......|
00000050  39 e7 00 f0 59 f8 00 f0  2e e8 00 f0 d4 ef 00 f0  |9...Y...........|
00000060  a4 f0 00 f0 f2 e6 00 f0  6e fe 00 f0 53 ff 00 f0  |........n...S...|
00000070  ed ef 00 f0 53 ff 00 f0  c7 ef 00 f0 ed 57 00 c0  |....S........W..|
00000080  53 ff 00 f0 53 ff 00 f0  53 ff 00 f0 53 ff 00 f0  |S...S...S...S...|
...
...
000afea0  00 00 00 00 00 00 00 00  aa aa aa 00 aa aa aa 00  |................|
000afeb0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000b0000  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
*
000c0000  55 aa 40 e9 62 0a 00 00  00 00 00 00 00 00 00 00  |[email protected]...........|
000c0010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 49 42  |..............IB|

Une analyse

La partie supérieure au-dessus de 000c0000 pourrait être liée au chargeur de démarrage. Pourquoi devrais-je soupçonner cela? Le code 55aah à l'emplacement 000c0000peut généralement être une marque en mémoire pour des choses telles qu'un déclencheur pour que le BIOS exécute un chargeur de démarrage secondaire.

Référence: Boot Signature - BIOS

  SS # 1

Cependant, étant donné que 55aah se produit dans la plage c0000h-effffh, il est plus probable que cette partie soit l'en-tête d'extension PNP:

Référence: spécification de démarrage du BIOS

3.3 Dispositifs avec en-têtes d'extension PnP

Tous les périphériques IPL avec ROM en option doivent contenir un en-tête de ROM en option valide qui réside entre les adresses de mémoire système C0000h et EFFFFh sur une limite de 2k et commence par 55AAh. Le démarrage d'un appareil ne peut être contrôlé que s'il possède un en-tête d'extension PnP. L'en-tête d'extension, dont l'adresse réside dans l'en-tête ROM de l'option standard à décalage + 1 Ah, contient des informations importantes utilisées pour configurer le périphérique. Il contient également des pointeurs à coder dans la ROM optionnelle du périphérique (BCV ou BEV) que le BIOS appellera pour démarrer à partir du périphérique. Voir l'annexe A pour la structure de l'en-tête d'extension PnP. Il existe deux façons de démarrer un périphérique IPL avec un en-tête d'extension PnP. Il doit contenir un BCV ou un BEV.

53ff ...

Quant aux données 53ffh qui sont au début. Pour moi, ce n'est pas clair. En poursuivant ses recherches, il est probable que le noyau Linux y ait écrit après le chargement du MBR par le BIOS remis au noyau Linux pour démarrer.

Habituellement, le chargeur de démarrage charge le noyau en mémoire, puis passe au noyau. Le noyau pourra alors récupérer la mémoire utilisée par le chargeur de démarrage (car il a déjà effectué son travail). Cependant, il est possible d'inclure le code du système d'exploitation dans le secteur de démarrage et de le conserver résident après le début du système d'exploitation

En creusant davantage, j'ai pu trouver ce paragraphe dans un document de recherche intitulé: Injection de code malveillant via / dev / mem :

1 L'appareil mem

/ dev / mem est l'interface du pilote vers la mémoire physiquement adressable. L'intention initiale de mem et kmem était d'aider au débogage du noyau. Nous pouvons utiliser le périphérique comme un périphérique de caractère normal, en utilisant lseek () pour sélectionner un décalage d'adresse. Le périphérique kmem est similaire mais fournit une image de la mémoire du noyau dans le contexte de l'adressage virtuel. Le serveur Xorg utilise le périphérique mem pour accéder à la mémoire vidéo VESA ainsi qu'à la table de vecteur d'interruption ROM ROM (IVT) située à l'adresse physique 0x00000000 pour manipuler les modes vidéo en mode VM86. DOSEMU l'utilise également pour accéder au BIOS IVT afin de pouvoir effectuer des interruptions du BIOS pour diverses tâches (lectures de disque, impression sur la console, etc.).

Les références

slm
la source