From daa5a43c4d1e4bcf62daf948248aed10c77ac5a5 Mon Sep 17 00:00:00 2001
From: Thierno-Souleymane BAH <thiernosouleymane.bah.etu@univ-lille.fr>
Date: Wed, 10 Feb 2021 11:20:22 +0100
Subject: [PATCH] feat(master mode and user mode scenario test done)

---
 MMUConfig.h              |   8 +++
 MMUConfig.ini            | 135 +++++++++++++++++++++++++++++++++++++++
 Makefile                 |   7 +-
 mi_kernel.c              |  85 ++++++++++++++++++++++++
 process.h => mi_kernel.h |  24 +++----
 mi_syscall.h             |  11 ++++
 mi_user.c                |  38 +++++++++++
 mi_user.h                |  12 ++++
 process.c                |  40 ------------
 x86-64/lib/libhardware.a | Bin 118486 -> 119034 bytes
 10 files changed, 307 insertions(+), 53 deletions(-)
 create mode 100644 MMUConfig.h
 create mode 100644 MMUConfig.ini
 create mode 100644 mi_kernel.c
 rename process.h => mi_kernel.h (68%)
 create mode 100644 mi_syscall.h
 create mode 100644 mi_user.c
 create mode 100644 mi_user.h
 delete mode 100644 process.c

diff --git a/MMUConfig.h b/MMUConfig.h
new file mode 100644
index 0000000..b5b8ee5
--- /dev/null
+++ b/MMUConfig.h
@@ -0,0 +1,8 @@
+#define MMU_ENABLE 1
+#define MMU_IRQ	13
+#define MMU_CMD		 0x66
+#define MMU_FAULT_ADDR	 0xCD
+#define TLB_ADD_ENTRY	 0xCE
+#define TLB_DEL_ENTRY	 0xDE
+#define TLB_SIZE       	 32		
+#define TLB_ENTRIES	 0x800		       
diff --git a/MMUConfig.ini b/MMUConfig.ini
new file mode 100644
index 0000000..a35ae17
--- /dev/null
+++ b/MMUConfig.ini
@@ -0,0 +1,135 @@
+# $Id: hardware.ini 114 2009-12-01 13:06:43Z simon_duquennoy $
+#------------------------------------------------------------
+
+#
+# Hardware.cfg
+#  Fichier de configuration du simulateur de mat�riel
+#
+
+#
+# Trace de Debug
+#
+# define DEBUG_SETUP   0x0001 /* trace hardware setup */
+# define DEBUG_IT      0x0010 /* trace interruptions generation */
+# define DEBUG_REG     0x0100 /* trace hardware register access */
+# define DEBUG_WARNING 0x1000 /* trace hardware warning messages */
+DEBUG          = 0x0000
+
+#
+# Configuration des param�tres de base du simulateur  
+#
+SYSTICK        = 1000          # delais entre deux ticks du simulateur (en micro-seconde) 
+
+#
+# Configuration des mat�riels r�seau
+#
+# configuration des ports COM (port serie de type RS232)
+# > Port Serie n�1
+SL1_ENABLE      = 0            # SL1_ENABLE = 0 => simulation lien serie d�sactiv�e
+SL1             = "chimay.lifl.fr"     # Machine connect� � la sortie du lien     serie n�1
+SL1_COM         = 1            # destinataire 1 = lien serie "com1", 2 = lien serie "com2" 
+SL1_NOISE       = 500          # bruit de la ligne 0-999 (exprim� en /1000 d'erreur)
+SL1_IRQ         = 4            # niveau d'interruption de l'UART          serie n�1
+SL1_UARTSR      = 0x3F8        # registre de status de l'UART             serie n�1    
+SL1_UARTDATA    = 0x3FA        # registre d'entr�e sortie de l'UART       serie n�1
+SL1_UDPPORT     = 1500         # port UDP "r�el" utiliser pour simulation serie n�1
+# > Port Serie n�2
+SL2_ENABLE      = 0            # SL2_ENABLE = 0 => simulation lien serie d�sactiv�e
+SL2             = "ldx2"       # Machine connect� � la sortie du lien     serie n�2
+SL2_COM         = 2            # destinataire 1 = lien serie "com1", 2 = lien serie "com2" 
+SL2_NOISE       = 0            # bruit de la ligne 0-999 (exprim� en /1000 d'erreur)
+SL2_IRQ         = 5            # niveau d'interruption de l'UART          serie n�2
+SL2_UARTSR      = 0x3FC        # registre de status de l'UART             serie n�2    
+SL2_UARTDATA    = 0x3FE        # registre d'entr�e sortie de l'UART       serie n�2
+SL2_UDPPORT     = 1501         # port UDP "r�el" utiliser pour simulation serie n�2
+# > Configuration de la carte Ethernet
+ENABLE_ETHERNET = 0            # ENABLE_ETHERNET = 0 => simulation ethernet d�sactiv�e
+Eth0_Link       = 1            # num de cable de connexion de la carte Eth0 
+Eth0_DMASR      = 0xE800       # registre de status de la carte Ethernet
+Eth0_DMABASE    = 0xE804       # adresse de base pour le vidage d'un paquet 
+Eth0_DMASIZE    = 0xE808       # adresse limite pour le vidage d'un paquet
+Eth0_IRQ        = 9            # niveau d'interruption de la carte.  
+Eth0_MCADR      = "225.0.0.1"  # adresse multicast utilis�e pour la simulation ethernet
+Eth0_UDPPORT    = 1502         # port UDP utilis� pour la simulation ethernet
+
+#
+# Configuration des disques durs
+#
+
+# > Disque dur IDE Maitre
+ENABLE_HDA      = 1            # ENABLE_HD = 0 => simulation du disque d�sactiv�e
+HDA_FILENAME    = "vdiskA.bin" # nom du fichier de stockage du disque simul�
+HDA_CMDREG      = 0x3F6        # registre de commande du disque maitre
+HDA_DATAREGS    = 0x110        # base des registres de donn�es (r,r+1,r+2,...r+7)
+HDA_IRQ         = 14           # Interruption du disque
+HDA_MAXCYLINDER = 16           # Nombre de pistes du disque ma�tre 
+HDA_MAXSECTOR   = 16           # Nombre de secteurs du disque ma�tre
+HDA_SECTORSIZE  = 32           # Taille (en octet) d'un secteur du disque ma�tre
+HDA_STPS        = 2            # nombre de SYSTICK pour changer de secteur
+HDA_STPC        = 1            # nombre de SYSTICK pour changer de piste 
+HDA_PON_DELAY   = 30           # nombre de SYSTICK avant amorce du disque
+HDA_POFF_DELAY  = 30           # nombre de SYSTICK avant arret du disque
+
+# > Disque dur IDE Esclave
+ENABLE_HDB      = 1            # ENABLE_HD = 0 => simulation du disque d�sactiv�e
+HDB_FILENAME    = "vdiskB.bin" # nom du fichier de stockage du disque simul�
+HDB_CMDREG      = 0x376        # registre de commande du disque esclave
+HDB_DATAREGS    = 0x170        # base des registres de donn�es (r,r+1,r+2,...r+7)
+HDB_IRQ         = 15           # Niveau d'interruption du disque
+HDB_MAXCYLINDER = 16           # Nombre de pistes du disque esclave 
+HDB_MAXSECTOR   = 16           # Nombre de secteurs du disque esclave
+HDB_SECTORSIZE  = 512          # Taille (en octet) d'un secteur du disque esclave
+HDB_STPS        = 2            # nombre de SYSTICK pour changer de secteur
+HDB_STPC        = 3            # nombre de SYSTICK pour changer de piste 
+HDB_PON_DELAY   = 30           # nombre de SYSTICK avant amorce du disque
+HDB_POFF_DELAY  = 30           # nombre de SYSTICK avant arret du disque
+
+#
+# Configuration de l'horologe interne
+#
+TIMER_CLOCK     = 0xF0         # registre de lecture de la date courante (en ms)
+TIMER_PARAM     = 0xF4         # registre de configuration du TIMER
+                               # bit 7 : RESET general (=1)
+                               # bit 6 : Alarm ON = 1, Alarm OFF = 0 
+                               # bit 5 : Declanche la division Hz du Timer (=1)
+                               # bit 4 \ Si le division Hz du timer est demand� : 
+                               # bit 3 / 00: 1 top d'alarme pour 1 tops d'horloge ,
+                               #         01: 1 top d'alarme pour 8 tops d'horloge ,
+                               #         10: 1 top d'alarme pour 64 tops d'horloge ,
+                               #         11: 1 top d'alarme pour 512 tops d'horloge. 
+                               # bit 2 - R.F.U. -
+                               # bit 1 \ Lecture d'un �tat interne de l'alarme 
+                               # bit 0 / 00: Alarme Courante,
+                               #         01: Division Hz,
+                               #         10: Ticks/Sec
+                               #         11: niveau d'interruption de l'horloge
+TIMER_ALARM     = 0xF8         # registre de generation d'interruption
+TIMER_IRQ       = 2            # Niveau d'interruption de l'horologe
+TIMER_TICKS     = 1            # Nombre de SYSTICKS par tick d'horloge
+
+#
+# Configuration de la MMU
+#
+MMU_ENABLE 	= 1				# MMU_ENABLE = 0 => simulation de la MMU d�sactiv�e
+MMU_IRQ		= 13				# Niveau d'interruption de la MMU
+MMU_CMD		= 0x66				# Registre de commande de la MMU
+MMU_FAULT_ADDR_HI	= 0xCD		# Registre contenant l'adresse m�moire ayant provoqu� une faute
+MMU_FAULT_ADDR_LO	= 0xCC
+TLB_ADD_ENTRY	= 0xCE			# Registre de commande d'ajout d'entr�e dans la TLB
+								# attend une valeur de la forme :
+								# struct tlb_entry_s {
+								#	unsigned unused: 8;
+								#	unsigned virt_page: 12;
+								#	unsigned phys_page: 8;
+								#	unsigned access_type: 3;
+								#	unsigned is_active: 1;
+								#};
+								# le nouveau mapping est ajout� dans la TLB,
+								# provoquant �ventuellement l'�crasement d'une ancienne entr�e
+TLB_DEL_ENTRY	= 0xDE			# Registre de commande de suppression d'entr�e dans la TLB
+								# seul phys_page est lu, et toutes les entr�e
+								# correspondant � cette page physicique sont supprim�es de la TLB
+TLB_SIZE		= 32			# Number of entries in the TLB
+TLB_ENTRIES		= 0x800			# Registre contenant les entr�es de la TLB (32 bits par entr�e)
+								# Accessible en lecture comme en �criture avec le m�me format que
+								# celui utilis� par TLB_ADD_ENTRY
diff --git a/Makefile b/Makefile
index f1c8d50..9512dba 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 INC=-I/vagrant/ASE/ordonnancement/x86-64/include 
 LIB=-L/vagrant/ASE/ordonnancement/x86-64/lib -lhardware
  
-all: try_mul pingpong pingpongpang
+all: try_mul pingpong pingpongpang mi_kernel
 
 display_stack : display_stack.o
 	gcc -o $@ $^ $(LIB)
@@ -15,8 +15,11 @@ pingpong : try.o pingpong.o
 pingpongpang : try.o pingpongpang.o
 	gcc -o $@ $^ $(LIB)
 
+mi_kernel : mi_user.o mi_kernel.o
+	gcc -o $@ $^ $(LIB)
+
 %.o:%.c
 	gcc $(INC) -c $<
 
 clean:
-	rm -f *.o *.s display_stack try_mul pingpong pingpongpang vdisk?.bin
\ No newline at end of file
+	rm -f *.o *.s display_stack try_mul pingpong pingpongpang vdisk?.bin mi_kernel
\ No newline at end of file
diff --git a/mi_kernel.c b/mi_kernel.c
new file mode 100644
index 0000000..f2ff8a1
--- /dev/null
+++ b/mi_kernel.c
@@ -0,0 +1,85 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "hardware.h"
+#include "mi_kernel.h"
+#include "mi_user.h"
+#include "MMUConfig.h"
+#include "mi_syscall.h"
+
+int current_process = 0;
+
+void mmu_handler()
+{
+    tlb_entry_t tlb;
+    long int vaddr = ((long int)_in(MMU_FAULT_ADDR_HI) << 32) | (_in(MMU_FAULT_ADDR_LO) & 0xFFFFFFFF);
+    long int vpage = (vaddr >> 12) & 0xFFF;
+    long int ppage = ppage_of_vpage(current_process, vpage);
+
+    //printf("%lx, %lx, %lx, %lx, %d\n", vpage, ppage, vaddr, (long int)virtual_memory, current_process);
+    if (vaddr < ((long int)virtual_memory) || vaddr > ((long int)virtual_memory) + VM_SIZE || ppage == -1)
+    {
+        fprintf(stderr, "Echec"); // TODO
+        exit(EXIT_FAILURE);
+    }
+
+    tlb.s.tlb_vpage = vpage;
+    tlb.s.tlb_ppage = ppage;
+    tlb.s.tlb_rwx = 7;
+    tlb.s.tlb_used = 1;
+
+    _out(TLB_ADD_ENTRY, tlb.i);
+    //_out(TLB_ADD_ENTRY, *(int *)(&tlb));
+}
+
+void switch_to_process0(void)
+{
+    current_process = 0;
+    _out(MMU_CMD, MMU_RESET);
+}
+
+void switch_to_process1(void)
+{
+    current_process = 1;
+    _out(MMU_CMD, MMU_RESET);
+}
+
+void init()
+{
+    if (init_hardware("MMUConfig.ini") == 0)
+    {
+        fprintf(stderr, "ERROR INITHARDWARE");
+        exit(EXIT_FAILURE);
+    }
+    IRQVECTOR[16] = switch_to_process0;
+    IRQVECTOR[17] = switch_to_process1;
+    IRQVECTOR[MMU_IRQ] = mmu_handler;
+}
+
+int ppage_of_vpage(int process, unsigned vpage)
+{
+    if (vpage < N)
+    {
+        if (process == PROCESS_0)
+            return 2 * vpage + 2;
+        if (process == PROCESS_1)
+            return 2 * vpage + 1;
+    }
+
+    return -1;
+}
+
+void main_master(void)
+{
+    init();
+    IRQVECTOR[16] = switch_to_process0;
+    IRQVECTOR[17] = switch_to_process1;
+}
+
+int main(int argc, char **argv)
+{
+
+    main_master();
+    _mask(0x1001); // Basculer mode utilisateur
+    main_user();
+}
diff --git a/process.h b/mi_kernel.h
similarity index 68%
rename from process.h
rename to mi_kernel.h
index ea0b40b..04dd604 100644
--- a/process.h
+++ b/mi_kernel.h
@@ -1,14 +1,12 @@
-#if !defined(PROCESS)
-#define PROCESS
-
-#define PROCESS_1 1
-#define PROCESS_0 0
-#define N 127
-#define VM_SIZE 160000 * 8
+#if !defined(MI_KERNEL)
+#define MI_KERNEL
 
+#define VM_SIZE 16000000
 #define MMU_FAULT_ADDR_LO 0xCC
 #define MMU_FAULT_ADDR_HI 0xCD
-#define TLB_ADD_ENTRY 0xCE
+
+#define PROCESS_1 1
+#define PROCESS_0 0
 
 struct tlb_entry_s
 {
@@ -27,8 +25,12 @@ union tlb_entry_u
 
 typedef union tlb_entry_u tlb_entry_t;
 
-int ppage_of_vpage(int process, unsigned vpage);
-
 void mmu_handler();
 
-#endif // PROCESS
+void switch_to_process0(void);
+
+void switch_to_process1(void);
+
+void main_master(void);
+
+#endif // MI_KERNEL
diff --git a/mi_syscall.h b/mi_syscall.h
new file mode 100644
index 0000000..9972004
--- /dev/null
+++ b/mi_syscall.h
@@ -0,0 +1,11 @@
+#if !defined(MI_SYSCALL)
+#define MI_SYSCALL
+
+#define SYSCALL_SWTCH_0 16
+#define SYSCALL_SWTCH_1 17
+
+#define N 127
+
+void init(void);
+
+#endif // MI_SYSCALL
diff --git a/mi_user.c b/mi_user.c
new file mode 100644
index 0000000..1f76de3
--- /dev/null
+++ b/mi_user.c
@@ -0,0 +1,38 @@
+#include <stdio.h>
+#include <string.h>
+#include "mi_user.h"
+#include "mi_syscall.h"
+#include "hardware.h"
+
+int sum(void *ptr)
+{
+    int i;
+    int sum = 0;
+
+    for (i = 0; i < PAGE_SIZE * N / 2; i++)
+        sum += ((char *)ptr)[i];
+    return sum;
+}
+
+void main_user(void)
+{
+    void *ptr;
+    int res;
+
+    ptr = virtual_memory;
+
+    _int(SYSCALL_SWTCH_0);
+    memset(ptr, 1, PAGE_SIZE * N / 2);
+
+    _int(SYSCALL_SWTCH_1);
+    memset(ptr, 3, PAGE_SIZE * N / 2);
+
+    _int(SYSCALL_SWTCH_0);
+    res = sum(ptr);
+
+    printf("Resultat du processus 0 : %d\n", res);
+    _int(SYSCALL_SWTCH_1);
+
+    res = sum(ptr);
+    printf("Resultat processus 1 : %d\n", res);
+}
\ No newline at end of file
diff --git a/mi_user.h b/mi_user.h
new file mode 100644
index 0000000..a5e8ac5
--- /dev/null
+++ b/mi_user.h
@@ -0,0 +1,12 @@
+#if !defined(MI_USER)
+#define MI_USER
+
+#define PAGE_SIZE 4000
+
+int ppage_of_vpage(int process, unsigned vpage);
+
+int sum(void *ptr);
+
+void main_user(void);
+
+#endif // MI_USER
diff --git a/process.c b/process.c
deleted file mode 100644
index 60ff9e7..0000000
--- a/process.c
+++ /dev/null
@@ -1,40 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include "hardware.h"
-#include "process.h"
-
-int current_process = NULL;
-
-int ppage_of_vpage(int process, unsigned vpage)
-{
-    if (vpage < N)
-    {
-        if (process == PROCESS_0)
-            return 2 * vpage + 2;
-        if (process == PROCESS_1)
-            return 2 * vpage + 1;
-    }
-
-    return -1;
-}
-
-void mmu_handler()
-{
-    tlb_entry_t tlb;
-    unsigned int vaddr = ((long int)_in(MMU_FAULT_ADDR_HI) << 32) | (_in(MMU_FAULT_ADDR_LO) & 0xFFFFFFFF);
-    unsigned int vpage = (vaddr >> 12) & 0xFFF;
-    unsigned int ppage = ppage_of_vpage(current_process, vpage);
-
-    if (vaddr < virtual_memory || vaddr > virtual_memory + VM_SIZE || vpage == -1)
-    {
-        fprintf(stderr, "Echec");
-        exit(EXIT_FAILURE);
-    }
-
-    tlb.s.tlb_vpage = vpage;
-    tlb.s.tlb_ppage = ppage;
-    tlb.s.tlb_rwx = 7;
-    tlb.s.tlb_used = 1;
-
-    _out(TLB_ADD_ENTRY, tlb.i);
-}
diff --git a/x86-64/lib/libhardware.a b/x86-64/lib/libhardware.a
index 7b1a9ec9aef8e1f6016442b01674e62265a07f6a..738b2688a371f209faf966aee457234e0d5b4443 100644
GIT binary patch
delta 5740
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
Mjkmu&!1$6E0G>WdM*si-

delta 4274
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

-- 
GitLab