| 1 | |
| 2 | = Besoins = |
| 3 | |
| 4 | * Booter en utilisant un espace mémoire indépendant du nombre de |
| 5 | processeurs. |
| 6 | * Booter sans connaitre à priori le nombre de processeurs. |
| 7 | * Booter sans faire d'assertions sur l'état initial de la mémoire. |
| 8 | |
| 9 | = Solutions = |
| 10 | |
| 11 | 1. Un processeur est élu pour initialiser la mémoire, il débloque les |
| 12 | autres processeurs pour la suite après l'initialisation de certaines |
| 13 | cases |
| 14 | 1. Tous les processeurs incrémentent atomiquement une variable globale |
| 15 | comptant les processeurs de la plateforme |
| 16 | |
| 17 | |
| 18 | = Déroulement = |
| 19 | |
| 20 | `asm_magic_val`:: |
| 21 | Une case mémoire contenant à priori n'importe quoi, devant contenir |
| 22 | MAGIC1 pour élire un chef, et MAGIC2 pour débloquer tout le mode |
| 23 | `start_bar`:: |
| 24 | Une case mémoire contenant un booléen "doit démarrer", modifié par |
| 25 | le chef, lu par tous |
| 26 | `cpu_count`:: |
| 27 | Un compteur atomique contenant le nombre de procs sur la plateforme |
| 28 | `bs_cpuid`:: |
| 29 | Globale contenant le CpuId du chef |
| 30 | |
| 31 | * Le chef: |
| 32 | * Lire @`asm_magic_val` |
| 33 | * Réussir à écrire atomiquement MAGIC1 -> @`asm_magic_val` |
| 34 | * Init 0 -> @`start_bar` |
| 35 | * Init 1 -> @`cpu_count` |
| 36 | * Init cpuid() -> @`bs_cpuid` |
| 37 | * Écrire MAGIC2 -> @`asm_magic_val` |
| 38 | * Saut dans arch_init() |
| 39 | * Les autres: |
| 40 | * Lire @`asm_magic_val` |
| 41 | * Ne pas réussir à écrire atomiqumente MAGIC1 dans @`asm_magic_val` |
| 42 | * Attendre MAGIC2 == @`asm_magic_val` |
| 43 | * Incrément atomique @`cpu_count` |
| 44 | * Attendre 1 @`start_bar` |
| 45 | * Saut dans `stack_poll_call(arch_init_stack_pool)` |
| 46 | |
| 47 | Dans `arch_init()` |
| 48 | |
| 49 | * Le chef: |
| 50 | * Initialise la mémoire |
| 51 | * Initialise son CPU |
| 52 | * Initialise les globales |
| 53 | * Initialise les Pool d'allocateurs |
| 54 | * Initialise le scheduler |
| 55 | * Initialise la globale `arch_init_stack_pool` pour permettre aux autres CPU de démarrer |
| 56 | * Initialise `running_count` = 1 |
| 57 | * cpu_start_other() |
| 58 | * Attend `running_count` == @`cpu_count` |
| 59 | * Les autres: |
| 60 | * Initialise son CPU |
| 61 | * Initialise son sched |
| 62 | * Incrémente atomiquement `running_count` |
| 63 | * Termine avec stack_poll_terminate_call(stack_poll_call, start_stack_pool) |
| 64 | |
| 65 | `cpu_start_other()`:: |
| 66 | Écrit 1 -> @`start_bar` |
| 67 | |
| 68 | = APIs = |
| 69 | |
| 70 | == stack_pool == |
| 71 | |
| 72 | * Bas niveau: |
| 73 | |
| 74 | `stack_poll_initialize(struct stack_pool_s *pool, func_t *func)`:: |
| 75 | Initialise la stack_pool devant appeler une fonction `func`. |
| 76 | `stack_poll_newstack(struct stack_pool_s *pool, void *stack, size_t size)`:: |
| 77 | Donne une nouvelle pile dans le pool |
| 78 | `void *stack_poll_getstack(struct stack_pool_s *pool)`:: |
| 79 | Récupère la première stack libre dans le pool |
| 80 | |
| 81 | * User: |
| 82 | |
| 83 | `stack_pool_call(struct stack_pool_s *pool)`:: |
| 84 | Utilise une pile dans l'ensemble de piles disponibles dans `pool` |
| 85 | pour exécuter la fonction `c_function`. Le stack-pointer à l'entrée |
| 86 | de cette fonction ne sert à rien, il peut être invalide. |
| 87 | `stack_pool_terminate_call(func_t *stackless_func, void *arg)`:: |
| 88 | Relâche la pile partagée utilisée par le CPU courrant et saute dans |
| 89 | la fonction `stackless_func(arg)`. Cette fonction ne doit pas |
| 90 | utiliser la pile et peut recevoir un stack pointer invalide. |