Mise a jour de mon avancement

This commit is contained in:
2025-09-17 19:34:16 +02:00
parent 1bc67f8364
commit 3a422bc6bd
2 changed files with 318 additions and 0 deletions

111
Makefile Normal file
View File

@@ -0,0 +1,111 @@
SYSTEM?=UEFI
PLATFORM?=QEMU
QEMU=qemu-system-i386 -enable-kvm -m 512 -monitor stdio -vga virtio
QEMU_VNC=qemu-system-i386 -m 512 -monitor stdio -vga virtio -display vnc=:1,password=on -audiodev none,id=audio0
ISO_NAME=yoctos.iso
GRUB_CONF=grub/grub.cfg
SYSTEM_OK=0
ifeq ($(SYSTEM),BIOS)
GRUB_MKRESCUE_ARGS=
SYSTEM_OK=1
endif
ifeq ($(SYSTEM),UEFI)
GRUB_MKRESCUE_ARGS=/usr/lib/grub/i386-pc
SYSTEM_OK=1
endif
ifneq ($(SYSTEM_OK),1)
$(error invalid SYSTEM)
endif
PLATFORM_OK=0
ifeq ($(PLATFORM),QEMU)
CC_DEFINES=-DQEMU
PLATFORM_OK=1
endif
ifeq ($(PLATFORM),PC)
CC_DEFINES=-DEMULATE_DISK_IN_RAM
PLATFORM_OK=1
endif
ifneq ($(PLATFORM_OK),1)
$(error invalid PLATFORM)
endif
ifeq ($(DEBUG),0)
CC_FLAGS=-O3
LD_FLAGS=
else
CC_FLAGS=-g -O0
LD_FLAGS=-g
endif
help:
@echo "Available targets:"
@echo "run build the OS ISO image (+ filsystem) and run it in QEMU"
@echo "iso build the OS ISO image (+ filesystem)"
@echo "common build the common object files only"
@echo "kernel build the kernel only"
@echo "debug build the OS ISO image (+ filsystem) and run it in QEMU for debugging"
@echo "deploy build the OS ISO image (+ filsystem) and deploy it to the specified device"
@echo " Requires DEV to be defined (eg. DEV=/dev/sdb)"
@echo "clean clean up everything"
@echo ""
@echo "Available variables:"
@echo "SYSTEM target system type, either UEFI or BIOS (default: UEFI)"
@echo "PLATFORM target platform type, either QEMU or PC (default: QEMU)"
@echo "DEBUG whether to generate debug code, either on or off (default: on)"
@echo "DEV device to deploy the ISO image onto (only used by the \"deploy\" target)"
@echo ""
@echo "Usage examples:"
@echo "make run"
@echo "make run SYSTEM=BIOS PLATFORM=QEMU"
@echo "make PLATFORM=PC DEBUG=0 DEV=/dev/sdb deploy"
iso: $(ISO_NAME)
msg:
@echo "================================================="
@echo "Build targeting a $(PLATFORM)/$(SYSTEM) platform"
@echo "================================================="
run: $(ISO_NAME)
$(QEMU) -cdrom $<
run_vnc: $(ISO_NAME)
$(QEMU_VNC) -cdrom $<
# The "accel=tcg" option is necessary to be able to debug an ELF within QEMU
debug: $(ISO_NAME)
$(QEMU) -s -S -cdrom $< -machine accel=tcg
# ISO image creation taken from http://wiki.osdev.org/Bare_Bones#Booting_the_Kernel
# Requires grub-mkrescue and xorriso
# NOTE: on hosts that boot via UEFI, the path /usr/lib/grub/i386-pc is required
# by grub-mkrescue otherwise the ISO won't be bootable by qemu.
$(ISO_NAME): msg $(GRUB_CONF) kernel
mkdir -p build/boot/grub build/data/
cp $(GRUB_CONF) build/boot/grub/
cp kernel/kernel.elf build/boot/
grub-mkrescue $(GRUB_MKRESCUE_ARGS) -o $@ build
@echo "Built the $@ image for a $(SYSTEM) system."
common:
$(MAKE) -C $@ CC_DEFINES=$(CC_DEFINES) CC_FLAGS="$(CC_FLAGS)" LD_FLAGS="$(LD_FLAGS)"
kernel:
$(MAKE) -C $@ CC_DEFINES=$(CC_DEFINES) CC_FLAGS="$(CC_FLAGS)" LD_FLAGS="$(LD_FLAGS)"
deploy: $(ISO_NAME)
sudo dd if=/dev/urandom of=$(DEV) bs=1M count=10
sudo dd if=$< of=$(DEV)
sudo sync
clean:
/bin/rm -rf build $(ISO_NAME)
$(MAKE) -C common clean
$(MAKE) -C kernel clean
.PHONY: clean common kernel

207
tp1.md Normal file
View File

@@ -0,0 +1,207 @@
# TP 1 Mini noyeau avec affichage graphique
## Installation
OK alors je suis sur macos donc c'est un peu sepcial. Ma vm de compilation etc... est pas une vm graphique car ca serait beaucoup trop lent. Donc on ne peut pas utiliser le makefile d'origine.
J'ai mis le makefile que j'ai du fabriquer moi même sur mesure pour faire fonctionner qemu en utilisant un vnc.
Desormais pour faire un test de l'iso depuis la vm ubuntu:
`make run_vnc`
l'invite de commande qemu va s'ouvrir
`change vnc password`
ensuite on entre un password et on peut revenir sur la machine macos
`open vnc://[ip_vm]:5901`
Et normalement après avoir entré le mot de passe on a droit à un magnifique ecran noir avec un curseur qui clignotte.
Yay.
## Objectif 1 affichage graphique VBE 640x480 en 16bpp
Pour se faire on a un certain nombre de choses à modifier dans le squelette original :
1. Changer le Bootstrap pour demander à GRUB d'init le mode grahique
Dans le bootstrap original il faut ajouter les lignes suivante :
`MULTIBOOT_VIDEO_MODE equ 4 ; ask grub to switch to VBE graphics modes` le flag qui dit a GRUB d'activer le mode graphique
Changer la ligne MULTIBOOT_FLAGS en `MULTIBOOT_FLAGS equ (MULTIBOOT_ALIGN|MULTIBOOT_MEMINFO|MULTIBOOT_VIDEO_MODE)`
```
; for MULTIBOOT_VIDEO_MODE
dd 0x00000000 ; mode_type
dd 640 ; width
dd 480 ; height
dd 16 ; depth
```
Après MULTIBOOT_MEMORY_INFOS
Normalement après si on relance un test avec `make run_vnc` on a un ecran noir sans le curseur qui clignotte
2. Faire une fonction d'init qui efface l'ecran et setup les structures internes.
Maintenant on peut s'attaquer au C directement dans `kernel.c`
Dans `kernel_main` on a en argument les informations que nous donne grub au démarrage. J'ai laissé le code du prof c'est à dire ces deux lignes :
```c
multiboot_set_info(mbi);
gdt_init();
```
Honnêtement gdt_init je ne sais pas exactement ce que ca fait à part mettre des registres à 0. J'imagine que c'est une fonction d'init classique qui doit se justifier en lisant un peu plus la doc.
Multiboot_set_info c'est simplement pour mettre mbi en variable globale mais j'ai pas encore essayé de l'utiliser comme tel.
On va essayer d'afficher un gradient de bleu sur tout l'ecran pour tester que tout fonctionne bien.
Pour ca dans un premier temps on récupère toutes les infos :
```c
multiboot_info_t* infos_grub = multiboot_get_info();
uint16_t* fb = (uint16_t*)infos_grub->framebuffer_addr;
uint32_t pitch = infos_grub->framebuffer_pitch;
uint32_t width = infos_grub->framebuffer_width;
uint32_t height = infos_grub->framebuffer_height;
uint32_t bpp = infos_grub->framebuffer_bpp;
```
Avec ces infos en principe on peut tout faire. Attention, ici on cast le tableau framebuffer en 16 bits car on sait deja que le but est d'utiliser 16 bits par couleur. Si on veut que le code puisse marcher de manière générique il faudrait garder un tableau de 32 bits et dans la boucle faire un déalage dans une même case de tableau.
3. Fonction affichant un pixel aux coordonées (x,y) avec la couleur demandée
Pour afficher un gradient de couleur sur tout l'ecran on peut utiliser ce petit bout de code :
```c
uint32_t true_pitch = pitch/(bpp/8);
uint32_t index = 0;
for(uint32_t y = 0; y < height; y++){
for(uint32_t x = 0; x < width; x++){
index = y * true_pitch + x; // this gives a position in 1D
fb[index] = (uint16_t)y;
}
}
```
Le "true pitch" convertis le pitch qui est la largeur réelle du framebuffer en bytes en pixels avec deux divisions.
Ensuite la seule "technicité" est de convertir la position 2D en position 1D pour le framebuffer et le calcul est plutôt simple.
Ensuite la valeur a chaque pixel est la valeur de y qui rolloff ce qui nous donne un gradient degeulasse mais qui fonctionne.
4. Fonction put_pixel(color) et clear_screen(color)
Dans un premier temps on doit faire une methode qui permet de convertir une couleur 32bit en 16bit 565
```c
#define RGB(r,g,b) ( ((uint16_t)(((r) >> 3) & 0x1F) << 11) | \
((uint16_t)(((g) >> 2) & 0x3F) << 5) | \
((uint16_t)(((b) >> 3) & 0x1F)) )
```
Avec ca on peut faire une nouvelle methode put pixel
```c
void put_pixel(uint32_t x,uint32_t y,uint16_t color){
uint32_t index = y * FRAMEBUFFER_TRUE_PITCH + x; // this gives a position in 1D
FRAMEBUFFER[index] = color;
}
```
Et ensuite on peut facilement faire des fonctions pour clear l'écran ou afficher un gradient de debug
```c
void clear_display(uint16_t color){
for(uint32_t y = 0; y < SCREEN_HEIGHT; y++){
for(uint32_t x = 0; x < SCREEN_WIDTH; x++){
put_pixel(x,y,color);
}
}
}
void display_debug_gradient(){
for(uint32_t y = 0; y < SCREEN_HEIGHT; y++){
for(uint32_t x = 0; x < SCREEN_WIDTH; x++){
put_pixel(x,y,RGB(y,x,100));
}
}
}
```
5. Fonction similaire à printf affichant un texte formaté à arguments variables à la position
2D courante du curseur ; les formats minimum à gérer sont : %c, %s, %d et %x ;
Deja on va commencer par faire une methode qui display les caractères.
Le fonctionnement est le suivant:
On a un tableau de bytes. La font fait 8 bits de large et 16 lignes. On a donc un tableau d'octets ou chaque octet est une ligne de font.
Chaque caractère est donc composé de 16 nombres 8 bits.
Voici la methode pour dessiner un caractère
```c
void display_char(char c,uint32_t x,uint32_t y, uint16_t color){
//#define FONT_HEIGHT 16
//#define FONT_WIDTH 8
uint32_t index = c * FONT_HEIGHT;
for(int i = 0; i < FONT_HEIGHT;i++){
uint8_t row = font_8x16[index + i];
for(int j = 0; j < 8;j ++){
if (((row << j) & 128))
put_pixel(x+j,y+i,color);
}
}
}
```
Ensuite on a le moment un peu technique ou il a fallu reprendre la methode snprintf et l'adapter.
```c
int printf(char *fmt, ...) {
va_list args;
va_start(args, fmt);
int count = vsnprintf(CHAR_BUFFER, CHAR_BUFFER_SIZE, fmt, args);
va_end(args);
char c;
for(int i =0; i < count;i++){
c = CHAR_BUFFER[i];
if(c == '\n'){
CURSOR_X = 0;
CURSOR_Y += FONT_HEIGHT + 2; //TODO HANDLE SCROLL
}else{
display_char(c,CURSOR_X,CURSOR_Y,FONT_COLOR);
CURSOR_X += FONT_WIDTH + 2;
}
}
return count;
}
```
Dans cette fonction on utilise vsnprintf pour remplir un buffer puis on itere sur ce buffer pour acceder à chaque caractère.
La subtilité est que on doit utiliser vsnprintf car en c on ne peut pas forward des paramètres infinis.
J'ai aussi mis en variables globales le buffer de caractère le curseur la couleur de font et la couleur du background.
#### TODO
Maintenant il faudrait pouvoir scroller quand on arrive trop bas et detecter les inputs clavier pour faire des retours claviers
#### TODO
Afficher les infos que j'ai pu recuperer de grub ainsi que tous les caractères imprimables possibles