Merge branch 'thread_scheduler' of https://gitlab.istic.univ-rennes1.fr/simpleos/burritos into thread_scheduler
3
.gitignore
vendored
@ -1 +1,4 @@
|
||||
/target
|
||||
/.idea
|
||||
*.iml
|
||||
/*.txt
|
||||
|
27
.gitlab-ci.yml
Normal file
@ -0,0 +1,27 @@
|
||||
default:
|
||||
image: rust:latest
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
|
||||
build-job:
|
||||
stage: build
|
||||
script:
|
||||
- echo "Compiling the code..."
|
||||
- cargo build
|
||||
- echo "Compile complete."
|
||||
|
||||
unit-test-job:
|
||||
stage: test
|
||||
script:
|
||||
- echo "Running unit tests..."
|
||||
- cargo test
|
||||
|
||||
lint-test-job:
|
||||
only:
|
||||
- merge_requests
|
||||
stage: test
|
||||
script:
|
||||
- echo "Linting code..."
|
||||
- cargo clippy
|
7
Cargo.lock
generated
@ -6,16 +6,9 @@ version = 3
|
||||
name = "burritos"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.139"
|
||||
|
@ -4,5 +4,4 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
libc = { version = "0.2.139", features = ["extra_traits"] }
|
||||
|
@ -1,3 +1,5 @@
|
||||
![BurritOS Logo](assets/logo/logo_full.png)
|
||||
|
||||
# BurritOS
|
||||
|
||||
BurritOS (BurritOS Using Rust Really Improves The Operating System) is an educational operating system written in Rust and running on a RISC-V emulator. It aims to be used as an educational platform for learning about operating systems.
|
||||
|
104
assets/logo/README.md
Normal file
@ -0,0 +1,104 @@
|
||||
# BurritOS logo
|
||||
|
||||
Designed with Inkscape.
|
||||
Font: [Fira Code](https://github.com/tonsky/FiraCode) by The Fira Code contributors
|
||||
|
||||
Logo by François AUTIN
|
||||
|
||||
## Licences
|
||||
|
||||
### Fira Code
|
||||
|
||||
Copyright (c) 2014, The Fira Code Project Authors (https://github.com/tonsky/FiraCode)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
BIN
assets/logo/logo.png
Normal file
After Width: | Height: | Size: 154 KiB |
377
assets/logo/logo.svg
Normal file
After Width: | Height: | Size: 328 KiB |
BIN
assets/logo/logo_128.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
assets/logo/logo_full.png
Normal file
After Width: | Height: | Size: 298 KiB |
5986
assets/logo/logo_full.svg
Normal file
After Width: | Height: | Size: 447 KiB |
196
doc/DOCUMENTATION.md
Normal file
@ -0,0 +1,196 @@
|
||||
# Comment utiliser la documentation Rust
|
||||
|
||||
![](https://www.rust-lang.org/static/images/rust-social-wide.jpg)
|
||||
|
||||
## Écrire un commentaire en Rust
|
||||
|
||||
Pour écrire un commentaire qui apparaitra sur la doc de cargo, écrivez :
|
||||
|
||||
```rust
|
||||
/// Votre commentaire
|
||||
```
|
||||
pour commenter votre fonction, attribut, structure, module....
|
||||
|
||||
Pour les commentaires de **crate**, utilisez :
|
||||
|
||||
```rust
|
||||
//! Votre commentaire
|
||||
```
|
||||
|
||||
*Les commentaires style java (`/** */`) sont supporté à l'exception des règles `@` **mais utilisez plutôt les commentaires standard de rust please***.
|
||||
|
||||
Vous pouvez ensuite écrire votre commentaire comme bon vous semble; la documentation supporte le markdown, n'hésitez surtout pas à mettre des h3, des textes den gras, des exemple encapsulé, etc.
|
||||
|
||||
Petit aide avec la markdown dans le chapitre [#aide-markdown](#aide-markdown).
|
||||
|
||||
|
||||
### Exemple
|
||||
|
||||
Vous obtenez quelque chose sur votre code comme à la figure 1.
|
||||
|
||||
![Capture d'écran de main.rs](Screenshot_20221019_153737.png "Aperçu du main")
|
||||
|
||||
|
||||
## Générer la documentation Rust
|
||||
|
||||
Éxecutez la commande __`cargo doc`__ dans le dossier du projet, cela va générer un dossier dans le dossier **target/doc/** qui contient des pages web, ouvrez le fichier **target/doc/burritos/all.html**.
|
||||
|
||||
Lors de l'affichage de la liste de vos fonctions et autres éléments, la doc **n'affiche que la première ligne**, le autres lignes sont affiché lorsqu'on ouvre les détails.
|
||||
|
||||
### Exemple
|
||||
|
||||
En partant du code de la figure 1, vous obtenez une documentation comme aux figures 2 et 3.
|
||||
|
||||
!["Page d'accueil de la doc"](Screenshot_20221019_153803.png "Page all.html de la crate burritos généré à partir de l'exemple de la figure 1")
|
||||
|
||||
!["Page de la fonction main de la doc"](Screenshot_20221019_153817.png "Page de la fonction main")
|
||||
|
||||
|
||||
## Aide markdown
|
||||
|
||||
Comme le markdown est utile pour écrire et mettre en page des commentaire en Rust, petit tuto sur comment formaté du texte en markdown.
|
||||
|
||||
lorsque vous voulez passer à la ligne, faites 2 sauts à la ligne en markdown (oui pas une blague), un saut à la ligne sur le fichier markdown n'est pas
|
||||
considérer comme un passage à la ligne, ça vous permet juste d'écrire des commentaires sans aller trop à droite dans votre IDE comme celui-ci.
|
||||
|
||||
### Italic et gras
|
||||
|
||||
```md
|
||||
*Texte en italic* _Texte en italic_
|
||||
|
||||
**Texte en gras** __Texte en gras__
|
||||
|
||||
***Texte en italic et gras***
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Titre et sous-titres
|
||||
|
||||
```md
|
||||
# Titre type h1
|
||||
|
||||
## Titre type h2
|
||||
|
||||
etc. jusqu'à
|
||||
|
||||
###### Titre type h6
|
||||
```
|
||||
|
||||
### Listes
|
||||
|
||||
```md
|
||||
- Une
|
||||
- Liste
|
||||
- Non
|
||||
- Ordonnée
|
||||
|
||||
1. Une
|
||||
2. Liste
|
||||
3. Ordonnée
|
||||
|
||||
- [x] Liste
|
||||
- [ ] de
|
||||
- [ ] tâches
|
||||
```
|
||||
|
||||
#### Exemple
|
||||
|
||||
- Une
|
||||
- Liste
|
||||
- Non
|
||||
- Ordonnée
|
||||
|
||||
1. Une
|
||||
2. Liste
|
||||
3. Ordonnée
|
||||
|
||||
- [x] Liste
|
||||
- [ ] de
|
||||
- [ ] tâches
|
||||
|
||||
|
||||
### Code
|
||||
|
||||
```md
|
||||
`Commentaire mono-ligne`
|
||||
```
|
||||
|
||||
```md
|
||||
```
|
||||
Commentaire
|
||||
Multi
|
||||
ligne
|
||||
```
|
||||
```
|
||||
|
||||
```md
|
||||
```md
|
||||
Commentaire
|
||||
multi
|
||||
ligne
|
||||
avec
|
||||
coloration
|
||||
syntaxique (ici md pour markdown)
|
||||
```
|
||||
```
|
||||
|
||||
#### Exemple
|
||||
|
||||
|
||||
Texte avant `Commentaire mono-ligne` texte après
|
||||
|
||||
```
|
||||
Commentaire
|
||||
multi
|
||||
ligne
|
||||
```
|
||||
|
||||
### Tableaux
|
||||
|
||||
```md
|
||||
|
||||
| Colonne 1 | Colonne 2 |
|
||||
|-----------|-----------|
|
||||
| ligne 1 | ligne 1 |
|
||||
| ligne 2 | ligne 2 |
|
||||
|
||||
```
|
||||
|
||||
#### Exemple
|
||||
|
||||
| Colonne 1 | Colonne 2 |
|
||||
|-----------|-----------|
|
||||
| ligne 1 | ligne 1 |
|
||||
| ligne 2 | ligne 2 |
|
||||
|
||||
### Liens et images
|
||||
|
||||
```md
|
||||
[Mon lien](www.google.com)
|
||||
![Mon image sur internet](www.imagelink.com/image.png)
|
||||
![Mon image stocké sur mon pc](image/monscreenshot.png)
|
||||
```
|
||||
|
||||
|
||||
## Faire des tests
|
||||
|
||||
Pour écrire vos tests faites comme dans l'exemple ci-dessous:
|
||||
|
||||
![Exemple de test](Screenshot_20221019_173551.png)
|
||||
|
||||
Executez ensuite les tests en faisant `cargo test`, vous obtenez comme en suivant l'exemple ci-dessus le résultat de la figure 5.
|
||||
|
||||
![Exemple de résultat des tests](Screenshot_20221019_174036.png)
|
||||
|
||||
Lors d'un cargo build optimisé (`cargo build --release`), les tests ne sont pas inclus dans le fichier binaire.
|
||||
|
||||
## Liens utiles
|
||||
|
||||
La documentation officielle du crate core: [https://doc.rust-lang.org/core/index.html](https://doc.rust-lang.org/core/index.html)
|
||||
|
||||
La documentation officielle du crate std: [https://doc.rust-lang.org/std/index.html](https://doc.rust-lang.org/std/index.html)
|
||||
|
||||
La documentation des crates téléchargeable sur [https://crates.io/](https://crates.io/) est retrouvable sur: [https://docs.rs/](https://docs.rs/)
|
||||
|
||||
Pour le markdown vous pouvez retrouver des tutos ici: [https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html](https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html) et ici [https://www.markdownguide.org/cheat-sheet/](https://www.markdownguide.org/cheat-sheet/)
|
BIN
doc/Screenshot_20221019_153737.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
doc/Screenshot_20221019_153803.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
doc/Screenshot_20221019_153817.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
doc/Screenshot_20221019_173551.png
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
doc/Screenshot_20221019_174036.png
Normal file
After Width: | Height: | Size: 46 KiB |
143
src/kernel/elf.rs
Normal file
@ -0,0 +1,143 @@
|
||||
//Declaration des alias
|
||||
|
||||
/*
|
||||
Def ELF :
|
||||
|
||||
The header file <elf.h> defines the format of ELF executable binary
|
||||
files. Amongst these files are normal executable files, relocatable
|
||||
object files, core files and shared libraries.
|
||||
|
||||
An executable file using the ELF file format consists of an ELF header,
|
||||
followed by a program header table or a section header table, or both.
|
||||
The ELF header is always at offset zero of the file. The program
|
||||
header table and the section header table's offset in the file are
|
||||
defined in the ELF header. The two tables describe the rest of the
|
||||
particularities of the file
|
||||
*/
|
||||
|
||||
|
||||
/* Type for a 16-bit quantity. */
|
||||
|
||||
type Elf32_Half = u16;
|
||||
|
||||
type Elf64_Half = u16;
|
||||
|
||||
/* Types for signed and unsigned 32-bit quantities. */
|
||||
|
||||
type Elf32_Word = u32;
|
||||
|
||||
type Elf32_Sword = i32;
|
||||
|
||||
type Elf64_Word = u32;
|
||||
|
||||
type Elf64_Sword = i32;
|
||||
|
||||
/* Types for signed and unsigned 64-bit quantities. */
|
||||
|
||||
type Elf32_Xword = u64;
|
||||
|
||||
type Elf32_Sxword = i64;
|
||||
|
||||
type Elf64_Xword = u64;
|
||||
|
||||
type Elf64_Sxword = i64;
|
||||
|
||||
/* Type of addresses. */
|
||||
|
||||
type Elf32_Addr = u32;
|
||||
|
||||
type Elf64_Addr = u64;
|
||||
|
||||
/* Type of file offsets. */
|
||||
|
||||
type Elf32_Off = u32;
|
||||
|
||||
type Elf64_Off = u64;
|
||||
|
||||
//role de ce truc ?
|
||||
const EI_NIDENT : u8 = 16;
|
||||
|
||||
//ELF file header 32 bits
|
||||
struct Elf32Ehdr{
|
||||
e_ident : [u8;EI_NIDENT],//16 octects décrivant comment le fichier doit etre parsé
|
||||
//e_ident must starts with magice number : 0x 7f 45 4c 46
|
||||
e_type : Elf32_Half,//type of the file
|
||||
e_machine : Elf32_Half,//type architecture machine
|
||||
e_version : Elf32_Word,//always 1
|
||||
e_entry : Elf32_Addr,//entry point @ for executable
|
||||
e_phoff : Elf32_Off,//Offset of the program header table
|
||||
e_shoff : Elf32_Off,//Offset of the section header table
|
||||
e_flags : Elf32_Word,//des flags ?
|
||||
e_ehsize : Elf32_Half,//size of this (the header), redundant
|
||||
e_phentsize : Elf32_Half,//size per program header
|
||||
e_phnum : Elf32_Half,//number of program header
|
||||
e_shentsize : Elf32_Half,//size per section header
|
||||
e_shnum : Elf32_Half,//number of section header
|
||||
e_shstrndx : Elf32_Half//section header string table index
|
||||
}
|
||||
|
||||
|
||||
//ELF file header 64 bits
|
||||
//les champs ont le meme rôle que dans le header 32 bits
|
||||
struct Elf64Ehdr{
|
||||
e_ident : [u8;EI_NIDENT],
|
||||
e_type : Elf64_Half,
|
||||
e_machine : Elf64_Half,
|
||||
e_version : Elf64_Word,
|
||||
e_entry : Elf64_Addr,
|
||||
e_phoff : Elf64_Off,
|
||||
e_shoff : Elf64_Off,
|
||||
e_flags : Elf64_Word,
|
||||
e_ehsize : Elf64_Half,
|
||||
e_phentsize : Elf64_Half,
|
||||
e_phnum : Elf64_Half,
|
||||
e_shentsize : Elf64_Half,
|
||||
e_shnum : Elf64_Half,
|
||||
e_shstrndx : Elf64_Half
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* e_ident offsets */
|
||||
const EI_MAG0 : u32 = 0;
|
||||
const EI_MAG1 : u32 = 1;
|
||||
const EI_MAG2 : u32 = 2;
|
||||
const EI_MAG3 : u32 = 3;
|
||||
const EI_CLASS : u32 = 4;
|
||||
const EI_DATA : u32 = 5;
|
||||
const EI_VERSION : u32 = 6;
|
||||
const EI_PAD : u32 = 7;
|
||||
|
||||
/* e_ident[EI_CLASS] */
|
||||
const ELFCLASSNONE : u32 = 0;
|
||||
const ELFCLASS32 : u32 = 1;
|
||||
const ELFCLASS64 : u32 = 2;
|
||||
|
||||
/* e_ident[EI_DATA] */
|
||||
const ELFDATANONE : u32 = 0;
|
||||
const ELFDATA2LSB : u32 = 1;
|
||||
const ELFDATA2MSB : u32 = 2;
|
||||
|
||||
/* e_type */
|
||||
const ET_NONE : u32 = 0; /* No file type */
|
||||
const ET_REL : u32 = 1; /* Relocatable file */
|
||||
const ET_EXEC : u32 = 2; /* Executable file */
|
||||
const ET_DYN : u32 = 3; /* Shared object file */
|
||||
const ET_CORE : u32 = 4; /* Core file */
|
||||
const ET_LOPROC : u32 = 0xff00; /* Processor-specific */
|
||||
const ET_HIPROC : u32 = 0xffff; /* Processor-specific */
|
||||
|
||||
/* e_machine */
|
||||
const EM_NONE : u32 = 0; /* No machine */
|
||||
const EM_M32 : u32 = 1; /* AT&T WE 32100 */
|
||||
const EM_SPARC : u32 = 2; /* SPARC */
|
||||
const EM_386 : u32 = 3; /* Intel 80386 */
|
||||
const EM_68K : u32 = 4; /* Motorola 68000 */
|
||||
const EM_88K : u32 = 5; /* Motorola 88000 */
|
||||
const EM_860 : u32 = 7; /* Intel 80860 */
|
||||
const EM_MIPS : u32 = 8; /* MIPS R3000 */
|
||||
const EM_RISC : u32 = 243; /* RISCV */
|
||||
|
||||
/* e_version */
|
||||
const EV_NONE : u32 = 0; /* invalid version */
|
||||
const EV_CURRENT : u32 = 1; /* current version */
|
@ -1,31 +1,31 @@
|
||||
|
||||
/// Error enum, use it with Result<YourSucessStruct, **ErrorCode**>
|
||||
pub enum ErrorCode {
|
||||
INC_ERROR,
|
||||
OPENFILE_ERROR,
|
||||
EXEC_FILE_FORMAT_ERROR,
|
||||
OUT_OF_MEMORY,
|
||||
IncError,
|
||||
OpenfileError,
|
||||
ExecFileFormatError,
|
||||
OutOfMemory,
|
||||
|
||||
OUT_OF_DISK,
|
||||
ALREADY_IN_DIRECTORY,
|
||||
INEXIST_FILE_ERROR,
|
||||
INEXIST_DIRECTORY_ERROR,
|
||||
NOSPACE_IN_DIRECTORY,
|
||||
NOT_A_FILE,
|
||||
NOT_A_DIRECTORY,
|
||||
DIRECTORY_NOT_EMPTY,
|
||||
INVALID_COUNTER,
|
||||
OutOfDisk,
|
||||
AlreadyInDirectory,
|
||||
InexistFileError,
|
||||
InexistDirectoryError,
|
||||
NospaceInDirectory,
|
||||
NotAFile,
|
||||
NotADirectory,
|
||||
DirectoryNotEmpty,
|
||||
InvalidCounter,
|
||||
|
||||
/* Invalid typeId fields: */
|
||||
INVALID_SEMAPHORE_ID,
|
||||
INVALID_LOCK_ID,
|
||||
INVALID_CONDITION_ID,
|
||||
INVALID_FILE_ID,
|
||||
INVALID_THREAD_ID,
|
||||
InvalidSemaphoreId,
|
||||
InvalidLockId,
|
||||
InvalidConditionId,
|
||||
InvalidFileId,
|
||||
InvalidThreadId,
|
||||
|
||||
/* Other messages */
|
||||
WRONG_FILE_ENDIANESS,
|
||||
NO_ACIA,
|
||||
WrongFileEndianess,
|
||||
NoAcia,
|
||||
|
||||
NUMMSGERROR /* Must always be last */
|
||||
}
|
@ -4,3 +4,5 @@ pub mod scheduler;
|
||||
pub mod mgerror;
|
||||
pub mod system;
|
||||
mod ucontext;
|
||||
mod synch;
|
||||
mod thread_manager;
|
@ -1,5 +1,5 @@
|
||||
|
||||
#[derive(PartialEq)]
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct Process {
|
||||
pub num_thread: usize,
|
||||
}
|
@ -1,12 +1,14 @@
|
||||
use std::sync::Arc;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::utility::list::List;
|
||||
use crate::kernel::thread::Thread;
|
||||
|
||||
use super::system::{G_CURRENT_THREAD, G_THREAD_TO_BE_DESTROYED};
|
||||
use super::system::System;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct Scheduler {
|
||||
ready_list: List<Arc<Thread>>
|
||||
ready_list: List<Rc<RefCell<Thread>>>
|
||||
}
|
||||
|
||||
impl Scheduler {
|
||||
@ -27,7 +29,7 @@ impl Scheduler {
|
||||
/// ## Pamameter
|
||||
///
|
||||
/// **thread** is the thread to be put on the read list
|
||||
pub fn ready_to_run(&mut self, thread: Arc<Thread>) {
|
||||
pub fn ready_to_run(&mut self, thread: Rc<RefCell<Thread>>) {
|
||||
self.ready_list.push(thread);
|
||||
}
|
||||
|
||||
@ -37,7 +39,7 @@ impl Scheduler {
|
||||
/// Thread is removed from the ready list.
|
||||
///
|
||||
/// **return** Thread thread to be scheduled
|
||||
pub fn find_next_to_run(&mut self) -> Option<Arc<Thread>> {
|
||||
pub fn find_next_to_run(&mut self) -> Option<Rc<RefCell<Thread>>> {
|
||||
self.ready_list.pop()
|
||||
}
|
||||
|
||||
@ -51,34 +53,16 @@ impl Scheduler {
|
||||
/// ## Parameter
|
||||
///
|
||||
/// **next_thread** thread to dispatch to the CPU
|
||||
pub fn switch_to(&self, next_thread: Thread) {
|
||||
match G_CURRENT_THREAD.write() {
|
||||
Ok(mut current_thread) => {
|
||||
let old_thread = current_thread.as_mut().unwrap();
|
||||
|
||||
pub fn switch_to(&self, system: &System, next_thread: Rc<RefCell<Thread>>) {
|
||||
/* if let Some(old_thread) = system.get_g_current_thread() {
|
||||
old_thread.save_processor_state();
|
||||
old_thread.save_simulator_state();
|
||||
|
||||
if old_thread != &next_thread {
|
||||
next_thread.restore_processor_state();
|
||||
next_thread.restore_simulator_state();
|
||||
current_thread.replace(next_thread);
|
||||
}
|
||||
|
||||
match G_THREAD_TO_BE_DESTROYED.write() {
|
||||
Ok(mut thread_to_be_destroyed) => {
|
||||
if thread_to_be_destroyed.is_some() {
|
||||
drop(thread_to_be_destroyed.take());
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
panic!("RwLock is poisonned: {}", err);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
panic!("RwLock is poisonned: {}", err);
|
||||
}
|
||||
system.set_g_current_thread(Option::Some(next_thread));
|
||||
}
|
||||
} */
|
||||
}
|
||||
}
|
125
src/kernel/synch.rs
Normal file
@ -0,0 +1,125 @@
|
||||
use crate::utility::list::List;
|
||||
use crate::kernel::thread::Thread;
|
||||
use crate::simulator::interrupt::InterruptStatus::InterruptOff;
|
||||
use crate::simulator::machine::Machine;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
|
||||
use super::scheduler::Scheduler;
|
||||
use super::thread_manager::ThreadManager;
|
||||
|
||||
pub struct Semaphore<'t> {
|
||||
|
||||
counter:i32,
|
||||
waiting_queue:List<Rc<RefCell<Thread>>>,
|
||||
thread_manager: Rc<RefCell<ThreadManager<'t>>> // On s'assure que le tm vit plus longtemps que les semaphore avec le lifetime
|
||||
|
||||
}
|
||||
|
||||
impl<'t> Semaphore<'_> {
|
||||
|
||||
pub fn p(&mut self, current_thread: Rc<RefCell<Thread>>, machine: &mut Machine){
|
||||
let old_status = machine.interrupt.set_status(InterruptOff);
|
||||
self.counter -= 1;
|
||||
if self.counter < 0 {
|
||||
self.waiting_queue.push(Rc::clone(¤t_thread));
|
||||
self.thread_manager.borrow_mut().thread_sleep(current_thread);
|
||||
}
|
||||
machine.interrupt.set_status(old_status);
|
||||
}
|
||||
|
||||
pub fn v(&mut self, machine: &mut Machine, scheduler: &mut Scheduler){
|
||||
let old_status = machine.interrupt.set_status(InterruptOff);
|
||||
self.counter -= 1;
|
||||
if self.waiting_queue.peek() != None {
|
||||
scheduler.ready_to_run(self.waiting_queue.pop().unwrap());
|
||||
}
|
||||
machine.interrupt.set_status(old_status);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Lock<'t>{
|
||||
|
||||
owner: Rc<RefCell<Thread>>,
|
||||
waiting_queue:List<Rc<RefCell<Thread>>>,
|
||||
thread_manager: Rc<RefCell<ThreadManager<'t>>>,
|
||||
free: bool
|
||||
|
||||
}
|
||||
|
||||
impl<'t> Lock<'_> {
|
||||
pub fn acquire(&mut self, machine: &mut Machine, current_thread: Rc<RefCell<Thread>>) {
|
||||
let old_status = machine.interrupt.set_status(InterruptOff);
|
||||
|
||||
if self.free {
|
||||
self.free = false;
|
||||
self.owner = current_thread;
|
||||
} else {
|
||||
self.waiting_queue.push(Rc::clone(¤t_thread));
|
||||
self.thread_manager.borrow_mut().thread_sleep(current_thread);
|
||||
}
|
||||
|
||||
machine.interrupt.set_status(old_status);
|
||||
}
|
||||
|
||||
pub fn release(&mut self, machine: &mut Machine, scheduler: &mut Scheduler, current_thread: Rc<RefCell<Thread>>) {
|
||||
let old_status = machine.interrupt.set_status(InterruptOff);
|
||||
|
||||
if self.is_held_by_current_thread(current_thread) {
|
||||
if self.waiting_queue.peek() != None {
|
||||
self.owner = self.waiting_queue.pop().unwrap();
|
||||
scheduler.ready_to_run(Rc::clone(&self.owner));
|
||||
} else {
|
||||
self.free = true;
|
||||
}
|
||||
}
|
||||
|
||||
machine.interrupt.set_status(old_status);
|
||||
}
|
||||
|
||||
pub fn is_held_by_current_thread(&mut self, current_thread: Rc<RefCell<Thread>>) -> bool {
|
||||
Rc::ptr_eq(&self.owner, ¤t_thread)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Condition<'t>{
|
||||
|
||||
waiting_queue:List<Rc<RefCell<Thread>>>,
|
||||
thread_manager: Rc<RefCell<ThreadManager<'t>>>,
|
||||
|
||||
}
|
||||
|
||||
impl<'t> Condition<'_> {
|
||||
|
||||
pub fn wait(&mut self, machine: &mut Machine, current_thread: Rc<RefCell<Thread>>) {
|
||||
let old_status = machine.interrupt.set_status(InterruptOff);
|
||||
|
||||
self.waiting_queue.push(Rc::clone(¤t_thread));
|
||||
self.thread_manager.borrow_mut().thread_sleep(current_thread);
|
||||
|
||||
machine.interrupt.set_status(old_status);
|
||||
}
|
||||
|
||||
pub fn signal(&mut self, machine: &mut Machine, scheduler: &mut Scheduler) {
|
||||
let old_status = machine.interrupt.set_status(InterruptOff);
|
||||
|
||||
if self.waiting_queue.peek() != None {
|
||||
scheduler.ready_to_run(self.waiting_queue.pop().unwrap());
|
||||
}
|
||||
|
||||
machine.interrupt.set_status(old_status);
|
||||
|
||||
}
|
||||
|
||||
pub fn broadcast(&mut self, machine: &mut Machine, scheduler: &mut Scheduler) {
|
||||
let old_status = machine.interrupt.set_status(InterruptOff);
|
||||
|
||||
while self.waiting_queue.peek() != None {
|
||||
scheduler.ready_to_run(self.waiting_queue.pop().unwrap());
|
||||
}
|
||||
machine.interrupt.set_status(old_status);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,21 +1,59 @@
|
||||
use std::{sync::{RwLock, Arc}};
|
||||
use std::cell::RefCell;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use crate::simulator::machine::Machine;
|
||||
|
||||
use crate::{kernel::{thread::Thread, scheduler::Scheduler}, utility::list::List, simulator::machine::Machine};
|
||||
use super::thread_manager::ThreadManager;
|
||||
|
||||
extern crate lazy_static;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref G_MACHINE: RwLock<Machine> = RwLock::new(Machine::_init_machine());
|
||||
pub static ref G_CURRENT_THREAD: RwLock<Option<Thread>> = RwLock::new(Option::None);
|
||||
pub static ref G_THREAD_TO_BE_DESTROYED: RwLock<Option<Thread>> = RwLock::new(Option::None);
|
||||
pub static ref G_ALIVE: RwLock<List<Arc<Thread>>> = RwLock::new(List::new());
|
||||
pub static ref G_SCHEDULER: RwLock<Scheduler> = RwLock::new(Scheduler::new());
|
||||
/// # System
|
||||
///
|
||||
/// This structure represents the state of the threads running on the operating system.
|
||||
/// It contains references to the following:
|
||||
///
|
||||
/// - The simulated machine
|
||||
/// - The current running thread
|
||||
/// - The list of active threads
|
||||
/// - The thread to be destroyed next
|
||||
/// - The scheduler which acts upon these threads
|
||||
#[derive(PartialEq)]
|
||||
pub struct System<'a> {
|
||||
g_machine: RefCell<Machine>,
|
||||
thread_manager: ThreadManager<'a>
|
||||
}
|
||||
|
||||
impl<'a> System<'a> {
|
||||
|
||||
#[derive(PartialEq)]
|
||||
/// System constructor
|
||||
pub fn new(machine: Machine) -> System<'a> {
|
||||
Self {
|
||||
g_machine: RefCell::new(machine),
|
||||
thread_manager: ThreadManager::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// use thread_manager setter to send it system instance
|
||||
pub fn freeze(&'a mut self) {
|
||||
self.thread_manager.system.set(Option::Some(self));
|
||||
}
|
||||
|
||||
// GETTERS
|
||||
|
||||
/// Returns the Machine
|
||||
///
|
||||
/// Useful to access RAM, devices, ...
|
||||
pub fn get_g_machine(&self) -> &RefCell<Machine> {
|
||||
&self.g_machine
|
||||
}
|
||||
|
||||
// Setters
|
||||
|
||||
/// Assign a machine to the system
|
||||
pub fn set_g_machine(&mut self, machine: RefCell<Machine>) {
|
||||
self.g_machine = machine
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum ObjectType {
|
||||
SemaphoreType,
|
||||
LockType,
|
||||
@ -24,3 +62,25 @@ pub enum ObjectType {
|
||||
ThreadType,
|
||||
InvalidType
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::{System, Machine};
|
||||
|
||||
macro_rules! init_system {
|
||||
() => {{
|
||||
let m = Machine::init_machine();
|
||||
init_system!(m)
|
||||
}};
|
||||
($a:expr) => {{
|
||||
System::new($a)
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_init_system() {
|
||||
init_system!();
|
||||
}
|
||||
|
||||
}
|
@ -1,80 +1,50 @@
|
||||
use std::{sync::Arc};
|
||||
use super::{process::Process, system::ObjectType, thread_manager::SIMULATORSTACKSIZE};
|
||||
use crate::{simulator::machine::{NUM_INT_REGS, NUM_FP_REGS, STACK_REG}};
|
||||
|
||||
use super::{process::Process, mgerror::ErrorCode, system::{ObjectType, G_ALIVE, G_SCHEDULER}, ucontext::UContextT};
|
||||
use crate::{simulator::machine::{NUM_INT_REGS, NUM_FP_REGS, STACK_REG}, kernel::system::{G_MACHINE, G_THREAD_TO_BE_DESTROYED}};
|
||||
|
||||
const SIMULATORSTACKSIZE: usize = 32 * 1024;
|
||||
const STACK_FENCEPOST: u32 = 0xdeadbeef;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct ThreadContext {
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct ThreadContext {
|
||||
pub int_registers: [i64; NUM_INT_REGS],
|
||||
pub float_registers: [i64; NUM_FP_REGS],
|
||||
pub float_registers: [f32; NUM_FP_REGS],
|
||||
pc: i64,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct Thread {
|
||||
name: String,
|
||||
process: Option<Process>,
|
||||
pub process: Option<Process>,
|
||||
// simulation_context: UContextT,
|
||||
thread_context: ThreadContext,
|
||||
stack_pointer: i32,
|
||||
pub thread_context: ThreadContext,
|
||||
pub stack_pointer: i32,
|
||||
object_type: ObjectType
|
||||
}
|
||||
|
||||
impl Thread {
|
||||
|
||||
pub fn new(name: String) -> Self {
|
||||
/// Thread constructor
|
||||
pub fn new(name: &str) -> Self {
|
||||
Self {
|
||||
name,
|
||||
name: String::from(name),
|
||||
process: None,
|
||||
// simulation_context: UContextT::new(),
|
||||
thread_context: ThreadContext {
|
||||
int_registers: [0; NUM_INT_REGS],
|
||||
float_registers: [0; NUM_FP_REGS],
|
||||
float_registers: [0f32; NUM_FP_REGS],
|
||||
pc: 0
|
||||
},
|
||||
stack_pointer: 0,
|
||||
object_type: ObjectType::ThreadType
|
||||
object_type: ObjectType::ThreadType,
|
||||
}
|
||||
}
|
||||
|
||||
/// Start a thread, attaching it to a process
|
||||
pub fn start(mut self, owner: Process, func: i64, arg: i64) -> Result<(), ErrorCode> {
|
||||
self.process = Option::Some(owner);
|
||||
let ptr = 0; // todo addrspace
|
||||
self.init_thread_context(func, ptr, arg);
|
||||
let base_stack_addr: [i8; SIMULATORSTACKSIZE] = [0; SIMULATORSTACKSIZE]; // todo AllocBoundedArray
|
||||
self.init_simulator_context(base_stack_addr);
|
||||
self.process.as_mut().unwrap().num_thread += 1;
|
||||
match G_ALIVE.write() {
|
||||
Ok(mut alive) => {
|
||||
let this = Arc::new(self);
|
||||
alive.push(Arc::clone(&this));
|
||||
match G_SCHEDULER.write() {
|
||||
Ok(mut scheduler) => {
|
||||
scheduler.ready_to_run(Arc::clone(&this));
|
||||
},
|
||||
Err(err) => {
|
||||
panic!("RwLock poisonned, {}", err);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
panic!("RwLock poisonned, {}", err);
|
||||
}
|
||||
}
|
||||
Result::Ok(())
|
||||
}
|
||||
|
||||
fn init_thread_context(&mut self, initial_pc_reg: i64, initial_sp: i64, arg: i64) {
|
||||
pub fn init_thread_context(&mut self, initial_pc_reg: i64, initial_sp: i64, arg: i64) {
|
||||
self.thread_context.pc = initial_pc_reg;
|
||||
self.thread_context.int_registers[10] = arg;
|
||||
self.thread_context.int_registers[STACK_REG] = initial_sp;
|
||||
}
|
||||
|
||||
fn init_simulator_context(&self, base_stack_addr: [i8; SIMULATORSTACKSIZE]) {
|
||||
pub fn init_simulator_context(&self, base_stack_addr: [i8; SIMULATORSTACKSIZE]) {
|
||||
// let res = self.simulation_context.get_context();
|
||||
// if res != 0 {
|
||||
// panic!("getcontext returns non-zero value {}", res);
|
||||
@ -89,83 +59,22 @@ impl Thread {
|
||||
// self.simulation_context.stackBottom[0] = STACK_FENCEPOST;
|
||||
}
|
||||
|
||||
/// Wait for another thread to finish its execution
|
||||
pub fn join(&self, id_thread: Arc<Thread>) {
|
||||
match G_ALIVE.write() {
|
||||
Ok(alive) => {
|
||||
while alive.contains(&Arc::clone(&id_thread)) {
|
||||
self.t_yield();
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
panic!("RwLock poisonned, {}", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Relinquish the CPU if any other thread is runnable.
|
||||
///
|
||||
/// Cannot use yield as a function name -> reserved name in rust
|
||||
pub fn t_yield(&self) {
|
||||
todo!();
|
||||
}
|
||||
|
||||
/// Put the thread to sleep and relinquish the processor
|
||||
pub fn sleep(&self) {
|
||||
todo!();
|
||||
}
|
||||
|
||||
/// Finish the execution of the thread and prepare its deallocation
|
||||
pub fn finish(mut self) {
|
||||
match G_MACHINE.write() {
|
||||
Ok(mut machine) => {
|
||||
let old_status = machine.interrupt.set_status(crate::simulator::interrupt::InterruptStatus::InterruptOff);
|
||||
match G_ALIVE.write() {
|
||||
Ok(alive) => {
|
||||
// todo alive.remove(T) à implémenter dans List
|
||||
},
|
||||
Err(err) => {
|
||||
panic!("RwLock is poisoned: {}", err);
|
||||
}
|
||||
}
|
||||
match G_THREAD_TO_BE_DESTROYED.write() {
|
||||
Ok(mut thread_to_be_destroyed) => {
|
||||
thread_to_be_destroyed.replace(self);
|
||||
},
|
||||
Err(err) => {
|
||||
panic!("RwLock is poisoned: {}", err);
|
||||
}
|
||||
}
|
||||
// self.sleep();
|
||||
machine.interrupt.set_status(old_status);
|
||||
},
|
||||
Err(err) => {
|
||||
panic!("RwLock is poisoned: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
todo!();
|
||||
}
|
||||
|
||||
/// Check if a thread has overflowed its stack
|
||||
///
|
||||
/// This assertion doesn't catch all stack overflow conditions and your program may still crash because of an overflow.
|
||||
///
|
||||
pub fn check_overflow(&self) {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub fn save_processor_state(&self) {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub fn restore_processor_state(&self) {
|
||||
todo!();
|
||||
// if self.simulator_context.stackBottom != STACK_FENCEPOST {
|
||||
// panic!("thread {} has overflowed", self.get_name())
|
||||
// }
|
||||
}
|
||||
|
||||
pub fn save_simulator_state(&self) {
|
||||
todo!();
|
||||
// todo!(); // simulator state will maybe be removed so panic call is remove. See ucontext.rs
|
||||
}
|
||||
|
||||
pub fn restore_simulator_state(&self) {
|
||||
todo!();
|
||||
// todo!(); // simulator state will maybe be removed so panic call is remove. See ucontext.rs
|
||||
}
|
||||
|
||||
pub fn get_name(&self) -> String {
|
||||
@ -186,3 +95,54 @@ impl Drop for Thread {
|
||||
fn start_thread_execution() {
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::{Thread, ThreadContext, NUM_INT_REGS, NUM_FP_REGS, ObjectType};
|
||||
const DEFAULT_THREAD_NAME: &str = "test_thread";
|
||||
|
||||
/// Polymorphic macro to get thread without passing a name by default
|
||||
macro_rules! get_new_thread {
|
||||
() => { Thread::new(DEFAULT_THREAD_NAME) };
|
||||
($a:literal) => {
|
||||
Thread::new(&$a.to_string())
|
||||
};
|
||||
}
|
||||
|
||||
/// This macro allows for getting a Thread for which we've ensured proper initial state
|
||||
/// in case a commit further down the line changes the initial state of threads generated
|
||||
/// from Thread::new
|
||||
macro_rules! expected_initial_state {
|
||||
() => { expected_initial_state!(DEFAULT_THREAD_NAME) };
|
||||
($a:expr) => { {
|
||||
let mut x = Thread::new($a);
|
||||
x.name = $a.to_string();
|
||||
x.process = Option::None;
|
||||
x.thread_context = ThreadContext {
|
||||
int_registers: [0; NUM_INT_REGS],
|
||||
float_registers: [0f32; NUM_FP_REGS],
|
||||
pc: 0
|
||||
};
|
||||
x.stack_pointer = 0;
|
||||
x.object_type = ObjectType::ThreadType;
|
||||
x }
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro() {
|
||||
let t = get_new_thread!("hello");
|
||||
assert_eq!(t.get_name(), "hello");
|
||||
let t = get_new_thread!(1);
|
||||
assert_eq!(t.get_name(), "1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_init() {
|
||||
let t = get_new_thread!();
|
||||
let expected_state = expected_initial_state!();
|
||||
assert_eq!(t, expected_state)
|
||||
}
|
||||
|
||||
}
|
138
src/kernel/thread_manager.rs
Normal file
@ -0,0 +1,138 @@
|
||||
use std::{rc::Rc, cell::{Cell, RefCell, RefMut, Ref}};
|
||||
|
||||
use crate::{utility::list::List, simulator::machine::{NUM_INT_REGS, NUM_FP_REGS}};
|
||||
|
||||
use super::{scheduler::Scheduler, thread::Thread, system::System, mgerror::ErrorCode, process::Process};
|
||||
|
||||
pub const SIMULATORSTACKSIZE: usize = 32 * 1024;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct ThreadManager<'a> {
|
||||
pub g_current_thread: Option<Thread>,
|
||||
pub g_thread_to_be_destroyed: Option<Thread>,
|
||||
pub g_alive: List<Rc<RefCell<Thread>>>,
|
||||
pub g_scheduler: Scheduler,
|
||||
pub system: Cell<Option<&'a System<'a>>>
|
||||
}
|
||||
|
||||
impl<'a> ThreadManager<'a> {
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
g_current_thread: Option::None,
|
||||
g_thread_to_be_destroyed: Option::None,
|
||||
g_alive: List::new(),
|
||||
g_scheduler: Scheduler::new(),
|
||||
system: Cell::new(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Start a thread, attaching it to a process
|
||||
pub fn start_thread(&mut self, thread: Rc<RefCell<Thread>>, owner: Process, func_pc: i64, argument: i64) -> Result<(), ErrorCode> {
|
||||
let mut thread_m = thread.borrow_mut();
|
||||
thread_m.process = Option::Some(owner);
|
||||
let ptr = 0; // todo addrspace
|
||||
thread_m.init_thread_context(func_pc, ptr, argument);
|
||||
let base_stack_addr: [i8; SIMULATORSTACKSIZE] = [0; SIMULATORSTACKSIZE]; // todo AllocBoundedArray
|
||||
thread_m.init_simulator_context(base_stack_addr);
|
||||
thread_m.process.as_mut().unwrap().num_thread += 1;
|
||||
self.get_g_alive().push(Rc::clone(&thread));
|
||||
self.g_scheduler().ready_to_run(Rc::clone(&thread));
|
||||
Result::Ok(())
|
||||
}
|
||||
|
||||
/// Wait for another thread to finish its execution
|
||||
pub fn thread_join(&mut self, id_thread: Rc<RefCell<Thread>>) {
|
||||
while self.get_g_alive().contains(&Rc::clone(&id_thread)) {
|
||||
self.thread_yield(Rc::clone(&id_thread));
|
||||
}
|
||||
}
|
||||
|
||||
/// Relinquish the CPU if any other thread is runnable.
|
||||
///
|
||||
/// Cannot use yield as a function name -> reserved name in rust
|
||||
pub fn thread_yield(&mut self, thread: Rc<RefCell<Thread>>) {
|
||||
if let Some(system) = self.system.get() {
|
||||
let mut machine = system.get_g_machine().borrow_mut();
|
||||
let old_status = machine.interrupt.set_status(crate::simulator::interrupt::InterruptStatus::InterruptOff);
|
||||
|
||||
let next_thread = self.g_scheduler().find_next_to_run();
|
||||
if let Some(next_thread) = next_thread {
|
||||
let scheduler = self.g_scheduler();
|
||||
scheduler.ready_to_run(thread);
|
||||
scheduler.switch_to(system, next_thread);
|
||||
}
|
||||
machine.interrupt.set_status(old_status);
|
||||
}
|
||||
}
|
||||
|
||||
/// Put the thread to sleep and relinquish the processor
|
||||
pub fn thread_sleep(&mut self, thread: Rc<RefCell<Thread>>) {
|
||||
todo!();
|
||||
}
|
||||
|
||||
/// Finish the execution of the thread and prepare its deallocation
|
||||
pub fn thread_finish(&self, thread: Rc<RefCell<Thread>>) {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub fn thread_save_processor_state(&mut self, thread: Rc<RefCell<Thread>>) {
|
||||
if let Some(system) = self.system.get() {
|
||||
let mut t: RefMut<_> = thread.borrow_mut();
|
||||
for i in 0..NUM_INT_REGS {
|
||||
t.thread_context.int_registers[i] = system.get_g_machine().borrow().read_int_register(i);
|
||||
}
|
||||
for i in 0..NUM_FP_REGS {
|
||||
t.thread_context.float_registers[i] = system.get_g_machine().borrow().read_fp_register(i);
|
||||
}
|
||||
} else {
|
||||
unreachable!("System is None")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn thread_restore_processor_state(&self, thread: Rc<RefCell<Thread>>) {
|
||||
if let Some(system) = self.system.get() {
|
||||
let t: Ref<_> = thread.borrow();
|
||||
for i in 0..NUM_INT_REGS {
|
||||
let machine = system.get_g_machine();
|
||||
let mut machine = machine.borrow_mut();
|
||||
machine.write_int_register(i, t.thread_context.int_registers[i]);
|
||||
}
|
||||
} else {
|
||||
unreachable!("System is None")
|
||||
}
|
||||
}
|
||||
|
||||
/// Currently running thread
|
||||
pub fn get_g_current_thread(&mut self) -> &mut Option<Thread> {
|
||||
&mut self.g_current_thread
|
||||
}
|
||||
|
||||
/// Thread to be destroyed by [...]
|
||||
///
|
||||
/// TODO: Finish the comment with the relevant value
|
||||
pub fn get_g_thread_to_be_destroyed(&mut self) -> &mut Option<Thread> {
|
||||
&mut self.g_thread_to_be_destroyed
|
||||
}
|
||||
|
||||
/// List of alive threads
|
||||
pub fn get_g_alive(&mut self) -> &mut List<Rc<RefCell<Thread>>> {
|
||||
&mut self.g_alive
|
||||
}
|
||||
|
||||
/// Current scheduler
|
||||
pub fn g_scheduler(&mut self) -> &mut Scheduler {
|
||||
&mut self.g_scheduler
|
||||
}
|
||||
|
||||
/// Set currently running thread
|
||||
pub fn set_g_current_thread(&mut self, thread: Option<Thread>) {
|
||||
self.g_current_thread = thread
|
||||
}
|
||||
|
||||
/// Set thread to be destroyed next
|
||||
pub fn set_g_thread_to_be_destroyed(&mut self, thread: Option<Thread>) {
|
||||
self.g_thread_to_be_destroyed = thread
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
/// Safe wrapper for ucontext_t struct of linux-gnu libc
|
||||
@ -11,19 +11,20 @@ use std::mem::MaybeUninit;
|
||||
#[derive(PartialEq)]
|
||||
pub struct UContextT {
|
||||
#[cfg(not(target_os = "windows"))] // struct non disponible sur la libc sur windows
|
||||
pub buf: lib::ucontext_t,
|
||||
pub stackBottom: Vec<i8>
|
||||
pub buf: libc::ucontext_t,
|
||||
pub stack_bottom: Vec<i8>
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[allow(unused)] // Temporary as we currently doesn't use this structure (this structure may disapear in a near future)
|
||||
impl UContextT {
|
||||
|
||||
pub fn new() -> Self {
|
||||
let mut context = MaybeUninit::<ucontext_t>::uninit();
|
||||
unsafe { lib::getcontext(context.as_mut_ptr()) };
|
||||
let mut context = MaybeUninit::<libc::ucontext_t>::uninit();
|
||||
unsafe { libc::getcontext(context.as_mut_ptr()) };
|
||||
Self {
|
||||
buf: unsafe { context.assume_init() },
|
||||
stackBottom: Vec::default(),
|
||||
stack_bottom: Vec::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,7 +33,7 @@ impl UContextT {
|
||||
/// Use `man getcontext` for more informations
|
||||
pub fn get_context(&mut self) -> i32 {
|
||||
unsafe {
|
||||
lib::getcontext(&mut self.buf)
|
||||
libc::getcontext(&mut self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,24 +42,25 @@ impl UContextT {
|
||||
/// Use `man setcontext` for more informations
|
||||
pub fn set_context(&mut self) -> i32 {
|
||||
unsafe {
|
||||
lib::setcontext(&self.buf)
|
||||
libc::setcontext(&self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_context(&mut self, func: extern "C" fn(), args: i32) {
|
||||
unsafe {
|
||||
lib::makecontext(&mut self.buf, func, args)
|
||||
libc::makecontext(&mut self.buf, func, args)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[allow(unused)]
|
||||
impl UContextT {
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
stackBottom: Vec::default()
|
||||
stack_bottom: Vec::default()
|
||||
}
|
||||
}
|
||||
|
||||
|
23
src/main.rs
@ -1,16 +1,23 @@
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![warn(clippy::missing_docs_in_private_items)]
|
||||
|
||||
//! Crate burritos ((BurritOS Using Rust Really Improves The Operating System)
|
||||
//!
|
||||
//! Burritos is an educational operating system written in Rust
|
||||
//! running on RISC-V emulator.
|
||||
|
||||
/// Contain hardware simulated part of the machine
|
||||
mod simulator;
|
||||
mod kernel;
|
||||
/// module containing useful tools which can be use in most part of the OS to ease the development of the OS
|
||||
pub mod utility;
|
||||
|
||||
use kernel::system::System;
|
||||
use simulator::machine::Machine;
|
||||
|
||||
fn main() {
|
||||
let mut m = Machine::_init_machine();
|
||||
m.main_memory[4] = 43;
|
||||
m.main_memory[5] = 150;
|
||||
let a : u8 = 128;
|
||||
let b : i8 = a as i8;
|
||||
let c : u8 = b as u8;
|
||||
println!("aaa {c}");
|
||||
println!("read_memory : {}", Machine::read_memory(&mut m, 2, 4));
|
||||
let machine = Machine::init_machine();
|
||||
let mut system = System::new(machine);
|
||||
system.freeze();
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct Interrupt {
|
||||
level: InterruptStatus
|
||||
}
|
||||
@ -12,10 +12,10 @@ impl Interrupt {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_status(&mut self, newStatus: InterruptStatus) -> InterruptStatus {
|
||||
pub fn set_status(&mut self, new_status: InterruptStatus) -> InterruptStatus {
|
||||
let old = self.level;
|
||||
self.level = newStatus;
|
||||
if newStatus == InterruptStatus::InterruptOn && old == InterruptStatus::InterruptOff {
|
||||
self.level = new_status;
|
||||
if new_status == InterruptStatus::InterruptOn && old == InterruptStatus::InterruptOff {
|
||||
self.one_tick(1);
|
||||
}
|
||||
old
|
||||
|
34
src/simulator/loader.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use crate::Machine;
|
||||
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::BufRead;
|
||||
|
||||
|
||||
|
||||
/// Load a file into a new machine
|
||||
///
|
||||
/// `panic!` when size is not 1, 2, 4 or 8
|
||||
/// `panic!` when the text does not represents instructions in hexadecimal
|
||||
///
|
||||
/// ### Parameters
|
||||
///
|
||||
/// - **path** the path of the file to load
|
||||
/// - **size** the number of bytes to write (1, 2, 4 or 8)
|
||||
pub fn _load(path : &str, instruction_size: i32) -> Machine {
|
||||
let file = fs::File::open(path).expect("Wrong filename");
|
||||
let reader = io::BufReader::new(file);
|
||||
let mut machine = Machine::init_machine();
|
||||
|
||||
for (i,line) in reader.lines().enumerate() {
|
||||
let res = u64::from_str_radix(&line.unwrap(), 16);
|
||||
match res {
|
||||
Ok(value) => {
|
||||
Machine::write_memory(&mut machine, instruction_size, i*instruction_size as usize, value);
|
||||
},
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
println!("{:x}", Machine::read_memory(& mut machine, 4, 0));
|
||||
machine
|
||||
}
|
@ -1,16 +1,41 @@
|
||||
use std::{ops::{Add, Sub}, io::Write};
|
||||
|
||||
use crate::simulator::print;
|
||||
|
||||
use super::{decode::{Instruction, decode}, interrupt::Interrupt};
|
||||
use super::global::*;
|
||||
use std::fs::File;
|
||||
|
||||
|
||||
/*
|
||||
* Decommenter la variant si il est utilisé quelque part
|
||||
*/
|
||||
pub enum ExceptionType {
|
||||
NO_EXCEPTION,//Everything ok!
|
||||
//SYSCALL_EXCEPTION,//A program executed a system call.
|
||||
PAGEFAULT_EXCEPTION,//Page fault exception
|
||||
READONLY_EXCEPTION,//Write attempted to a page marked "read-only" */
|
||||
//BUSERROR_EXCEPTION,
|
||||
/* translation resulted
|
||||
in an invalid physical
|
||||
address (mis-aligned or
|
||||
out-of-bounds) */
|
||||
ADDRESSERROR_EXCEPTION, /* Reference that was
|
||||
not mapped in the address
|
||||
space */
|
||||
//OVERFLOW_EXCEPTION, //Integer overflow in add or sub.
|
||||
//ILLEGALINSTR_EXCEPTION, //Unimplemented or reserved instr.
|
||||
//NUM_EXCEPTION_TYPES
|
||||
}
|
||||
|
||||
|
||||
pub const STACK_REG: usize = 2;
|
||||
|
||||
pub const NUM_INT_REGS: usize = 32;
|
||||
pub const NUM_FP_REGS: usize = 32;
|
||||
|
||||
/// doit disparaitre
|
||||
const MEM_SIZE : usize = 4096;
|
||||
const MEM_SIZE : usize = 0x500000;
|
||||
|
||||
pub trait RegisterNum: Add<Output=Self> + Sub<Output=Self> + PartialEq + Copy {}
|
||||
|
||||
@ -19,7 +44,7 @@ impl RegisterNum for i64 {}
|
||||
impl RegisterNum for f32 {}
|
||||
|
||||
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct Register<U: RegisterNum> {
|
||||
register: [U; 32]
|
||||
}
|
||||
@ -45,7 +70,7 @@ impl Register<i64> {
|
||||
self.register[position] = value;
|
||||
} else {
|
||||
// Panic ou rien ? (dans le doute pour le moment panic)
|
||||
unreachable!("You can't write to zero register")
|
||||
// unreachable!("You can't write to zero register")
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,14 +90,16 @@ impl Register<f32> {
|
||||
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct Machine {
|
||||
pub pc : u64,
|
||||
pub sp: usize,
|
||||
pub int_reg : Register<i64>,
|
||||
pub fp_reg : Register<f32>,
|
||||
pub main_memory : [u8 ; MEM_SIZE],
|
||||
pub main_memory : Vec<u8>,
|
||||
pub shiftmask : [u64 ; 64],
|
||||
pub interrupt: Interrupt,
|
||||
pub registers_trace : String, // for tests
|
||||
pub interrupt: Interrupt
|
||||
// futur taille à calculer int memSize = g_cfg->NumPhysPages * g_cfg->PageSize;
|
||||
//creer une struct cfg(configuration) qui s'initialise avec valeur dans un fichier cfg
|
||||
}
|
||||
@ -80,7 +107,7 @@ pub struct Machine {
|
||||
|
||||
impl Machine {
|
||||
|
||||
pub fn _init_machine() -> Machine {
|
||||
pub fn init_machine() -> Machine {
|
||||
let mut shiftmask : [u64 ; 64] = [0 ; 64];
|
||||
let mut value : u64 = 0xffffffff;
|
||||
|
||||
@ -90,16 +117,19 @@ impl Machine {
|
||||
value >>= 1;
|
||||
}
|
||||
|
||||
Machine {
|
||||
let mut ret = Machine {
|
||||
pc : 0,
|
||||
sp: 0,
|
||||
int_reg : Register::<i64>::init(),
|
||||
fp_reg : Register::<f32>::init(),
|
||||
main_memory : [0 ; MEM_SIZE],
|
||||
main_memory : vec![0_u8; MEM_SIZE],
|
||||
shiftmask,
|
||||
interrupt: Interrupt::new()
|
||||
}
|
||||
interrupt: Interrupt::new(),
|
||||
registers_trace : String::from("")
|
||||
};
|
||||
|
||||
ret.int_reg.set_reg(10, -1);
|
||||
ret
|
||||
}
|
||||
|
||||
/// Read from main memory of the machine
|
||||
@ -150,9 +180,43 @@ impl Machine {
|
||||
/// ### Parameters
|
||||
///
|
||||
/// - **machine** contains the memory
|
||||
pub fn extract_memory(machine: &mut Machine){
|
||||
let mut file = File::create("burritos_memory.txt").unwrap();
|
||||
file.write(&machine.main_memory);
|
||||
pub fn _extract_memory(machine: &mut Machine){
|
||||
let file_path = "burritos_memory.txt";
|
||||
let write_to_file = |path| -> std::io::Result<File> {
|
||||
let mut file = File::create(path)?;
|
||||
file.write_all(&machine.main_memory)?;
|
||||
Ok(file)
|
||||
};
|
||||
match write_to_file(file_path) {
|
||||
Err(e) => eprintln!("Failed to write memory to file: {}", e),
|
||||
Ok(_) => println!("Memory extracted to {}", file_path)
|
||||
};
|
||||
}
|
||||
|
||||
pub fn print_machine_status(machine: &mut Machine) {
|
||||
println!("######### Machine status #########");
|
||||
for i in (0..32).step_by(3) {
|
||||
print!(">{0: <4} : {1:<16x} ", print::REG_X[i], machine.int_reg.get_reg(i));
|
||||
print!(">{0: <4} : {1:<16x} ", print::REG_X[i+1], machine.int_reg.get_reg(i+1));
|
||||
if i+2 < 32 {
|
||||
print!(">{0: <4} : {1:<16x} ", print::REG_X[i+2], machine.int_reg.get_reg(i+2));
|
||||
}
|
||||
println!();
|
||||
}
|
||||
println!("________________SP________________");
|
||||
let sp_index = machine.int_reg.get_reg(2);
|
||||
for i in 0..5 {
|
||||
println!("SP+{:<2} : {:16x}", i*8, Self::read_memory(machine, 8, (sp_index + i*8) as usize));
|
||||
}
|
||||
println!("##################################");
|
||||
}
|
||||
|
||||
pub fn string_registers(machine: &mut Machine) -> String {
|
||||
let mut s = String::from("");
|
||||
for i in 0..32 {
|
||||
s.push_str(format!("{} ", machine.int_reg.get_reg(i)).as_str());
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
/// Execute the instructions table of a machine putted in param
|
||||
@ -160,11 +224,9 @@ impl Machine {
|
||||
/// ### Parameters
|
||||
///
|
||||
/// - **machine** which contains a table of instructions
|
||||
pub fn run(machine : Machine){
|
||||
let mut m = machine;
|
||||
loop{
|
||||
Machine::one_instruction(&mut m);
|
||||
}
|
||||
pub fn run(machine : &mut Machine){
|
||||
while Machine::one_instruction(machine) == 0 {}
|
||||
println!("trace : \n{}", machine.registers_trace);
|
||||
}
|
||||
|
||||
/// execute the current instruction
|
||||
@ -172,7 +234,7 @@ impl Machine {
|
||||
/// ### Parameters
|
||||
///
|
||||
/// - **machine** which contains a table of instructions and a pc to the actual instruction
|
||||
pub fn one_instruction(machine :&mut Machine) {
|
||||
pub fn one_instruction(machine :&mut Machine) -> i32 {
|
||||
|
||||
let unsigned_reg1 : u64;
|
||||
let unsigned_reg2 : u64;
|
||||
@ -187,32 +249,37 @@ impl Machine {
|
||||
uint64_t value;*/
|
||||
|
||||
if machine.main_memory.len() <= machine.pc as usize {
|
||||
println!("ERROR : number max of instructions rushed");
|
||||
return ;
|
||||
panic!("ERROR : number max of instructions rushed");
|
||||
}
|
||||
let mut val: [u8; 8] = [0; 8];
|
||||
for i in 0..8 {
|
||||
val[i] = machine.main_memory[machine.pc as usize + i];
|
||||
let mut val: [u8; 4] = [0; 4];
|
||||
for (i, mut _item) in val.iter_mut().enumerate() {
|
||||
_item = &mut machine.main_memory[machine.pc as usize + i];
|
||||
}
|
||||
|
||||
let val = u64::from_le_bytes(val);
|
||||
let val = u32::from_be_bytes(val) as u64;
|
||||
let inst : Instruction = decode(val);
|
||||
Self::print_machine_status(machine);
|
||||
println!("executing instruction : {:016x} at pc {:x}", val, machine.pc);
|
||||
println!("{}", print::print(decode(val), machine.pc as i32));
|
||||
let trace = Self::string_registers(machine);
|
||||
machine.registers_trace.push_str(format!("{}\n", trace).as_str());
|
||||
|
||||
machine.pc += 4;
|
||||
|
||||
match inst.opcode {
|
||||
RISCV_LUI => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, inst.imm31_12 as i64);
|
||||
},
|
||||
RISCV_AUIPC => {
|
||||
machine.int_reg.set_reg(inst.rd as usize,machine.pc as i64 - 8 + inst.imm31_12 as i64);
|
||||
machine.int_reg.set_reg(inst.rd as usize,machine.pc as i64 - 4 + inst.imm31_12 as i64);
|
||||
},
|
||||
RISCV_JAL => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.pc as i64);
|
||||
machine.pc += inst.imm21_1_signed as u64 - 8;
|
||||
machine.pc = (machine.pc as i64 + inst.imm21_1_signed as i64 - 4) as u64;
|
||||
},
|
||||
RISCV_JALR => {
|
||||
let tmp = machine.pc;
|
||||
machine.pc = (machine.int_reg.get_reg(inst.rs1 as usize) as u64 + inst.imm12_I_signed as u64) & 0xfffffffe;
|
||||
machine.pc = (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_I_signed as i64) as u64 & 0xfffffffe;
|
||||
machine.int_reg.set_reg(inst.rd as usize, tmp as i64);
|
||||
},
|
||||
|
||||
@ -222,32 +289,32 @@ impl Machine {
|
||||
match inst.funct3 {
|
||||
RISCV_BR_BEQ => {
|
||||
if machine.int_reg.get_reg(inst.rs1 as usize) == machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||
machine.pc += inst.imm13_signed as u64 - 8;
|
||||
machine.pc = (machine.pc as i64 + inst.imm13_signed as i64 - 4) as u64;
|
||||
}
|
||||
},
|
||||
RISCV_BR_BNE => {
|
||||
if machine.int_reg.get_reg(inst.rs1 as usize) != machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||
machine.pc += inst.imm13_signed as u64 - 8;
|
||||
machine.pc = (machine.pc as i64 + inst.imm13_signed as i64 - 4) as u64;
|
||||
}
|
||||
},
|
||||
RISCV_BR_BLT => {
|
||||
if machine.int_reg.get_reg(inst.rs1 as usize) < machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||
machine.pc += inst.imm13_signed as u64 - 8;
|
||||
machine.pc = (machine.pc as i64 + inst.imm13_signed as i64 - 4) as u64;
|
||||
}
|
||||
},
|
||||
RISCV_BR_BGE => {
|
||||
if machine.int_reg.get_reg(inst.rs1 as usize) >= machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||
machine.pc += inst.imm13_signed as u64 - 8;
|
||||
machine.pc = (machine.pc as i64 + inst.imm13_signed as i64 - 4) as u64;
|
||||
}
|
||||
},
|
||||
RISCV_BR_BLTU => {
|
||||
if machine.int_reg.get_reg(inst.rs1 as usize) < machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||
machine.pc += inst.imm13_signed as u64 - 8;
|
||||
machine.pc = (machine.pc as i64 + inst.imm13_signed as i64 - 4) as u64;
|
||||
}
|
||||
},
|
||||
RISCV_BR_BGEU => {
|
||||
if machine.int_reg.get_reg(inst.rs1 as usize) >= machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||
machine.pc += inst.imm13_signed as u64 - 8;
|
||||
machine.pc = (machine.pc as i64 + inst.imm13_signed as i64 - 4) as u64;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
@ -273,7 +340,7 @@ impl Machine {
|
||||
machine.int_reg.set_reg(inst.rd as usize, tmp);
|
||||
},
|
||||
RISCV_LD_LD => {
|
||||
let tmp = Self::read_memory(machine, 1, (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_I_signed as i64) as usize) as i64;
|
||||
let tmp = Self::read_memory(machine, 8, (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_I_signed as i64) as usize) as i64;
|
||||
machine.int_reg.set_reg(inst.rd as usize, tmp);
|
||||
},
|
||||
_ => {
|
||||
@ -309,7 +376,7 @@ impl Machine {
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_I_signed as i64);
|
||||
},
|
||||
RISCV_OPI_SLTI => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, if machine.int_reg.get_reg(inst.rs1 as usize) < inst.imm12_I_signed as i64 { 1 } else { 0 } );
|
||||
machine.int_reg.set_reg(inst.rd as usize, (machine.int_reg.get_reg(inst.rs1 as usize) < inst.imm12_I_signed as i64) as i64);
|
||||
},
|
||||
RISCV_OPI_XORI => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) ^ inst.imm12_I_signed as i64);
|
||||
@ -429,12 +496,11 @@ impl Machine {
|
||||
machine.int_reg.set_reg(inst.rd as usize, result);
|
||||
},
|
||||
RISCV_OPIW_SRW => {
|
||||
let result;
|
||||
if inst.funct7 == RISCV_OPIW_SRW_SRLIW {
|
||||
result = (local_data >> inst.shamt) & machine.shiftmask[32 + inst.shamt as usize] as i64;
|
||||
let result = if inst.funct7 == RISCV_OPIW_SRW_SRLIW {
|
||||
(local_data >> inst.shamt) & machine.shiftmask[32 + inst.shamt as usize] as i64
|
||||
} else { // SRAIW
|
||||
result = local_data >> inst.shamt;
|
||||
}
|
||||
local_data >> inst.shamt
|
||||
};
|
||||
machine.int_reg.set_reg(inst.rd as usize, result);
|
||||
},
|
||||
_ => {
|
||||
@ -591,13 +657,13 @@ impl Machine {
|
||||
RISCV_FP_FCMP => {
|
||||
match inst.funct3 {
|
||||
RISCV_FP_FCMP_FEQ => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, if machine.fp_reg.get_reg(inst.rs1 as usize) == machine.fp_reg.get_reg(inst.rs2 as usize) {1} else {0});
|
||||
machine.int_reg.set_reg(inst.rd as usize, (machine.fp_reg.get_reg(inst.rs1 as usize) == machine.fp_reg.get_reg(inst.rs2 as usize)) as i64);
|
||||
},
|
||||
RISCV_FP_FCMP_FLT => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, if machine.fp_reg.get_reg(inst.rs1 as usize) < machine.fp_reg.get_reg(inst.rs2 as usize) {1} else {0});
|
||||
machine.int_reg.set_reg(inst.rd as usize, (machine.fp_reg.get_reg(inst.rs1 as usize) < machine.fp_reg.get_reg(inst.rs2 as usize)) as i64);
|
||||
},
|
||||
RISCV_FP_FCMP_FLE => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, if machine.fp_reg.get_reg(inst.rs1 as usize) <= machine.fp_reg.get_reg(inst.rs2 as usize) {1} else {0});
|
||||
machine.int_reg.set_reg(inst.rd as usize, (machine.fp_reg.get_reg(inst.rs1 as usize) <= machine.fp_reg.get_reg(inst.rs2 as usize)) as i64);
|
||||
},
|
||||
_ => {
|
||||
panic!("this instruction ({}) doesn't exists", inst.value);
|
||||
@ -609,21 +675,62 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => { panic!("{} opcode non géré", inst.opcode)},
|
||||
RISCV_SYSTEM => {
|
||||
// temporary return value to stop the loop of run
|
||||
// before we can use system call
|
||||
return 1;
|
||||
}
|
||||
_ => { panic!("{:x} opcode non géré pc : {:x}", inst.opcode, machine.pc)},
|
||||
}
|
||||
|
||||
machine.pc += 8;
|
||||
0
|
||||
|
||||
}
|
||||
|
||||
/// print memory FOR DEBUG
|
||||
///
|
||||
/// "@"adresse [16 bytes]
|
||||
pub fn _print_memory(machine : &mut Machine, from: usize, to: usize) {
|
||||
for i in from..to {
|
||||
if i%16 == 0 {
|
||||
print!("\n@{:04x} ", i);
|
||||
}
|
||||
print!("{:02x}", machine.main_memory[i]);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
pub fn read_int_register(&self, index: usize) -> i64 {
|
||||
self.int_reg.get_reg(index)
|
||||
}
|
||||
|
||||
pub fn read_fp_register(&self, index: usize) -> f32 {
|
||||
self.fp_reg.get_reg(index)
|
||||
}
|
||||
|
||||
pub fn write_int_register(&mut self, index: usize, value: i64) {
|
||||
self.int_reg.set_reg(index, value);
|
||||
}
|
||||
|
||||
pub fn write_fp_register(&mut self, index: usize, value: f32) {
|
||||
self.fp_reg.set_reg(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::simulator::machine::Machine;
|
||||
mod test {
|
||||
use std::fs;
|
||||
|
||||
use crate::simulator::{machine::Machine, mem_cmp};
|
||||
|
||||
#[test]
|
||||
fn test_init_machine() {
|
||||
let _ = Machine::init_machine();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_memory() {
|
||||
let mut m = Machine::_init_machine();
|
||||
let mut m = Machine::init_machine();
|
||||
m.main_memory[4] = 43;
|
||||
m.main_memory[5] = 150;
|
||||
assert_eq!((43 << 8) + 150, Machine::read_memory(&mut m, 2, 4));
|
||||
@ -631,9 +738,126 @@ impl Machine {
|
||||
|
||||
#[test]
|
||||
fn test_write_memory() {
|
||||
let mut m = Machine::_init_machine();
|
||||
let mut m = Machine::init_machine();
|
||||
Machine::write_memory(&mut m, 2, 6, (43 << 8) + 150);
|
||||
assert_eq!(43, m.main_memory[6]);
|
||||
assert_eq!(150, m.main_memory[7]);
|
||||
Machine::write_memory(&mut m, 4, 8, (52 << 24) + (20 << 16) + (43 << 8) + 150);
|
||||
assert_eq!(52, m.main_memory[8]);
|
||||
assert_eq!(20, m.main_memory[9]);
|
||||
assert_eq!(43, m.main_memory[10]);
|
||||
assert_eq!(150, m.main_memory[11]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_comp() {
|
||||
let mut m = Machine::init_machine();
|
||||
let memory_before = mem_cmp::MemChecker::from("test/machine/memoryComp.txt").unwrap();
|
||||
let memory_after = mem_cmp::MemChecker::from("test/machine/memoryCompEnd.txt").unwrap();
|
||||
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||
Machine::run(&mut m);
|
||||
|
||||
let expected_trace = fs::read_to_string("test/machine/memoryCompTrace.txt").unwrap();
|
||||
|
||||
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div() {
|
||||
let mut m = Machine::init_machine();
|
||||
let memory_before = mem_cmp::MemChecker::from("test/machine/memoryDiv.txt").unwrap();
|
||||
let memory_after = mem_cmp::MemChecker::from("test/machine/memoryDivEnd.txt").unwrap();
|
||||
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||
Machine::run(&mut m);
|
||||
|
||||
let expected_trace = fs::read_to_string("test/machine/memoryDivTrace.txt").unwrap();
|
||||
|
||||
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_if() {
|
||||
let mut m = Machine::init_machine();
|
||||
let memory_before = mem_cmp::MemChecker::from("test/machine/memoryIf.txt").unwrap();
|
||||
let memory_after = mem_cmp::MemChecker::from("test/machine/memoryIfEnd.txt").unwrap();
|
||||
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||
Machine::run(&mut m);
|
||||
|
||||
let expected_trace = fs::read_to_string("test/machine/memoryIfTrace.txt").unwrap();
|
||||
|
||||
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_jump() {
|
||||
let mut m = Machine::init_machine();
|
||||
let memory_before = mem_cmp::MemChecker::from("test/machine/memoryJump.txt").unwrap();
|
||||
let memory_after = mem_cmp::MemChecker::from("test/machine/memoryJumpEnd.txt").unwrap();
|
||||
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||
Machine::run(&mut m);
|
||||
|
||||
let expected_trace = fs::read_to_string("test/machine/memoryJumpTrace.txt").unwrap();
|
||||
|
||||
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul() {
|
||||
let mut m = Machine::init_machine();
|
||||
let memory_before = mem_cmp::MemChecker::from("test/machine/memoryMul.txt").unwrap();
|
||||
let memory_after = mem_cmp::MemChecker::from("test/machine/memoryMulEnd.txt").unwrap();
|
||||
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||
Machine::run(&mut m);
|
||||
|
||||
let expected_trace = fs::read_to_string("test/machine/memoryMulTrace.txt").unwrap();
|
||||
|
||||
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ret() {
|
||||
let mut m = Machine::init_machine();
|
||||
let memory_before = mem_cmp::MemChecker::from("test/machine/memoryRet.txt").unwrap();
|
||||
let memory_after = mem_cmp::MemChecker::from("test/machine/memoryRetEnd.txt").unwrap();
|
||||
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||
Machine::run(&mut m);
|
||||
|
||||
let expected_trace = fs::read_to_string("test/machine/memoryRetTrace.txt").unwrap();
|
||||
|
||||
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sub() {
|
||||
let mut m = Machine::init_machine();
|
||||
let memory_before = mem_cmp::MemChecker::from("test/machine/memorySub.txt").unwrap();
|
||||
let memory_after = mem_cmp::MemChecker::from("test/machine/memorySubEnd.txt").unwrap();
|
||||
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||
Machine::run(&mut m);
|
||||
|
||||
let expected_trace = fs::read_to_string("test/machine/memorySubTrace.txt").unwrap();
|
||||
|
||||
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_switch() {
|
||||
let mut m = Machine::init_machine();
|
||||
let memory_before = mem_cmp::MemChecker::from("test/machine/memorySwitch.txt").unwrap();
|
||||
let memory_after = mem_cmp::MemChecker::from("test/machine/memorySwitchEnd.txt").unwrap();
|
||||
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||
Machine::run(&mut m);
|
||||
|
||||
let expected_trace = fs::read_to_string("test/machine/memorySwitchTrace.txt").unwrap();
|
||||
|
||||
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,9 @@
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::BufRead;
|
||||
use std::{fs, io::{BufRead, BufReader, Lines, Error}};
|
||||
use crate::Machine;
|
||||
|
||||
const MEM_SIZE : usize = 4096;
|
||||
|
||||
|
||||
|
||||
/* TRUCS MANQUANTS
|
||||
* Verifier qu'il y a un nombre pair de caractere hexa dans la ligne correspondante d'une section du fichier source
|
||||
* Sinon on ne peut pas automatiquement remplir chaque octect car 2 hexa = 1 octet
|
||||
@ -30,14 +27,14 @@ const MEM_SIZE : usize = 4096;
|
||||
//content est une suite hexadécimale
|
||||
|
||||
//Section dans le fichier, champ String car informations proviennent d'un fichier txt
|
||||
struct SectionFormat{
|
||||
pub struct SectionFormat{
|
||||
addr: String,
|
||||
len: String,
|
||||
content: String,
|
||||
}
|
||||
|
||||
//Section dans le programme
|
||||
struct Section{
|
||||
pub struct Section{
|
||||
addr: usize, // adresse dans la mémoire
|
||||
len: usize, // nombre d'octets de la donnée à addr
|
||||
content: Vec<u8>, // la donnée en question
|
||||
@ -55,7 +52,6 @@ impl Section{
|
||||
let len: usize = string_hex_to_usize(§ion.len);
|
||||
|
||||
let mut tmp_a: char = ' ';
|
||||
let mut tmp_b: char = ' ';
|
||||
|
||||
for (i, c) in section.content.chars().enumerate(){
|
||||
|
||||
@ -63,18 +59,17 @@ impl Section{
|
||||
tmp_a = c;
|
||||
}
|
||||
else {
|
||||
tmp_b = c;
|
||||
content.push(two_hex_to_u8(tmp_a,tmp_b));
|
||||
content.push(two_hex_to_u8(tmp_a,c));
|
||||
}
|
||||
}
|
||||
|
||||
Section{addr:addr, len:len, content:content}
|
||||
Section{addr, len, content}
|
||||
}
|
||||
|
||||
|
||||
fn print_Section(s: &Section){
|
||||
println!("ADDR :: {}", s.addr);
|
||||
println!("LEN :: {}", s.len);
|
||||
fn print_section(s: &Section){
|
||||
println!("ADDR :: {:x}", s.addr);
|
||||
println!("LEN :: {:x}", s.len);
|
||||
println!("CONTENT :: {:?}", s.content);
|
||||
}
|
||||
}
|
||||
@ -82,42 +77,64 @@ impl Section{
|
||||
/*
|
||||
* Representation de l'etat de la mémoire (apres execution.... a confirmer), sous forme de sections
|
||||
*/
|
||||
struct Mem_Checker{
|
||||
pub struct MemChecker{
|
||||
pc: usize,
|
||||
sp: usize,
|
||||
sections: Vec<Section>,
|
||||
}
|
||||
|
||||
|
||||
impl Mem_Checker{
|
||||
impl MemChecker{
|
||||
|
||||
fn from(path: &String) -> Mem_Checker {
|
||||
///Translate lines of a file in e Vector of String
|
||||
///We need this method to parse the memory we received
|
||||
///
|
||||
/// ### Parameters
|
||||
///
|
||||
/// - **Lines** The file to parse
|
||||
///
|
||||
/// ### Return
|
||||
/// - A vector of String where each line of the file os an element of the vector
|
||||
fn vect_from_lines(lines: &mut Lines<BufReader<fs::File>>, pc: &mut usize, sp: &mut usize) -> Vec<String>{
|
||||
let mut vector = Vec::new();
|
||||
for (_,line) in lines.enumerate() {
|
||||
vector.push(line.unwrap());
|
||||
}
|
||||
let size = vector.len();
|
||||
*pc = string_hex_to_usize(vector.get(size - 2).expect("0"));
|
||||
*sp = string_hex_to_usize(vector.get(size - 1).expect("0"));
|
||||
vector
|
||||
}
|
||||
|
||||
let file = fs::File::open("test_file_section.txt").expect("Wrong filename");
|
||||
let reader = io::BufReader::new(file);
|
||||
/// Fill a mem checker from a file (here the mock memory)
|
||||
/// Extract the values of pc, sp and sections
|
||||
///
|
||||
/// ### Parameter
|
||||
/// -**path** addr to the file
|
||||
///
|
||||
/// ### Return
|
||||
/// Mem-checker filled
|
||||
pub fn from(path: &str) -> Result<MemChecker, Error> {
|
||||
|
||||
let file = fs::File::open(path)?;
|
||||
|
||||
let reader = BufReader::new(file);
|
||||
let mut lines = reader.lines();
|
||||
|
||||
let mut pc: usize = 0;
|
||||
let mut sp: usize = 0;
|
||||
let mut sections: Vec<Section> = Vec::new();
|
||||
let vector = MemChecker::vect_from_lines(&mut lines, &mut pc, &mut sp);
|
||||
|
||||
let mut sections: Vec<Section> = Vec::new();
|
||||
let mut tmp_addr_str: String = String::new();
|
||||
let mut tmp_len_str: String = String::new();
|
||||
|
||||
for (i,line) in reader.lines().enumerate() {
|
||||
let default = String::new();
|
||||
for i in 0..vector.len()-2 {
|
||||
let current_line = vector.get(i).unwrap_or(&default);
|
||||
|
||||
let current_line = line.unwrap();
|
||||
|
||||
if i == current_line.len()-2 {
|
||||
//Lecture de PC
|
||||
pc = string_hex_to_usize(¤t_line);
|
||||
}
|
||||
else if i == current_line.len()-1 {
|
||||
//Lecture SP
|
||||
sp = string_hex_to_usize(¤t_line);
|
||||
}
|
||||
else {
|
||||
//Lecture des sections
|
||||
if current_line.contains(' ') {
|
||||
if i % 2 == 0 {
|
||||
//lecture ligne ADDR LEN
|
||||
let next_word_index = current_line.find(' ').unwrap();
|
||||
tmp_addr_str = String::from(¤t_line[0..next_word_index]);
|
||||
@ -128,32 +145,43 @@ impl Mem_Checker{
|
||||
let section_f = SectionFormat{
|
||||
addr: tmp_addr_str.clone(),
|
||||
len: tmp_len_str.clone(),
|
||||
content: current_line,
|
||||
content: current_line.clone().replace(' ', ""),
|
||||
};
|
||||
sections.push(Section::from(§ion_f));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Mem_Checker{pc:pc, sp:sp, sections:sections}
|
||||
Ok(MemChecker{pc, sp, sections})
|
||||
}
|
||||
|
||||
|
||||
fn print_Mem_Checker(m_c: &Mem_Checker){
|
||||
println!("PC :: {}", m_c.pc);
|
||||
println!("SP :: {}", m_c.sp);
|
||||
/// Print the content of a Mem_Checker
|
||||
///
|
||||
/// ### Parameter
|
||||
///
|
||||
/// - **m_c** Contains the data we want to print
|
||||
pub fn print_mem_checker(m_c: &MemChecker){
|
||||
println!("PC :: {:x}", m_c.pc);
|
||||
println!("SP :: {:x}", m_c.sp);
|
||||
|
||||
for(i,s) in m_c.sections.iter().enumerate() {
|
||||
println!("\nSection {}\n", i);
|
||||
Section::print_Section(&s);
|
||||
Section::print_section(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn fill_memory_from_Mem_Checker(m_c: &Mem_Checker, machine: &mut Machine){
|
||||
/// Fill a machine's memory from a Mem Chacker
|
||||
///
|
||||
/// ### Parameters
|
||||
///
|
||||
/// - **m_c** contains the data
|
||||
/// - **machine** contains the memry to fill
|
||||
pub fn fill_memory_from_mem_checker(m_c: &MemChecker, machine: &mut Machine){
|
||||
|
||||
machine.sp = m_c.sp;
|
||||
machine.int_reg.set_reg(2, m_c.sp as i64);
|
||||
machine.pc = m_c.pc as u64;
|
||||
|
||||
|
||||
@ -169,9 +197,9 @@ impl Mem_Checker{
|
||||
/*
|
||||
* FOR DEBUG
|
||||
*/
|
||||
fn compare_print_m_c_machine(m_c: &Mem_Checker, machine: &mut Machine){
|
||||
fn compare_print_m_c_machine(m_c: &MemChecker, machine: &mut Machine){
|
||||
|
||||
Mem_Checker::print_Mem_Checker(m_c);
|
||||
MemChecker::print_mem_checker(m_c);
|
||||
|
||||
for section in m_c.sections.iter() {
|
||||
|
||||
@ -187,12 +215,27 @@ impl Mem_Checker{
|
||||
|
||||
}
|
||||
|
||||
/// Compare sections of a memChecker and a machine memory
|
||||
///
|
||||
/// ### Parameters
|
||||
///
|
||||
/// - **m_c** contains section of the memory checker
|
||||
/// - **machine** contains the main memory
|
||||
pub fn compare_machine_memory(m_c: &MemChecker, machine: &Machine) -> bool {
|
||||
m_c.sections.iter().map(|section| {
|
||||
(0..section.len).into_iter().all(|i| machine.main_memory[section.addr + i] == section.content[i])
|
||||
}).all(|e| e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn string_hex_to_usize(s: &String) -> usize{
|
||||
fn string_hex_to_usize(s: &String) -> usize {
|
||||
|
||||
if s.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let max_pow = (s.len()-1) as u32;
|
||||
let mut ret_value: usize = 0;
|
||||
@ -200,11 +243,11 @@ fn string_hex_to_usize(s: &String) -> usize{
|
||||
|
||||
for (i,c )in s.chars().enumerate(){
|
||||
//println!("Current char :: {} :: Current pow :: {} ::", c, max_pow - (i as u32));
|
||||
let tmp: usize = (one_hex_to_dec(c) as usize);
|
||||
let tmp: usize = one_hex_to_dec(c) as usize;
|
||||
ret_value += base.pow(max_pow - (i as u32))*tmp;
|
||||
}
|
||||
|
||||
return ret_value;
|
||||
ret_value
|
||||
}
|
||||
|
||||
|
||||
@ -222,11 +265,10 @@ fn one_hex_to_dec(c: char) -> u8 {
|
||||
'E' | 'e' => 14,
|
||||
'F' | 'f' => 15,
|
||||
_ => {
|
||||
let ret : u8 = c.to_digit(10).unwrap() as u8;
|
||||
return ret;
|
||||
c.to_digit(10).unwrap() as u8
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -245,7 +287,7 @@ fn two_hex_to_u8(c1: char, c2: char) -> u8 {
|
||||
*/
|
||||
fn test_show_sections_file(){
|
||||
let file = fs::File::open("test_file_section.txt").expect("Wrong filename");
|
||||
let reader = io::BufReader::new(file);
|
||||
let reader = BufReader::new(file);
|
||||
|
||||
for line in reader.lines() {
|
||||
//println!("Tailles de la ligne : {}",
|
||||
@ -266,18 +308,17 @@ mod tests {
|
||||
#[test]
|
||||
fn test_fill_memory(){
|
||||
let path = "osef".to_string();
|
||||
let m_c = Mem_Checker::from(&path);
|
||||
let mut machine = Machine::_init_machine();
|
||||
let m_c = MemChecker::from(&path).unwrap();
|
||||
let mut machine = Machine::init_machine();
|
||||
|
||||
Mem_Checker::fill_memory_from_Mem_Checker(&m_c, &mut machine);
|
||||
MemChecker::fill_memory_from_mem_checker(&m_c, &mut machine);
|
||||
|
||||
print!("\n Comparing memory from loaded context\n\n");
|
||||
|
||||
Mem_Checker::compare_print_m_c_machine(&m_c, &mut machine);
|
||||
MemChecker::compare_print_m_c_machine(&m_c, &mut machine);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_enum_start_at_zero(){
|
||||
let v = vec![1,2,3];
|
||||
@ -288,10 +329,10 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_Mem_Checker(){
|
||||
fn test_create_mem_checker(){
|
||||
let path: String = "osef".to_string();
|
||||
let m_c = Mem_Checker::from(&path);
|
||||
Mem_Checker::print_Mem_Checker(&m_c);
|
||||
let m_c = MemChecker::from(&path).unwrap();
|
||||
MemChecker::print_mem_checker(&m_c);
|
||||
}
|
||||
|
||||
|
||||
@ -320,12 +361,7 @@ mod tests {
|
||||
};
|
||||
|
||||
let section = Section::from(§ion_format);
|
||||
let mut expected_vec: Vec<u8> = Vec::new();
|
||||
expected_vec.push(0u8);
|
||||
expected_vec.push(255u8);
|
||||
expected_vec.push(10u8);
|
||||
expected_vec.push(160u8);
|
||||
expected_vec.push(165u8);
|
||||
let expected_vec: Vec<u8> = vec![0u8, 255u8, 10u8, 160u8, 165u8];
|
||||
|
||||
//println!("Vec from created section {:?}", §ion.content);
|
||||
//println!("Expected vec {:?}", &expected_vec);
|
||||
@ -333,18 +369,6 @@ mod tests {
|
||||
assert_eq!(section.content, expected_vec);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mod(){
|
||||
let cond = (0%2) == 0;
|
||||
assert_eq!(true, cond);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mod_2(){
|
||||
let cond = (1%2) == 1;
|
||||
assert_eq!(true, cond);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hex_1(){
|
||||
let b = two_hex_to_u8('0', '0');
|
||||
|
0
src/simulator/mmu.rs
Normal file
@ -2,6 +2,7 @@ pub mod machine;
|
||||
pub mod decode;
|
||||
pub mod print;
|
||||
pub mod mem_cmp;
|
||||
pub mod loader;
|
||||
pub mod interrupt;
|
||||
|
||||
pub mod global {
|
||||
|
@ -13,7 +13,7 @@ const NAMES_OPIW: [&str; 8] = ["addiw", "slliw", "", "", "", "sri", "", ""];
|
||||
|
||||
|
||||
// Register name mapping
|
||||
const REG_X: [&str; 32] = ["zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1",
|
||||
pub const REG_X: [&str; 32] = ["zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1",
|
||||
"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
|
||||
"s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11",
|
||||
"t3", "t4", "t5", "t6"];
|
||||
@ -78,7 +78,7 @@ pub fn print(ins: Instruction, pc: i32) -> String { //TODO pc should be u64
|
||||
format!("jal\t{},{:x}", REG_X[rd], (pc + ins.imm21_1_signed))
|
||||
},
|
||||
RISCV_JALR => {
|
||||
format!("jalr\t{},{}({})", REG_X[rd], ins.imm12_I_signed, REG_X[rs1])
|
||||
format!("jalr\t{},{:x}({})", REG_X[rd], ins.imm12_I_signed, REG_X[rs1])
|
||||
},
|
||||
RISCV_BR => {
|
||||
format!("{}\t{},{},{:x}", NAMES_BR[ins.funct3 as usize], REG_X[rs1], REG_X[rs2], pc + (ins.imm13_signed as i32))
|
||||
@ -139,7 +139,6 @@ pub fn print(ins: Instruction, pc: i32) -> String { //TODO pc should be u64
|
||||
format!("fnmadd\t{}{}{}{}", REG_F[rd], REG_F[rs1], REG_F[rs2], REG_F[rs3])
|
||||
},
|
||||
RISCV_FP => {
|
||||
let name: &str;
|
||||
match ins.funct7 {
|
||||
RISCV_FP_ADD => {
|
||||
format!("{}\t{}{}{}", "fadd", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
@ -217,7 +216,7 @@ pub fn print(ins: Instruction, pc: i32) -> String { //TODO pc should be u64
|
||||
RISCV_SYSTEM => {
|
||||
"ecall".to_string()
|
||||
},
|
||||
_ => todo!("Unknown or currently unsupported opcode") // Change todo! to panic! in the future, I put todo! because there's a lot of opcode currently not implemented
|
||||
_ => todo!("{:x} opcode non géré pc : {:x}, value : {:x}", ins.opcode, pc, ins.value) // Change todo! to panic! in the future, I put todo! because there's a lot of opcode currently not implemented
|
||||
}
|
||||
|
||||
}
|
||||
|
78
src/simulator/translationtable.rs
Normal file
@ -0,0 +1,78 @@
|
||||
//Nombre maximum de correspondances dans une table des pages
|
||||
//Cette donnée devra a terme etre recupérée depuis un fichier de configuration
|
||||
const MaxVirtPages : u64 = 200000;
|
||||
|
||||
|
||||
/* Une table de correspondance propre à un processus
|
||||
*/
|
||||
struct TranslationTable{
|
||||
//capacité de cette table <=> nombre de correspondances possibles
|
||||
maxNumPages : u64,
|
||||
|
||||
//la table en question
|
||||
//Vec implemente le trait Index, donc un bon choix
|
||||
pageTable : Vec<PageTableEntry>
|
||||
}
|
||||
|
||||
impl TranslationTable {
|
||||
|
||||
fn create() -> TranslationTable {
|
||||
|
||||
TranslationTable{
|
||||
maxNumPages : MaxVirtPages,
|
||||
page
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Une correspondance + données sur cette correspondance
|
||||
*/
|
||||
struct PageTableEntry{
|
||||
//true <=> la correspondance est valide et la page est présente dans la ram
|
||||
valid : bool,
|
||||
|
||||
//true <=> la page a été accédée (lecture/ecriture) récemment
|
||||
U : bool,
|
||||
|
||||
//true <=> page modifiée mais non sauvegardée sur disque
|
||||
M : bool,
|
||||
|
||||
//droits d'accès sur cette page
|
||||
readAllowed : bool,
|
||||
writeAllowed : bool,
|
||||
|
||||
//numero de page physique <=> c'est notre correspondance
|
||||
physicalPage : i32,
|
||||
|
||||
//true <=> cette page doit etre chargée depuis la swap zone du disque
|
||||
swap : bool,
|
||||
|
||||
//a définir plus tard, en relation avec swap
|
||||
addrDisk : i32,
|
||||
|
||||
//mis à 1 par le système quand cette page est impliquée dans une opération d'IO
|
||||
io : bool
|
||||
}
|
||||
|
||||
impl PageTableEntry{
|
||||
|
||||
//Default PageTableEntry Constructor
|
||||
fn create() -> PageTableEntry {
|
||||
PageTableEntry {
|
||||
valid : false,
|
||||
U : false,
|
||||
M : false,
|
||||
readAllowed : false,
|
||||
writeAllowed : false,
|
||||
physicalPage : -1i32,
|
||||
swap : false,
|
||||
addrDisk : -1i32,
|
||||
io : false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,16 +1,24 @@
|
||||
|
||||
/// Data structure and definition of a genericsingle-linked LIFO list.
|
||||
///
|
||||
/// This is a
|
||||
#[derive(PartialEq)]
|
||||
pub struct List<T: PartialEq> {
|
||||
head: Link<T>,
|
||||
}
|
||||
|
||||
|
||||
type Link<T> = Option<Box<Node<T>>>;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct Node<T> {
|
||||
elem: T,
|
||||
next: Link<T>,
|
||||
}
|
||||
|
||||
impl<T: PartialEq> List<T> {
|
||||
|
||||
/// Create an empty list
|
||||
pub fn new() -> Self {
|
||||
List { head: None }
|
||||
}
|
||||
@ -69,14 +77,49 @@ impl<T: PartialEq> List<T> {
|
||||
false
|
||||
}
|
||||
|
||||
/// Remove the item from the list
|
||||
///
|
||||
/// Return true if the item has been found, otherwise return false
|
||||
///
|
||||
/// Worst-case complexity is O(n)
|
||||
pub fn remove(&mut self, item: T)-> bool {
|
||||
let mut found = false;
|
||||
let mut tmp_list: List<T> = List::new();
|
||||
while !self.is_empty() {
|
||||
let current = self.pop().unwrap();
|
||||
if current != item {
|
||||
tmp_list.push(current);
|
||||
} else {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while !tmp_list.is_empty() {
|
||||
self.push(tmp_list.pop().unwrap());
|
||||
}
|
||||
found
|
||||
}
|
||||
|
||||
/// Return true if the list is empty, false otherwise
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.head.is_none()
|
||||
}
|
||||
|
||||
/// Turn the list into an iterator for use in a for loop per example.
|
||||
///
|
||||
/// When you iter using into_iter, elements are remove from the list
|
||||
pub fn into_iter(self) -> IntoIter<T> {
|
||||
IntoIter(self)
|
||||
}
|
||||
|
||||
/// Turn the list into an iterator for use in a for loop
|
||||
///
|
||||
/// When you iter using this method, elements are dereferenced
|
||||
pub fn iter(&self) -> Iter<'_, T> {
|
||||
Iter { next: self.head.as_deref() }
|
||||
}
|
||||
|
||||
/// Same as iter but make the iterator mutable
|
||||
pub fn iter_mut(&mut self) -> IterMut<'_, T> {
|
||||
IterMut { next: self.head.as_deref_mut() }
|
||||
}
|
||||
@ -91,6 +134,7 @@ impl<T: PartialEq> Drop for List<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator structure for use in a for loop, pop elements before returning it
|
||||
pub struct IntoIter<T: PartialEq>(List<T>);
|
||||
|
||||
impl<T: PartialEq> Iterator for IntoIter<T> {
|
||||
@ -101,6 +145,7 @@ impl<T: PartialEq> Iterator for IntoIter<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator structure for use in a for loop, dereference before returning it
|
||||
pub struct Iter<'a, T> {
|
||||
next: Option<&'a Node<T>>,
|
||||
}
|
||||
@ -115,6 +160,7 @@ impl<'a, T> Iterator for Iter<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as Iter structure, returned item are mutable
|
||||
pub struct IterMut<'a, T> {
|
||||
next: Option<&'a mut Node<T>>,
|
||||
}
|
||||
|
4
test/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# Ignoring dump files
|
||||
*.dump
|
||||
*.o
|
||||
target
|
21
test/Makefile
Normal file
@ -0,0 +1,21 @@
|
||||
TOPDIR=.
|
||||
include $(TOPDIR)/Makefile.config
|
||||
|
||||
#
|
||||
# Main targets
|
||||
#
|
||||
dumps:
|
||||
$(MAKE) dumps -C riscv_instructions/
|
||||
mkdir -p ${TOPDIR}/target/dumps/
|
||||
find . -name '*.dump' -exec mv {} ${TOPDIR}/target/dumps/ \;
|
||||
|
||||
user_lib:
|
||||
$(MAKE) -C userlib/
|
||||
|
||||
tests: user_lib
|
||||
$(MAKE) tests -C riscv_instructions/
|
||||
mkdir -p ${TOPDIR}/target/guac/
|
||||
find . -name '*.guac' -exec mv {} ${TOPDIR}/target/guac/ \;
|
||||
|
||||
clean:
|
||||
rm -rf $(TOPDIR)/target
|
@ -11,7 +11,8 @@ RISCV_PREFIX=/opt/riscv/bin/
|
||||
RISCV_AS = $(RISCV_PREFIX)riscv64-unknown-elf-gcc -x assembler-with-cpp -march=rv64imfd
|
||||
RISCV_GCC = $(RISCV_PREFIX)riscv64-unknown-elf-gcc
|
||||
RISCV_LD = $(RISCV_PREFIX)riscv64-unknown-elf-ld
|
||||
RISCV_OBJDUMP = $(RISCV_PREFIX)riscv64-unknown-elf-objdump
|
||||
RISCV_OBJCOPY = $(RISCV_PREFIX)riscv64-unknown-elf-objcopy
|
||||
DUMP_FORMAT = ihex
|
||||
RISCV_ASFLAGS = $(RISCV_CPPFLAGS)
|
||||
RISCV_CPPFLAGS = #nil
|
||||
RISCV_CFLAGS = -Wall $(RISCV_CPPFLAGS) -march=rv64imfd
|
38
test/Makefile.tests
Normal file
@ -0,0 +1,38 @@
|
||||
include $(TOPDIR)/Makefile.config
|
||||
USERLIB = $(TOPDIR)/userlib
|
||||
INCPATH += -I$(TOPDIR) -I$(USERLIB)
|
||||
LDFLAGS = $(RISCV_LDFLAGS) -T $(USERLIB)/ldscript.lds
|
||||
ASFLAGS = $(RISCV_ASFLAGS) $(INCPATH)
|
||||
CFLAGS = $(RISCV_CFLAGS) $(INCPATH)
|
||||
|
||||
# Rules
|
||||
%.o: %.s
|
||||
$(RISCV_AS) $(ASFLAGS) -c $<
|
||||
|
||||
%.o: %.c
|
||||
$(RISCV_GCC) $(CFLAGS) -c $<
|
||||
|
||||
%.dump: %.o
|
||||
$(RISCV_OBJCOPY) -j .text -O $(DUMP_FORMAT) $< $@
|
||||
|
||||
%.guac: %.o
|
||||
$(RISCV_LD) $(LDFLAGS) $+ -o $@
|
||||
|
||||
# Dependencies
|
||||
.%.d: %.s
|
||||
@echo Generating dependencies for $<
|
||||
@$(SHELL) -ec '$(GCC) -x assembler-with-cpp -M $(ASFLAGS) $< \
|
||||
| sed '\''s/\($*\)\.o[ :]*/\1.o $@ : /g'\'' > $@; \
|
||||
[ -s $@ ] || rm -f $@'
|
||||
|
||||
.%.d: %.c
|
||||
@echo Generating dependencies for $<
|
||||
@$(SHELL) -ec '$(GCC) -M $(CFLAGS) $< \
|
||||
| sed '\''s/\($*\)\.o[ :]*/\1.o $@ : /g'\'' > $@; \
|
||||
[ -s $@ ] || rm -f $@'
|
||||
|
||||
# Targets
|
||||
#clean:
|
||||
# rm -rf *.o 2> /dev/null
|
||||
# rm -rf *.dump 2> /dev/null
|
||||
# rm -rf *.guac 2> /dev/null
|
@ -11,7 +11,7 @@ Set the variables to the correct paths for the [RISCV Newlib compilation toolcha
|
||||
### Exporting objdumps
|
||||
|
||||
```
|
||||
$ make objdumps
|
||||
$ make dumps
|
||||
```
|
||||
|
||||
### Compiling programs
|
8
test/machine/memory.txt
Normal file
8
test/machine/memoryComp.txt
Normal file
8
test/machine/memoryDiv.txt
Normal file
8
test/machine/memoryIf.txt
Normal file
8
test/machine/memoryJump.txt
Normal file
8
test/machine/memoryMul.txt
Normal file
8
test/machine/memoryRet.txt
Normal file
8
test/machine/memorySub.txt
Normal file
8
test/machine/memorySwitch.txt
Normal file
9
test/riscv_instructions/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
dumps:
|
||||
make dumps -C boolean_logic/
|
||||
make dumps -C jump_instructions/
|
||||
make dumps -C simple_arithmetics/
|
||||
|
||||
tests:
|
||||
make tests -C boolean_logic/
|
||||
make tests -C jump_instructions/
|
||||
make tests -C simple_arithmetics/
|
9
test/riscv_instructions/boolean_logic/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
TOPDIR = ../..
|
||||
include $(TOPDIR)/Makefile.tests
|
||||
|
||||
dumps: comparisons.dump if.dump switch.dump
|
||||
|
||||
tests: comparisons.guac if.guac switch.guac
|
||||
|
||||
# Dependances
|
||||
$(PROGRAMS): % : $(USERLIB)/sys.o $(USERLIB)/libnachos.o %.o
|
@ -9,7 +9,7 @@ int main() {
|
||||
} else if (x < y) {
|
||||
y += 1;
|
||||
} else {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
17
test/riscv_instructions/boolean_logic/new_comparisons.c
Normal file
@ -0,0 +1,17 @@
|
||||
int main() {
|
||||
int x = 0;
|
||||
int y = 1;
|
||||
while (x <= y) {
|
||||
if (x > y) {
|
||||
y += 1;
|
||||
}
|
||||
if (x == y) {
|
||||
x += y;
|
||||
}
|
||||
if (x < y) {
|
||||
x += 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
int main() {
|
||||
int x = 0;
|
||||
switch(1) {
|
||||
switch(x) {
|
||||
case 1: x = 1; break;
|
||||
default: return;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
6
test/riscv_instructions/jump_instructions/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
TOPDIR = ../..
|
||||
include $(TOPDIR)/Makefile.tests
|
||||
|
||||
dumps: jump.dump ret.dump
|
||||
|
||||
tests: jump.guac ret.guac
|
@ -1,7 +1,8 @@
|
||||
int test() {
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int x = test();
|
||||
return x;
|
||||
}
|
6
test/riscv_instructions/simple_arithmetics/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
TOPDIR = ../..
|
||||
include $(TOPDIR)/Makefile.tests
|
||||
|
||||
dumps: unsigned_addition.dump unsigned_division.dump unsigned_multiplication.dump unsigned_substraction.dump
|
||||
|
||||
tests: unsigned_addition.guac unsigned_division.guac unsigned_multiplication.guac unsigned_substraction.guac
|
@ -1,5 +1,8 @@
|
||||
#include "userlib/syscall.h"
|
||||
#include "userlib/libnachos.h"
|
||||
|
||||
// EXPECTS TWO VARIABLES WITH A VALUE OF UNSIGNED 1
|
||||
void main() {
|
||||
int main() {
|
||||
unsigned int x = 0;
|
||||
unsigned int y = 1;
|
||||
x = x + y;
|
@ -1,5 +1,5 @@
|
||||
// Expecting two variables with a value of two
|
||||
void main() {
|
||||
int main() {
|
||||
unsigned int x = 4;
|
||||
unsigned int y = 2;
|
||||
x = x / y;
|
@ -1,5 +1,5 @@
|
||||
// EXPECTS TWO VARIABLES WITH A VALUE OF UNSIGNED 2
|
||||
void main() {
|
||||
int main() {
|
||||
unsigned int x = 1;
|
||||
unsigned int y = 2;
|
||||
x = x * y;
|
@ -1,5 +1,5 @@
|
||||
// EXPECTS TWO VARIABLES WITH A VALUE OF UNSIGNED 1
|
||||
void main() {
|
||||
int main() {
|
||||
unsigned int x = 1;
|
||||
unsigned int y = 1;
|
||||
x = x - y;
|
4
test/userlib/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
TOPDIR = ../
|
||||
include $(TOPDIR)/Makefile.tests
|
||||
|
||||
default: sys.o libnachos.o
|
61
test/userlib/ldscript.lds
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
ldscript for running user programs under Nachos
|
||||
|
||||
Sections should be aligned on page boundaries. Here an alignement of
|
||||
at least 0x2000 is selected, thus supporting pages up to 8KB
|
||||
large. See addrspace.cc for details.
|
||||
*/
|
||||
|
||||
ENTRY(__start)
|
||||
SECTIONS
|
||||
{
|
||||
|
||||
/* Skip an area of about 8k, so that NULL pointer dereferences can
|
||||
be detected */
|
||||
. += 0x2000;
|
||||
|
||||
.sys ALIGN(0x4000) : {
|
||||
*(.init)
|
||||
*(.sys)
|
||||
}
|
||||
|
||||
/* Code is aligned on a 16k boundary
|
||||
Due to the size of the .sys section, the code start address will
|
||||
presumably be at address 0x4000 */
|
||||
.text ALIGN(0x400000) : {
|
||||
_ftext = .;
|
||||
eprol = .;
|
||||
*(.text)
|
||||
*(.fini)
|
||||
}
|
||||
etext = .;
|
||||
_etext = .;
|
||||
|
||||
/* Initialized data is aligned on a 16k boundary */
|
||||
.data ALIGN(0x4000) : {
|
||||
_fdata = .;
|
||||
*(.data)
|
||||
*(.sdata)
|
||||
}
|
||||
.rodata ALIGN(0x4000) :
|
||||
{
|
||||
*(.rdata)
|
||||
*(.srodata)
|
||||
*(.rodata)
|
||||
}
|
||||
edata = .;
|
||||
_edata = .;
|
||||
|
||||
/* Non-initialized data is aligned on a 16k boundary */
|
||||
/* Bss = Block Started by Symbol */
|
||||
.bss ALIGN(0x4000) : {
|
||||
*(.bss)
|
||||
*(.sbss)
|
||||
*(.scommon)
|
||||
*(COMMON)
|
||||
}
|
||||
|
||||
end = .;
|
||||
_end = .;
|
||||
|
||||
}
|
630
test/userlib/libnachos.c
Normal file
@ -0,0 +1,630 @@
|
||||
/*! \file libnachos.c
|
||||
* \brief Functions of our library, for user programs.
|
||||
*
|
||||
* This library only provides some usefull functions for
|
||||
* programming.
|
||||
*
|
||||
* -----------------------------------------------------
|
||||
* This file is part of the Nachos-RiscV distribution
|
||||
* Copyright (c) 2022 University of Rennes 1.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details
|
||||
* (see see <http://www.gnu.org/licenses/>).
|
||||
* -----------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "libnachos.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// threadStart()
|
||||
/*! Makes a thread execute a function or program. This function
|
||||
// is static, it is called internally by library function threadCreate
|
||||
// and should not be called directly by user programs. The interest
|
||||
// of this function is to be able to terminate threads correctly,
|
||||
// even when the thread to be terminated does not explicitly call
|
||||
// Exit. threadStart provides the mechanism by which Exit
|
||||
// is called automatically
|
||||
//
|
||||
// \param func is the identificator of the function to execute.
|
||||
*/
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static void threadStart(uint64_t func)
|
||||
{
|
||||
VoidNoArgFunctionPtr func2;
|
||||
func2=(VoidNoArgFunctionPtr)func;
|
||||
// Call the function that actually contains the thread code
|
||||
(*func2)();
|
||||
// Call exit, such that there is no return using an empty stack
|
||||
Exit(0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// threadCreate()
|
||||
/*! Creates a thread and makes it execute a function.
|
||||
//
|
||||
// NB : instead of directly executing the required function,
|
||||
// function threadStart is called so as to ensure
|
||||
// that the thread will properly exit
|
||||
// This function must be called instead of calling directly
|
||||
// the system call newThread
|
||||
//
|
||||
// \param name the name of the thread (for debugging purpose)
|
||||
// \param func is the address of the function to execute.
|
||||
*/
|
||||
//----------------------------------------------------------------------
|
||||
ThreadId threadCreate(char *debug_name, VoidNoArgFunctionPtr func)
|
||||
{
|
||||
return newThread(debug_name, (uint64_t)threadStart,(uint64_t)func);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// n_strcmp()
|
||||
/*! String comparison
|
||||
//
|
||||
// \param s1 is the first string,
|
||||
// \param s2 is the second one.
|
||||
// \return an integer greater than, equal to, or less than 0,
|
||||
// if the first string is greater than, equal to, or less than
|
||||
// the the second string.
|
||||
*/
|
||||
//----------------------------------------------------------------------
|
||||
int n_strcmp(const char *s1, const char *s2)
|
||||
{
|
||||
int comparaison;
|
||||
int fini=0;
|
||||
int i=0;
|
||||
while(!fini) {
|
||||
if ((s1[i]==0)&&(s2[i]==0)) {
|
||||
fini=1;
|
||||
comparaison=0;
|
||||
}
|
||||
if (s1[i]<s2[i]) {
|
||||
fini=1;
|
||||
comparaison=-1;
|
||||
}
|
||||
if(s1[i]>s2[i]) {
|
||||
fini=1;
|
||||
comparaison=1;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return comparaison;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// n_strcpy()
|
||||
/*! String copy
|
||||
//
|
||||
// \param dst is where the string is to be copied,
|
||||
// \param src is where the string to copy is.
|
||||
// \return dst, if the copy successes, 0 otherwise
|
||||
*/
|
||||
//----------------------------------------------------------------------
|
||||
char *n_strcpy(char *dst, const char *src)
|
||||
{
|
||||
int i=0;
|
||||
int fini=0;
|
||||
if ((dst!=0)&&(src!=0)) {
|
||||
while(fini==0) {
|
||||
if(src[i]=='\0') fini=1;
|
||||
dst[i]=src[i];
|
||||
i++;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// n_strlen()
|
||||
/*! Gives the number of bytes in a string, not including the
|
||||
// terminating null character.
|
||||
//
|
||||
// \param c is a pointer onto a string.
|
||||
// \return the length of the string.
|
||||
*/
|
||||
//----------------------------------------------------------------------
|
||||
size_t n_strlen(const char *s)
|
||||
{
|
||||
size_t i=0;
|
||||
while (s[i] != 0) i++;
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// n_strcat()
|
||||
/*! Appends a copy of a string, including null character, to the end
|
||||
// of another string. Enough memory has to be available in the
|
||||
// destination string.
|
||||
//
|
||||
// \param dst is a pointer onto the string where the other string
|
||||
// will be appended.
|
||||
// \param src is the string to append.
|
||||
// \return the pointer string dst.
|
||||
*/
|
||||
//----------------------------------------------------------------------
|
||||
char *n_strcat(char *dst, const char *src)
|
||||
{
|
||||
int i,j,k;
|
||||
i=(int)n_strlen(dst);
|
||||
j=(int)n_strlen(src);
|
||||
for(k=i;k<=j+i;k++) {
|
||||
dst[k]=src[k-i];
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// n_toupper()
|
||||
/*! Gives the upper-case letter corresponding to the lower-case
|
||||
// letter passed as parameter.
|
||||
//
|
||||
// \param c is the ASCII code of the letter to transform.
|
||||
// \return the corresponding upper-case letter
|
||||
*/
|
||||
//----------------------------------------------------------------------
|
||||
int n_toupper(int c)
|
||||
{
|
||||
if((c>='a')&&(c<='z'))
|
||||
return c+('A'-'a');
|
||||
else return c;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// n_tolower()
|
||||
/*! Gives the lower-case letter corresponding to the upper-case
|
||||
// letter passed as parameter
|
||||
//
|
||||
// \param c is the ASCII code of the letter to transform.
|
||||
// \return the corresponding lower-case letter
|
||||
*/
|
||||
//----------------------------------------------------------------------
|
||||
int n_tolower(int c)
|
||||
{
|
||||
if((c<='Z')&&(c>='A'))
|
||||
return c+('a'-'A');
|
||||
else return c;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// n_atoi()
|
||||
/*! String to integer conversion.
|
||||
//
|
||||
// \param c is a pointer onto a string.
|
||||
// \return the corresponding value
|
||||
*/
|
||||
//----------------------------------------------------------------------
|
||||
int n_atoi(const char *str)
|
||||
{
|
||||
int i=0;
|
||||
int fini=0;
|
||||
int val=0;
|
||||
int negative = 0;
|
||||
if (str[i] == '-') {
|
||||
negative = 1; i=1;
|
||||
}
|
||||
while(!fini)
|
||||
{
|
||||
if(str[i]==0 || str[i]<'0' || str[i]>'9')
|
||||
fini=1;
|
||||
else
|
||||
{
|
||||
val*=10;
|
||||
val+=str[i]-'0';
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (negative) return(-val); else return val;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// n_memcmp()
|
||||
/*! Memory comparison.
|
||||
//
|
||||
// \param s1 is the first memory area,
|
||||
// \param s2 is the second memory area.
|
||||
// \param n size in bytes of the area to be compared.
|
||||
// \return an integer less than, equal to, or greater than 0,
|
||||
// according as s1 is lexicographically less than, equal to,
|
||||
// or greater than s2 when taken to be unsigned characters.
|
||||
//
|
||||
*/
|
||||
//----------------------------------------------------------------------
|
||||
int n_memcmp(const void *s1, const void *s2, size_t n)
|
||||
{
|
||||
unsigned char* c1=(unsigned char*)s1;
|
||||
unsigned char* c2=(unsigned char*)s2;
|
||||
|
||||
int comparaison=0;
|
||||
int fini=0;
|
||||
int i=0;
|
||||
while ((!fini)&&(i<n)) {
|
||||
if (c1[i]<c2[i]) {
|
||||
fini=1;
|
||||
comparaison=-1;
|
||||
}
|
||||
if (c1[i]>c2[i]) {
|
||||
fini=1;
|
||||
comparaison=1;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return comparaison;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// n_memcpy()
|
||||
/*! Memory copy.
|
||||
//
|
||||
// \param s1 is where the elements are to be copied,
|
||||
// \param s2 is the memory area to copy.
|
||||
// \param n size in bytes of the area to be copied.
|
||||
// \return the memory area where the copy has been done.
|
||||
*/
|
||||
//----------------------------------------------------------------------
|
||||
void *n_memcpy(void *s1, const void *s2, size_t n)
|
||||
{
|
||||
|
||||
unsigned char* c1=(unsigned char*)s1;
|
||||
unsigned char* c2=(unsigned char*)s2;
|
||||
|
||||
int i=0;
|
||||
if ((c1!=0)&&(c2!=0)) {
|
||||
while(i<n) {
|
||||
c1[i]=c2[i];
|
||||
i++;
|
||||
}
|
||||
return (void *)c1;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// n_memset()
|
||||
/*! Sets the first n bytes of a memory area to a value (converted to
|
||||
// an unsigned char).
|
||||
//
|
||||
// \param s is the memory area to transform,
|
||||
// \param c is the value wanted,
|
||||
// \param n is the number of bytes to put at c.
|
||||
// \return s.
|
||||
*/
|
||||
//----------------------------------------------------------------------
|
||||
void *n_memset(void *s, int c, size_t n)
|
||||
{
|
||||
unsigned char* c1=(unsigned char*)s;
|
||||
int i;
|
||||
for (i=0;i<n;i++) {
|
||||
c1[i]=c;
|
||||
}
|
||||
return (void *)c1;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// n_dumpmem()
|
||||
/*! Dumps on the string the n first bytes of a memory area
|
||||
// (used for debugging)
|
||||
//
|
||||
// \param addr address of the memory area
|
||||
// \param len number of bytes to be dumped
|
||||
*/
|
||||
//----------------------------------------------------------------------
|
||||
void n_dumpmem(char *addr, int len)
|
||||
{
|
||||
#define TOHEX(x) \
|
||||
({ char __x = (x); if(__x < 10) __x+='0'; else __x='a'+(__x-10) ; __x; })
|
||||
|
||||
int i;
|
||||
for (i = 0 ; i < len ; i++) {
|
||||
char s[3];
|
||||
if ((i%16) == 0)
|
||||
n_printf("%x\t", (unsigned long)&addr[i]);
|
||||
else if ((i%8) == 0)
|
||||
n_printf(" ");
|
||||
s[0] = TOHEX((addr[i] >> 4) & 0xf);
|
||||
s[1] = TOHEX(addr[i] & 0xf);
|
||||
s[2] = '\0';
|
||||
n_printf("%s ", s);
|
||||
if ((((i+1)%16) == 0) || (i == len-1))
|
||||
n_printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
#define PUTCHAR(carac) \
|
||||
do { \
|
||||
if (result < len-1) *buff++ = carac;\
|
||||
result++; \
|
||||
} while (0)
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// n_vsnprintf()
|
||||
/*! Build a string according to a specified format (internal function)
|
||||
//
|
||||
// Nachos vsnprintf accepts:
|
||||
// %c to print a character,
|
||||
// %s, to print a string,
|
||||
// %d, to print an integer,
|
||||
// %x, to print an integer in hexa
|
||||
// %lx %ld same for 64-bit values
|
||||
// %f, to print a floating point value
|
||||
//
|
||||
// \param buff the destination buffer to generate the string to
|
||||
// \param len the size of buff, determines the number max of
|
||||
// characters copied to buff (taking the final \0 into account)
|
||||
// \param format the string to parse
|
||||
// \param ap parameters to print
|
||||
//
|
||||
// \return the number of characters formatted (NOT including \0),
|
||||
// that is, the number of characters that would have been written
|
||||
// to the buffer if it were large enough. -1 on error.
|
||||
*/
|
||||
//----------------------------------------------------------------------
|
||||
static int n_vsnprintf(char *buff, int len, const char *format, va_list ap)
|
||||
{
|
||||
int i, result;
|
||||
|
||||
if (!buff || !format || (len < 0)) {
|
||||
return -1;
|
||||
}
|
||||
result = 0;
|
||||
|
||||
for (i=0 ; format[i] != '\0' ; i++) {
|
||||
switch (format[i]) {
|
||||
case '%':
|
||||
i++;
|
||||
switch(format[i]) {
|
||||
case '%': {
|
||||
PUTCHAR('%');
|
||||
break;
|
||||
}
|
||||
case 'i':
|
||||
case'd': {
|
||||
int integer = (int) va_arg(ap,int);
|
||||
int cpt2 = 0;
|
||||
char buff_int[11];
|
||||
if (integer<0) {PUTCHAR('-');
|
||||
}
|
||||
do {
|
||||
int m10 = integer%10;
|
||||
m10 = (m10 < 0)? -m10:m10;
|
||||
buff_int[cpt2++]=(char)('0'+ m10);
|
||||
integer=integer/10;
|
||||
} while(integer!=0);
|
||||
for (cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--) {
|
||||
PUTCHAR(buff_int[cpt2]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'l': {
|
||||
i++;
|
||||
switch(format[i]) {
|
||||
case 'd': {
|
||||
long longer = va_arg(ap,long);
|
||||
int cpt2 = 0;
|
||||
char buff_long[20];
|
||||
if (longer<0) {
|
||||
PUTCHAR('-');
|
||||
}
|
||||
do {
|
||||
int m10 = longer%10;
|
||||
m10 = (m10 < 0)? -m10:m10;
|
||||
buff_long[cpt2++]=(char)('0'+ m10);
|
||||
longer=longer/10;
|
||||
} while(longer!=0);
|
||||
for (cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--) {
|
||||
PUTCHAR(buff_long[cpt2]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'x': {
|
||||
uint64_t hexa = va_arg(ap,long);
|
||||
uint64_t nb;
|
||||
uint32_t i, had_nonzero = 0;
|
||||
for (i=0 ; i < 16 ; i++) {
|
||||
nb = (hexa << (i*4));
|
||||
nb = (nb >> 60);
|
||||
nb = nb & 0x000000000000000f;
|
||||
// Skip the leading zeros
|
||||
if (nb == 0) {
|
||||
if (had_nonzero) {
|
||||
PUTCHAR((uint8_t)'0');
|
||||
}
|
||||
}
|
||||
else {
|
||||
had_nonzero = 1;
|
||||
if (nb < 10)
|
||||
PUTCHAR((uint8_t)'0'+(uint8_t)nb);
|
||||
else
|
||||
PUTCHAR((uint8_t)'a'+(uint8_t)(nb-10));
|
||||
}
|
||||
}
|
||||
if (! had_nonzero)
|
||||
PUTCHAR((uint8_t)'0');
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
PUTCHAR('%');
|
||||
PUTCHAR('l');
|
||||
PUTCHAR(format[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'c': {
|
||||
int value = va_arg(ap,int);
|
||||
PUTCHAR((char)value);
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
char *string = va_arg(ap,char *);
|
||||
if (! string)
|
||||
string = "(null)";
|
||||
for( ; *string != '\0' ; string++)
|
||||
PUTCHAR(*string);
|
||||
break;
|
||||
}
|
||||
case 'x': {
|
||||
uint32_t hexa = va_arg(ap,int);
|
||||
uint32_t nb;
|
||||
uint32_t i, had_nonzero = 0;
|
||||
for (i=0 ; i < 8 ; i++) {
|
||||
nb = (hexa << (i*4));
|
||||
nb = (nb >> 28);
|
||||
nb = nb & 0x0000000f;
|
||||
// Skip the leading zeros
|
||||
if (nb == 0) {
|
||||
if (had_nonzero)
|
||||
PUTCHAR((uint8_t)'0');
|
||||
}
|
||||
else {
|
||||
had_nonzero = 1;
|
||||
if (nb < 10)
|
||||
PUTCHAR((uint8_t)'0'+(uint8_t)nb);
|
||||
else
|
||||
PUTCHAR((uint8_t)'a'+(uint8_t)(nb-10));
|
||||
}
|
||||
}
|
||||
if (! had_nonzero)
|
||||
PUTCHAR((uint8_t)'0');
|
||||
break;
|
||||
}
|
||||
/*case 'f': {
|
||||
// Very simple routine to print floats as xxxx.yyyyy
|
||||
// Not very good (unable to print large numbers)
|
||||
// If anyone wants to re-write it, feel free ...
|
||||
double f = (double) va_arg(ap,double);
|
||||
int cpt2, j;
|
||||
char buff_float[200];
|
||||
long ient,idec;
|
||||
if (f<0) {
|
||||
PUTCHAR('-');
|
||||
f = -f;
|
||||
}
|
||||
ient = (int)f;
|
||||
// 100000 = print 5 digits max
|
||||
idec = (int)((f - ((double)ient))*100000);
|
||||
// Round up
|
||||
if ( f - ((double)ient) - ((double)idec)/100000.0 >= 0.5E-5)
|
||||
idec ++;
|
||||
cpt2 = 0;
|
||||
// Print digits after the '.'
|
||||
for (j=0 ; j<5 ; j++) {
|
||||
buff_float[cpt2++]=(char)('0'+(idec%10));
|
||||
idec=idec/10;
|
||||
}
|
||||
buff_float[cpt2++] = '.';
|
||||
// Print digits before the '.'
|
||||
do {
|
||||
buff_float[cpt2++]=(char)('0'+ (ient%10));
|
||||
ient=ient/10;
|
||||
} while (ient!=0);
|
||||
for(j = cpt2 - 1 ; j >= 0 ; j--)
|
||||
PUTCHAR(buff_float[j]);
|
||||
break;
|
||||
}
|
||||
*/
|
||||
default:
|
||||
PUTCHAR('%');
|
||||
PUTCHAR(format[i]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
PUTCHAR(format[i]);
|
||||
}
|
||||
}
|
||||
*buff = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// n_snprintf()
|
||||
/*! Build a string according to a specified format
|
||||
//
|
||||
// Nachos snprintf accepts:
|
||||
// %c to print a character,
|
||||
// %s, to print a string,
|
||||
// %d, to print an integer,
|
||||
// %x, to print a string in hexa
|
||||
// %f, to print a floating point value
|
||||
//
|
||||
// \param buff the destination buffer to generate the string to
|
||||
// \param len the size of buff, determines the number max of
|
||||
// characters copied to buff (taking the final \0 into account)
|
||||
// \param format the string to parse
|
||||
// \param ... the (variable number of) arguments
|
||||
//
|
||||
// \return the number of characters formatted (NOT including \0),
|
||||
// that is, the number of characters that would have been written
|
||||
// to the buffer if it were large enough. -1 on error.
|
||||
*/
|
||||
//----------------------------------------------------------------------
|
||||
int n_snprintf(char * buff, int len, const char *format, ...){
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
len = n_vsnprintf(buff, len, format, ap);
|
||||
va_end(ap);
|
||||
return len;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// n_printf()
|
||||
/*! Print to the standard output parameters.
|
||||
//
|
||||
// Nachos printf accepts:
|
||||
// %c to print a character,
|
||||
// %s, to print a string,
|
||||
// %d, to print an integer,
|
||||
// %x, to print a string in hexa
|
||||
// %ld, %lx, same for 64-bit values
|
||||
// %f, to print a floating point value
|
||||
//
|
||||
// \param parameters to print,
|
||||
// \param type of print.
|
||||
*/
|
||||
//----------------------------------------------------------------------
|
||||
void n_printf(const char *format, ...){
|
||||
|
||||
va_list ap;
|
||||
char buff[200];
|
||||
int len;
|
||||
|
||||
va_start(ap, format);
|
||||
len = n_vsnprintf(buff, sizeof(buff), format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (len >= sizeof(buff)) {
|
||||
len = sizeof(buff) - 1;
|
||||
}
|
||||
if (len > 0) {
|
||||
Write(buff,len,CONSOLE_OUTPUT);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// n_read_int()
|
||||
/*!
|
||||
// Very basic minimalist read integer function, no error
|
||||
// checking...
|
||||
*/
|
||||
//----------------------------------------------------------------------
|
||||
int n_read_int(void) {
|
||||
char buff[200];
|
||||
Read(buff,200,CONSOLE_INPUT);
|
||||
return n_atoi(buff);
|
||||
}
|
87
test/userlib/libnachos.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*! \file libnachos.h
|
||||
\brief Function structures for programs
|
||||
|
||||
Libnachos proposes several 'libc-like' functions
|
||||
for:
|
||||
Input-Output operations,
|
||||
String operations,
|
||||
Memory operations,
|
||||
System calls are defined in kernel/syscalls.h
|
||||
|
||||
Nachos-libc functions are prefixed by 'n' to avoid
|
||||
any confusion with standard libc functions.
|
||||
|
||||
* -----------------------------------------------------
|
||||
* This file is part of the Nachos-RiscV distribution
|
||||
* Copyright (c) 2022 University of Rennes 1.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details
|
||||
* (see see <http://www.gnu.org/licenses/>).
|
||||
* -----------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "userlib/syscall.h"
|
||||
|
||||
typedef void (*VoidNoArgFunctionPtr)();
|
||||
typedef unsigned int size_t;
|
||||
|
||||
// Thread management
|
||||
// ----------------------------
|
||||
ThreadId threadCreate(char * debug_name, VoidNoArgFunctionPtr func);
|
||||
|
||||
// Input/Output operations :
|
||||
// ------------------------------------
|
||||
|
||||
// Print on the standard output specified parameters.
|
||||
void n_printf(const char *format, ...);
|
||||
|
||||
// Format <buff> (of max length <len>) according to the format <format>
|
||||
int n_snprintf(char * buff, int len, const char *format, ...);
|
||||
|
||||
// Read an integer on the standard input
|
||||
int n_read_int(void);
|
||||
|
||||
// String operations :
|
||||
// -------------------
|
||||
|
||||
// Compare two strings byte by byte.
|
||||
int n_strcmp(const char *s1, const char *s2);
|
||||
|
||||
// Copy a string.
|
||||
char* n_strcpy(char *dst, const char *src);
|
||||
|
||||
// Return the number of bytes in a string.
|
||||
size_t n_strlen(const char *s);
|
||||
|
||||
// appends a copy of a string, to the end of another string.
|
||||
char* n_strcat(char *dst, const char *src);
|
||||
|
||||
// Return a upper-case letter,
|
||||
// equivalent to the lower-case letter given.
|
||||
int n_toupper(int c);
|
||||
|
||||
// Return a lower-case letter,
|
||||
// equivalent to the upper-case letter given.
|
||||
int n_tolower(int c);
|
||||
|
||||
// Convert a string in integer.
|
||||
int n_atoi(const char *str);
|
||||
|
||||
// Concerning memory area operations :
|
||||
// -----------------------------------
|
||||
|
||||
// Compare two memory area, looking at the first n bytes .
|
||||
int n_memcmp(const void *s1, const void *s2, size_t n);
|
||||
|
||||
// Copy n byte from an memory area to another.
|
||||
void* n_memcpy(void *s1, const void *s2, size_t n);
|
||||
|
||||
// Set the first n bytes in a memory area to a specified value.
|
||||
void* n_memset(void *s, int c, size_t n);
|
343
test/userlib/sys.s
Normal file
@ -0,0 +1,343 @@
|
||||
/* Start.s
|
||||
* Assembly language assist for user programs running on top of Nachos.
|
||||
*
|
||||
* Since we don't want to pull in the entire C library, we define
|
||||
* what we need for a user program here, namely Start and the system
|
||||
* calls.
|
||||
*
|
||||
* -----------------------------------------------------
|
||||
* This file is part of the BurritOS distribution
|
||||
* Copyright (c) 2022 University of Rennes 1.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details
|
||||
* (see see <http://www.gnu.org/licenses/>).
|
||||
* -----------------------------------------------------
|
||||
*/
|
||||
|
||||
#define IN_ASM
|
||||
#include "userlib/syscall.h"
|
||||
|
||||
// Equivalent to ".text", but with a different name, in order
|
||||
// to be correctly handled by the ldscript
|
||||
.section .sys,"ax",@progbits
|
||||
|
||||
.align 2
|
||||
|
||||
/* -------------------------------------------------------------
|
||||
* __start
|
||||
* Initialize running a C program, by calling "main".
|
||||
*
|
||||
* NOTE: This has to be first, so that it gets loaded at location 0.
|
||||
* The Nachos kernel always starts a program by jumping to location 0.
|
||||
* -------------------------------------------------------------
|
||||
*/
|
||||
|
||||
.globl __start
|
||||
.type __start, @function
|
||||
__start:
|
||||
|
||||
/* Call the program entry point */
|
||||
call main
|
||||
li a0, 0
|
||||
call Exit
|
||||
jr ra /* if we return from main, exit(0) */
|
||||
|
||||
|
||||
/* -------------------------------------------------------------
|
||||
* System call stubs:
|
||||
* Assembly language assist to make system calls to the Nachos kernel.
|
||||
* There is one stub per system call, that places the code for the
|
||||
* system call into register r10, and leaves the arguments to the
|
||||
* system call alone (in other words, arg1 is in r12, arg2 is
|
||||
* in r13, arg3 is in r14, arg4 is in r15)
|
||||
*
|
||||
* The return value is in r10. This follows the standard C calling
|
||||
* convention on the RISC-V.
|
||||
* -------------------------------------------------------------
|
||||
*/
|
||||
|
||||
.globl Halt
|
||||
.type __Halt, @function
|
||||
Halt:
|
||||
addi a7,zero,SC_HALT
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl Exit
|
||||
.type __Exit, @function
|
||||
Exit:
|
||||
addi a7,zero,SC_EXIT
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl Exec
|
||||
.type __Exec, @function
|
||||
Exec:
|
||||
addi a7,zero,SC_EXEC
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl Join
|
||||
.type __Join, @function
|
||||
Join:
|
||||
addi a7,zero,SC_JOIN
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl Create
|
||||
.type __Create, @function
|
||||
Create:
|
||||
addi a7,zero,SC_CREATE
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl Open
|
||||
.type __Open, @function
|
||||
Open:
|
||||
addi a7,zero,SC_OPEN
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl Read
|
||||
.type __Read, @function
|
||||
Read:
|
||||
addi a7,zero,SC_READ
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
|
||||
.globl Write
|
||||
.type __Write, @function
|
||||
Write:
|
||||
addi a7,zero,SC_WRITE
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl Seek
|
||||
.type __Seek, @function
|
||||
Seek:
|
||||
addi a7,zero,SC_SEEK
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl Close
|
||||
.type __Close, @function
|
||||
Close:
|
||||
addi a7,zero,SC_CLOSE
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl FSList
|
||||
.type __FSList, @function
|
||||
FSList:
|
||||
addi a7,zero,SC_FSLIST
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl newThread
|
||||
.type __newThread, @function
|
||||
newThread:
|
||||
addi a7,zero,SC_NEW_THREAD
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl Remove
|
||||
.type __Remove, @function
|
||||
Remove:
|
||||
addi a7,zero,SC_REMOVE
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl Yield
|
||||
.type __Yield, @function
|
||||
Yield:
|
||||
addi a7,zero,SC_YIELD
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl PError
|
||||
.type __PError, @function
|
||||
PError:
|
||||
addi a7,zero,SC_PERROR
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl P
|
||||
.type __P, @function
|
||||
P:
|
||||
addi a7,zero,SC_P
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl V
|
||||
.type __V, @function
|
||||
V:
|
||||
addi a7,zero,SC_V
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
.globl SemCreate
|
||||
.type __SemCreate, @function
|
||||
SemCreate:
|
||||
addi a7,zero,SC_SEM_CREATE
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl SemDestroy
|
||||
.type __SemDestroy, @function
|
||||
SemDestroy:
|
||||
addi a7,zero,SC_SEM_DESTROY
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl SysTime
|
||||
.type __SysTime, @function
|
||||
SysTime:
|
||||
addi a7,zero,SC_SYS_TIME
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl LockCreate
|
||||
.type __LockCreate, @function
|
||||
LockCreate:
|
||||
addi a7,zero,SC_LOCK_CREATE
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
.globl LockDestroy
|
||||
.type __LockDestroy, @function
|
||||
LockDestroy:
|
||||
addi a7,zero,SC_LOCK_DESTROY
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl LockAcquire
|
||||
.type __LockAquire, @function
|
||||
LockAcquire:
|
||||
addi a7,zero,SC_LOCK_ACQUIRE
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl LockRelease
|
||||
.type __LockRelease, @function
|
||||
LockRelease:
|
||||
addi a7,zero,SC_LOCK_RELEASE
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl CondCreate
|
||||
.type __CondCreate, @function
|
||||
CondCreate:
|
||||
addi a7,zero,SC_COND_CREATE
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl CondDestroy
|
||||
.type __CondDestroy, @function
|
||||
CondDestroy:
|
||||
addi a7,zero,SC_COND_DESTROY
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl CondWait
|
||||
.type __CondWait, @function
|
||||
CondWait:
|
||||
addi a7,zero,SC_COND_WAIT
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl CondSignal
|
||||
.type __CondSignal, @function
|
||||
CondSignal:
|
||||
addi a7,zero,SC_COND_SIGNAL
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl CondBroadcast
|
||||
.type __CondBroadcast, @function
|
||||
CondBroadcast:
|
||||
addi a7,zero,SC_COND_BROADCAST
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl TtySend
|
||||
.type __TtySend, @function
|
||||
TtySend:
|
||||
addi a7,zero,SC_TTY_SEND
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl TtyReceive
|
||||
.type __TtyReceive, @function
|
||||
TtyReceive:
|
||||
addi a7,zero,SC_TTY_RECEIVE
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl Mkdir
|
||||
.type __Mkdir, @function
|
||||
Mkdir:
|
||||
addi a7,zero,SC_MKDIR
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl Rmdir
|
||||
.type __Rmdir, @function
|
||||
Rmdir:
|
||||
addi a7,zero,SC_RMDIR
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
||||
.globl Mmap
|
||||
.type __Mmap, @function
|
||||
Mmap:
|
||||
addi a7,zero,SC_MMAP
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
.globl Debug
|
||||
.type __Debug, @function
|
||||
Debug:
|
||||
addi a7,zero,SC_DEBUG
|
||||
ecall
|
||||
jr ra
|
||||
|
||||
|
287
test/userlib/syscall.h
Normal file
@ -0,0 +1,287 @@
|
||||
/*! \file syscall.h
|
||||
\brief Nachos system call interface.
|
||||
|
||||
These are Nachos kernel operations
|
||||
that can be invoked from user programs, by trapping to the kernel
|
||||
via the "syscall" instruction.
|
||||
|
||||
This file is included by user programs and by the Nachos kernel.
|
||||
|
||||
Each of these is invoked by a user program by simply calling the
|
||||
procedure; an assembly language stub stuffs the system call code
|
||||
into a register, and traps to the kernel. The kernel procedures
|
||||
are then invoked in the Nachos kernel, after appropriate error checking,
|
||||
from the system call entry point in exception.cc.
|
||||
|
||||
* -----------------------------------------------------
|
||||
* This file is part of the Nachos-RiscV distribution
|
||||
* Copyright (c) 2022 University of Rennes 1.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details
|
||||
* (see see <http://www.gnu.org/licenses/>).
|
||||
* -----------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SYSCALLS_H
|
||||
#define SYSCALLS_H
|
||||
|
||||
//#include "kernel/copyright.h"
|
||||
|
||||
/* system call codes -- used by the stubs to tell the kernel which system call
|
||||
* is being asked for
|
||||
*/
|
||||
#define SC_HALT 0
|
||||
#define SC_EXIT 1
|
||||
#define SC_EXEC 2
|
||||
#define SC_JOIN 3
|
||||
#define SC_CREATE 4
|
||||
#define SC_OPEN 5
|
||||
#define SC_READ 6
|
||||
#define SC_WRITE 7
|
||||
#define SC_SEEK 8
|
||||
#define SC_CLOSE 9
|
||||
#define SC_NEW_THREAD 10
|
||||
#define SC_YIELD 11
|
||||
#define SC_PERROR 12
|
||||
#define SC_P 13
|
||||
#define SC_V 14
|
||||
#define SC_SEM_CREATE 15
|
||||
#define SC_SEM_DESTROY 16
|
||||
#define SC_LOCK_CREATE 17
|
||||
#define SC_LOCK_DESTROY 18
|
||||
#define SC_LOCK_ACQUIRE 19
|
||||
#define SC_LOCK_RELEASE 20
|
||||
#define SC_COND_CREATE 21
|
||||
#define SC_COND_DESTROY 22
|
||||
#define SC_COND_WAIT 23
|
||||
#define SC_COND_SIGNAL 24
|
||||
#define SC_COND_BROADCAST 25
|
||||
#define SC_TTY_SEND 26
|
||||
#define SC_TTY_RECEIVE 27
|
||||
#define SC_MKDIR 28
|
||||
#define SC_RMDIR 29
|
||||
#define SC_REMOVE 30
|
||||
#define SC_FSLIST 31
|
||||
#define SC_SYS_TIME 32
|
||||
#define SC_MMAP 33
|
||||
#define SC_DEBUG 34
|
||||
|
||||
#ifndef IN_ASM
|
||||
|
||||
/* The system call interface. These are the operations the Nachos
|
||||
* kernel needs to support, to be able to run user programs.
|
||||
*
|
||||
*/
|
||||
|
||||
typedef int t_error;
|
||||
|
||||
/* Stop Nachos, and print out performance stats */
|
||||
void Halt();
|
||||
|
||||
|
||||
/* Return the time spent running Nachos */
|
||||
|
||||
/*! \brief Defines the Nachos basic time unit */
|
||||
typedef struct {
|
||||
long seconds;
|
||||
long nanos;
|
||||
} Nachos_Time;
|
||||
void SysTime(Nachos_Time *t);
|
||||
|
||||
/* Address space control operations: Exit, Exec, and Join */
|
||||
|
||||
/* This user program is done (status = 0 means exited normally). */
|
||||
void Exit(int status);
|
||||
|
||||
/* A unique identifier for a thread executed within a user program */
|
||||
typedef unsigned long ThreadId;
|
||||
|
||||
/* Run the executable, stored in the Nachos file "name", and return the
|
||||
* master thread identifier
|
||||
*/
|
||||
ThreadId Exec(char *name);
|
||||
|
||||
/* Create a new thread in the current process
|
||||
* Return thread identifier
|
||||
*/
|
||||
ThreadId newThread(char * debug_name, int func, int arg);
|
||||
|
||||
/* Only return once the the thread "id" has finished.
|
||||
*/
|
||||
t_error Join(ThreadId id);
|
||||
|
||||
/* Yield the CPU to another runnable thread, whether in this address space
|
||||
* or not.
|
||||
*/
|
||||
void Yield();
|
||||
|
||||
/*! Print the last error message with the personalized one "mess" */
|
||||
void PError(char *mess);
|
||||
|
||||
/* File system operations: Create, Open, Read, Write, Seek, Close
|
||||
* These functions are patterned after UNIX -- files represent
|
||||
* both files *and* hardware I/O devices.
|
||||
*
|
||||
* If this assignment is done before doing the file system assignment,
|
||||
* note that the Nachos file system has a stub implementation, which
|
||||
* will work for the purposes of testing out these routines.
|
||||
*/
|
||||
|
||||
/* A unique identifier for an open Nachos file. */
|
||||
typedef unsigned long OpenFileId;
|
||||
|
||||
/* when an address space starts up, it has two open files, representing
|
||||
* keyboard input and display output (in UNIX terms, stdin and stdout).
|
||||
* Read and Write can be used directly on these, without first opening
|
||||
* the console device.
|
||||
*/
|
||||
#define CONSOLE_INPUT 0
|
||||
#define CONSOLE_OUTPUT 1
|
||||
|
||||
/* Create a Nachos file, with "name" */
|
||||
t_error Create(char *name,int size);
|
||||
|
||||
/* Open the Nachos file "name", and return an "OpenFileId" that can
|
||||
* be used to read and write to the file.
|
||||
*/
|
||||
OpenFileId Open(char *name);
|
||||
|
||||
/* Write "size" bytes from "buffer" to the open file. */
|
||||
t_error Write(char *buffer, int size, OpenFileId id);
|
||||
|
||||
/* Read "size" bytes from the open file into "buffer".
|
||||
* Return the number of bytes actually read -- if the open file isn't
|
||||
* long enough, or if it is an I/O device, and there aren't enough
|
||||
* characters to read, return whatever is available (for I/O devices,
|
||||
* you should always wait until you can return at least one character).
|
||||
*/
|
||||
t_error Read(char *buffer, int size, OpenFileId id);
|
||||
|
||||
/* Seek to a specified offset into an opened file */
|
||||
t_error Seek(int offset, OpenFileId id);
|
||||
|
||||
#ifndef SYSDEP_H
|
||||
/* Close the file, we're done reading and writing to it. */
|
||||
t_error Close(OpenFileId id);
|
||||
#endif // SYSDEP_H
|
||||
|
||||
/* Remove the file */
|
||||
t_error Remove(char* name);
|
||||
|
||||
/******************************************************************/
|
||||
/* system calls concerning directory management */
|
||||
|
||||
/* Create a new repertory
|
||||
Return a negative number if an error ocurred.
|
||||
*/
|
||||
t_error Mkdir(char* name);
|
||||
|
||||
/* Destroy a repertory, which must be empty.
|
||||
Return a negative number if an error ocurred.
|
||||
*/
|
||||
t_error Rmdir(char* name);
|
||||
|
||||
/* List the content of NachOS FileSystem */
|
||||
t_error FSList();
|
||||
|
||||
/******************************************************************/
|
||||
/* User-level synchronization operations : */
|
||||
|
||||
/* System calls concerning semaphores management */
|
||||
|
||||
typedef unsigned long SemId;
|
||||
|
||||
/* Create a semaphore, initialising it at count.
|
||||
Return a Semid, which will enable to do operations on this
|
||||
semaphore */
|
||||
SemId SemCreate(char * debug_name, int count);
|
||||
|
||||
/* Destroy a semaphore identified by sema.
|
||||
Return a negative number if an error occured during the destruction */
|
||||
t_error SemDestroy(SemId sema);
|
||||
|
||||
/* Do the operation P() on the semaphore sema */
|
||||
t_error P(SemId sema);
|
||||
|
||||
/* Do the operation V() on the semaphore sema */
|
||||
t_error V(SemId sema);
|
||||
|
||||
/* System calls concerning locks management */
|
||||
typedef unsigned long LockId;
|
||||
|
||||
/* Create a lock.
|
||||
Return an identifier */
|
||||
LockId LockCreate(char * debug_name);
|
||||
|
||||
/* Destroy a lock.
|
||||
Return a negative number if an error ocurred
|
||||
during the destruction. */
|
||||
t_error LockDestroy(LockId id);
|
||||
|
||||
/* Do the operation Acquire on the lock id.
|
||||
Return a negative number if an error ocurred. */
|
||||
t_error LockAcquire(LockId id);
|
||||
|
||||
/* Do the operation Release on the lock id.
|
||||
Return a negative number if an error ocurred.
|
||||
*/
|
||||
t_error LockRelease(LockId id);
|
||||
|
||||
/* System calls concerning conditions variables. */
|
||||
typedef unsigned long CondId;
|
||||
|
||||
/* Create a new condition variable */
|
||||
CondId CondCreate(char * debug_name);
|
||||
|
||||
/* Destroy a condition variable.
|
||||
Return a negative number if an error ocurred.
|
||||
*/
|
||||
t_error CondDestroy(CondId id);
|
||||
|
||||
/* Do the operation Wait on a condition variable.
|
||||
Returns a negative number if an error ocurred.
|
||||
*/
|
||||
t_error CondWait(CondId cond);
|
||||
|
||||
/* Do the operation Signal on a condition variable (wake up only one thread).
|
||||
Return a negative number if an error ocurred.
|
||||
*/
|
||||
t_error CondSignal(CondId cond);
|
||||
|
||||
/* Do the operation Signal on a condition variable (wake up all threads).
|
||||
Return a negative number if an error ocurred.
|
||||
*/
|
||||
t_error CondBroadcast(CondId cond);
|
||||
|
||||
/******************************************************************/
|
||||
/* System calls concerning serial port and console */
|
||||
|
||||
/* Send the message on the serial communication link.
|
||||
Returns the number of bytes successfully sent.
|
||||
*/
|
||||
int TtySend(char *mess);
|
||||
|
||||
/* Wait for a message comming from the serial communication link.
|
||||
The length of the buffer where the bytes will be copied is given as a parameter.
|
||||
Returns the number of characters actually received.
|
||||
*/
|
||||
int TtyReceive(char *mess,int length);
|
||||
|
||||
/* Map an opened file in memory. Size is the size to be mapped in bytes.
|
||||
*/
|
||||
void *Mmap(OpenFileId f, int size);
|
||||
|
||||
/* For debug purpose
|
||||
*/
|
||||
void Debug(int param);
|
||||
|
||||
#endif // IN_ASM
|
||||
#endif // SYSCALL_H
|
@ -1,8 +0,0 @@
|
||||
0
|
||||
0
|
||||
FF FF
|
||||
F4A12200
|
||||
A0 0A
|
||||
01022B
|
||||
0B 0F
|
||||
FFACBC5CEF
|
@ -1,14 +0,0 @@
|
||||
TOPDIR=.
|
||||
include $(TOPDIR)/Makefile.config
|
||||
|
||||
#
|
||||
# Main targets
|
||||
#
|
||||
objdumps:
|
||||
$(MAKE) -C riscv_instructions
|
||||
|
||||
programs:
|
||||
$(MAKE) -C programs
|
||||
|
||||
clean:
|
||||
rm -rf $(TOPDIR)/target
|
@ -1,24 +0,0 @@
|
||||
include $(TOPDIR)/Makefile.config
|
||||
|
||||
COVERAGE = $(TOPDIR)/riscv_instructions
|
||||
|
||||
AS = $(RISCV_AS) -c
|
||||
GCC = $(RISCV_GCC)
|
||||
LD = $(RISCV_LD)
|
||||
|
||||
INCPATH += -I$(TOPDIR) -I$(COVERAGE)
|
||||
ASFLAGS = $(RISCV_ASFLAGS) $(INCPATH)
|
||||
CFLAGS = $(RISCV_CFLAGS) $(INCPATH)
|
||||
|
||||
# Rules
|
||||
%.a:
|
||||
$(AR) rcv $@ $^
|
||||
|
||||
%.o: %.c
|
||||
$(GCC) $(CFLAGS) -c $<
|
||||
|
||||
%.o: %.s
|
||||
$(AS) $(ASFLAGS) -c $<
|
||||
|
||||
$(PROGRAMS):
|
||||
$(LD) $+ -o $@
|
@ -1,2 +0,0 @@
|
||||
TOPDIR = ../
|
||||
include $(TOPDIR)/Makefile.objdumps
|