Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Username: Password: oppure
C/C++ - Uefi e ambiente di lavoro.
Forum - C/C++ - Uefi e ambiente di lavoro.

Avatar
TheDarkJuster (Member)
Guru^2


Messaggi: 1620
Iscritto: 27/09/2013

Segnala al moderatore
Postato alle 23:21
Sabato, 18/03/2017
Buona sera,
Mi sto dilettando a scrivere un so per x64 che USA uefi per farsi arrivare.

Fin qui tutto bene, ho anche scritto una funzione che mi stampa a video lo stato dei registri rax, rbx, cr0 e cr3.

Ora, sono consapevole che la modalità lunga (il funzionamento nativo delle CPU x64) prevede la paginazione sempre attiva, e infatti stampando cr0 vedo che i bit riguardanti la paginazione sono settati.

So che in modalità x64 la gdt è uguale alla x86, quindi non può descrivere tutta la memoria, quindi voglio:

Caricare la mia gdt, che rende "di libero accesso" tutta la memoria che può essere descritta con la gdt (perché voglio gestire solo la paginazione)

Ora... Non riesco a impostare la mia gdt.

A tal proposito mi sono informato: http://wiki.osdev.org/Global_Descriptor_Table e vedo che nel descrittore della gdt il riferimento all'inizio della gdt è espresso come indirizzo virtuale (logico, visto che in x64 disattivare la paginazione porta la CPU in uno stato di errore), quindi ho creato dello spazio all'interno del mio programma uefi e lo uso per contenere la gdt.

Il problema è che dopo aver fatto lgdt il sistema si riavvia, quindi sicuramente qualcosa sbaglio....

Suppongo che, per i motivi sopra elencati anche l'indirizzo del descrittore della gdt sia da intendere virtuale, ed è ciò che passo alla istruzione lgdt, eppure non funziona....

Qualcosa mi è sfuggito?

PM Quote
Avatar
TheDarkJuster (Member)
Guru^2


Messaggi: 1620
Iscritto: 27/09/2013

Segnala al moderatore
Postato alle 0:48
Domenica, 19/03/2017
Il codice che uso è:


Codice sorgente - presumibilmente C++

  1. typedef struct _gdt_entry
  2. {
  3.         uint16_t limit_low;
  4.         uint16_t base_low;
  5.         uint8_t base_middle;
  6.         uint8_t access;
  7.         uint8_t granularity;
  8.         uint8_t base_high;
  9. } __attribute__((packed)) gdt_entry_t;
  10.  
  11. typedef struct _gdt_desc {
  12.   uint16_t size;
  13.   uint32_t offset;
  14. } __attribute__((packed)) gdt_desc_t;
  15.  
  16. static gdt_entry_t gdt[32];
  17. static gdt_desc_t gdt_desc;
  18. static uint16_t gdt_num_entry;
  19.  
  20. void load_gdt(uint64_t gdt_addr);
  21.  
  22. void gdt_setup()
  23. {
  24.   //setup the GDT descriptor
  25.   gdt_desc.size = 0;
  26.   gdt_desc.offset = &gdt;
  27.  
  28.   gdt_num_entry = 0;
  29. }
  30.  
  31. void gdt_add_entry(uint32_t base, uint32_t limit, uint8_t access, uint8_t granularity) {
  32.         gdt[gdt_num_entry].base_low = (base & 0xFFFF);
  33.         gdt[gdt_num_entry].base_middle = (base >> 16) & 0xFF;
  34.         gdt[gdt_num_entry].base_high = (base >> 24) & 0xFF;
  35.         gdt[gdt_num_entry].limit_low = (limit & 0xFFFF);
  36.         gdt[gdt_num_entry].granularity = ((limit >> 16) & 0x0F) | (granularity & 0xF0);
  37.         gdt[gdt_num_entry].access = access;
  38.  
  39.        gdt_num_entry++; // la prossima chiamata riempira il prossimo "segmento" di GDT
  40. }
  41.  
  42. void gdt_apply() {
  43.   gdt_desc.size = (sizeof(gdt_entry_t) * gdt_num_entry) -1;
  44.  
  45.   load_gdt(&gdt_desc);
  46. }



load_gdt è scritta in assembly (nasm):

Codice sorgente - presumibilmente C/C++

  1. BITS 64
  2.  
  3. GLOBAL load_gdt
  4.  
  5. load_gdt:
  6.   lgdt  [rax]           ; load new GDT pointer
  7.   ret



e nell'entry point EFI:

Codice sorgente - presumibilmente C/C++

  1. gdt_setup();
  2.         gdt_add_entry(0, 0, 0, 0);                // null - questo ci va, punto e basta.
  3.         gdt_add_entry(0, 0xFFFFFFFF, 0x9A, 0xCF); // ring0 code a tutta la memoria
  4.         gdt_add_entry(0, 0xFFFFFFFF, 0x92, 0xCF); // ring0 data a tutta la memoria
  5.         gdt_add_entry(0, 0xFFFFFFFF, 0xFA, 0xCF); // ring3 code a tutta la memoria
  6.         gdt_add_entry(0, 0xFFFFFFFF, 0xF2, 0xCF); // ring3 data a tutta la memoria
  7.   gdt_apply();
  8.  
  9.   Print(L"GPT loaded.");
  10.  
  11. for (;;) // un bel ciclo infinito



la scritta "GPT loaded." appare, ma invece di ciclare all'infinito la vm si riavvia, mentre se tolgo gdt_apply() tutto funziona come previsto.

Ultima modifica effettuata da TheDarkJuster il 19/03/2017 alle 0:51
PM Quote
Avatar
nessuno (Normal User)
Guru^2


Messaggi: 6402
Iscritto: 03/01/2010

Segnala al moderatore
Postato alle 11:47
Domenica, 19/03/2017
Dopo la lgdt la ret non ritorna dove ti aspetti. Quello che è stato messo nello stack prima della lgdt NON vale dopo la lgdt.
Sicuramente una ret di una call fatta prima di cambiare modalità fa un disastro.

La lgdt non va chiamata in quel modo ma nel main (nel flusso principale programma) e va seguita da una

jmp GDT64.Code:Target64

che passi al segmento in long mode appena creato. E da lì parte l'inizializzazione di stack, registri e via verso il codice 64


Ricorda che nessuno è obbligato a risponderti e che nessuno è perfetto ...
---
Il grande studioso italiano Bruno de Finetti ( uno dei padri fondatori del moderno Calcolo delle probabilità ) chiamava il gioco del Lotto Tassa sulla stupidità.
PM Quote
Avatar
TheDarkJuster (Member)
Guru^2


Messaggi: 1620
Iscritto: 27/09/2013

Segnala al moderatore
Postato alle 13:43
Domenica, 19/03/2017
Volevo evitare di fare tutto nel main.

Ho visto questo: https://github.com/luksow/OS e volevo riproporre la stessa cosa per un sistema x64, però alcune cose non mi sono chiare:
1) perchè fa [esp+4] per trovare l'indirizzo della gdt? La calling convention non dovrebbe specificare che è in eax?
2) perchè lui riesce a saltare indietro al main e io no?

PM Quote