| | 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. |