Commit 9f7c59e7 authored by Bernhard Heinloth's avatar Bernhard Heinloth
Browse files

Vorgabe oostubs Aufgabe 7

parent ce9a34d2
// vim: set et ts=4 sw=4:
#include "device/vesagraphics.h"
#include "utils/memutil.h"
VESAGraphics::VESAGraphics(void* frontbuffer, void* backbuffer)
: VESAScreen(backbuffer), frontbuffer(frontbuffer), frontbuffer_new(false)
{
}
bool VESAGraphics::switch_buffers()
{
void* new_front = backbuffer;
backbuffer = frontbuffer;
frontbuffer = new_front;
printer->set_lfb(backbuffer);
return !__atomic_exchange_n(&frontbuffer_new, true, __ATOMIC_RELAXED);
}
void VESAGraphics::scanout_frontbuffer()
{
if(frontbuffer_new) {
memcpy(current_mode->PhysBasePtr, frontbuffer, bytes_pp * current_mode->XResolution * current_mode->YResolution);
frontbuffer_new = false;
}
}
// vim: set et ts=4 sw=4:
/*! \file
* \brief Enthält die Klasse VESAGraphics.
*/
#pragma once
#include "machine/vesascreen.h"
/*! \brief Treiber für die VESAGrafikkarte.
*
* Erweitert VESAScreen um die beiden Methoden switch_buffers() und
* scanout_frontbuffer(). Mit deren Hilfe ist es möglich eine Art
* Triplebuffering "per Hand" zu implementieren. Da diese beiden Methoden unter
* gegenseitigem Ausschluss ausgeführt werden müssen (während
* scanout_frontbuffer läuft, soll nicht der Puffer umgeschaltet werden) sind
* sie entweder in der guarded Variante oder im Epilog zu verwenden.
*
* Die Benutzung des Graphikmodus sieht dann grob ungefähr so aus:
* \code
* while(true) {
* //Puffer mit Inhalt füllen
* //Umschalten mit switch_buffers()
* }
* \endcode
* Den Aufruf von scanout_frontbuffer kann man nun auf zwei Arten durchführen.
* Zum einen ist es möglich ihn einfach die Schleife zu integrieren. Es ist
* aber auch möglich den Puffer mit einer festen Frequenz neu zu befüllen.
* Dazu ruft man scanout_frontbuffer sinnigerweise im Epilog des Timers auf.
*/
class VESAGraphics : public VESAScreen
{
void* frontbuffer;
bool frontbuffer_new;
public:
/*! \brief Konstruktor; bekommt zwei Puffer im Hauptspeicher als Parameter
*
*/
VESAGraphics(void* frontbuffer, void* backbuffer);
/*! \brief Tauscht Frontbuffer und Backbuffer aus.
*
* Zeichenoperationen über die Methoden von VESAScreen gehen immer in den
* aktuellen Backbuffer; scanout_frontbuffer kopiert immer den aktuellen
* Frontbuffer in den Speicher der Grafikkarte.
*
* \return \c true wenn der vorherige Frontbuffer in den Speicher der
* Graphikkarte übergeben wurde wurde
*/
bool switch_buffers();
/*! \brief Kopiert den aktuellen Frontbuffer in den Speicher der
* Grafikkarte.
*
*/
void scanout_frontbuffer();
};
# Minix V3 Dateisystemlayout
Auf einer Platte mit Minix V3 steht jeweils nacheinander
1. Boot block (1024 Byte)
2. Superblock (1024 Byte), siehe `struct Minix_Super_Block` in `minix/minix.h`
3. Bitmap inodes
4. Bitmap zonemap (Zonen sind hier genauso groß wie ein Block)
5. Inodes, für den Aufbau einer Inode siehe `struct Minix_Disk_Inode` in `minix/minix.h`
6. Daten in Blöcken. Bei `mkfs.minix` von Linux sind Blöcke jeweils 1024 Byte groß.
* Ein Datenblock kann alternativ auch Verzeichniseinträge enthalten, siehe `struct Minix_Dirent` in `minix/minix.h`
# Minix V3 Dateisystemabbild erstellen
Unter einem Linux:
$ dd if=/dev/zero of=~/file.img bs=1MiB count=1
$ mkfs.minix -3 ~/file.img # optional --inodes <number>
352 Inodes
1024 Blöcke
Erste Datenzone=26 (26)
Zonengröße=1024
Maximalgröße=2147483647
## Zugriff als eingebundenes Dateisystem
Als Root unter Linux:
$ losetup --find --show ~/file.img
$ mount /dev/loop0 /mnt/tmp
# Jetzt Dateien und Co anlegen
$ umount /dev/loop0
$ losetup --detach /dev/loop0
## Zugriff mit dem fstool
Da nicht immer Rootzugriff vorhanden ist (z.B. im CIP) gibt es eine Kommandozeilenanwendung, welche mit FTP-artigen Befehlen einen direkten Zugriff auf das Abbild erlaubt
$ cd tool
$ make
$ fstool ~/file.img
# Jetzt Dateien und Co bearbeiten.
# "help" für weitere Informationen
# Dateisystemabbild in OOStuBS einbinden
## ATA Treiber
Ermöglicht das Schreiben aus OOStuBS auf das Abbild, so dass diese Daten dann in z.B. Linux später gelesen werden können.
In `common.mk` bei `QEMU` `-drive file=./test_imgs/minix_sample_data.img,format=raw` hinzufügen. Dann ist das Abbild – sollte es das Einzige sein – Master am ersten Port. Es reicht dann in der App
#include "fs/harddisk.h"
Harddisk harddisk;
int error = VFS::mount("minix", &harddisk, "");
Möchte man einen anderen Port nutzen, muss man . Z.B. `-drive file=./test_imgs/minix_sample_data.img,format=raw,bus=1,unit=1` bei `QEMU` anhängen (siehe `man qemu`). Dementsprechend `Harddisk harddisk(secondary_bus_slave);` in der App verwenden. Alle weiteren unterstützten Ports finden sich in `enum Drive` unter `harddisk.h`.
## Initrd
Ermöglicht kein Schreiben. Dazu via `QEMUINITRD` Dateisystemabbild als initrd übergeben.
In der App
#include "memory/multiboot.h"
extern void *multiboot_addr;
multiboot_info_t *mb_info = (multiboot_info_t*)multiboot_addr;
if (mb_info->mods_count <= 0) {
Guarded_Scheduler::exit();
}
multiboot_module_t *mod = (multiboot_module_t*)mb_info->mods_addr;
void *initrd = (void *)mod->mod_start;
size_t initrdsize = ((size_t)mod->mod_end) - ((size_t)mod->mod_start);
Ramdisk ramdisk(initrd, initrdsize);
int error = VFS::mount("minix", &ramdisk, "");
## Headerdatei (nicht empfohlen)
Ermöglicht kein Schreiben. Das Erstellen einer Headerdatei, wodurch das Abbild dann über eine Variable genutzt werden kann, geht folgendermaßen:
xxd -i minix_mkfs.img > minix_img.h
In der App
#include "minix_img.h"
#include "fs/ramdisk.h"
static Ramdisk ramdisk(test_imgs_minix_sample_data_img, test_imgs_minix_sample_data_img_len);
int error = VFS::mount("minix", &ramdisk, "");
# Mit Dateien arbeiten
Einige Ordner- und Dateioperationen bietet das Virtualfilesystem (kurz VFS). Beispielsweise finden sich dort Operationen wie `VFS::open(…)`, `VFS::lseek(…)` oder `VFS::stat(…)`. Genauere Beschreibungen der Methoden finden sich in den Kommentaren von `vfs.h`.
#pragma once
#include "fs/blockdevice.h"
class Block
{
public:
static const int DIRTY_FLAG = 0x1;
uint64_t block_number;
void *data;
BlockDevice *bdev;
// void *bdev_data; private data for the owning block device,
// could be used to implement a dirty list
int flags;
Block(BlockDevice *bdev, uint64_t block_number) {
this->block_number = block_number;
data = NULL;
this->bdev = bdev;
flags = 0;
}
Block() {
block_number = -1;
data = NULL;
bdev = NULL;
flags = 0;
}
void mark_dirty() {
flags |= DIRTY_FLAG;
}
bool is_dirty() {
return flags & DIRTY_FLAG;
}
void clear_dirty() {
flags &= ~DIRTY_FLAG;
}
unsigned int get_size() {
return bdev->blocksize;
}
void sync() {
if (is_dirty()) {
bdev->sync(this);
}
}
void unfix() {
bdev->unfix(this);
}
void forget() {
// discard changes (currently not supported by ramdisk)
clear_dirty();
unfix();
}
};
#include "fs/blockdevice.h"
bool BlockDevice::set_blocksize(unsigned int blocksize)
{
// We could accept arbitrary blocksizes, but filesystems
// generally only support these.
if (blocksize == 512) {
this->blocksize = 512;
blocksize_bits = 9;
return true;
}
if (blocksize == 1024) {
this->blocksize = 1024;
blocksize_bits = 10;
return true;
}
if (blocksize == 2048) {
this->blocksize = 2048;
blocksize_bits = 11;
return true;
}
if (blocksize == 4096) {
this->blocksize = 4096;
blocksize_bits = 12;
return true;
}
return false;
}
#pragma once
#include "fs/definitions.h"
class Block;
class BlockDevice
{
public:
unsigned int blocksize;
unsigned int blocksize_bits;
BlockDevice() {
blocksize = 0;
blocksize_bits = 0;
}
// Fixates a block in memory.
// (without block cache this is simply a read)
virtual Block fix(uint64_t block_number) = 0;
// Releases the block and eventually writes its
// data to disk if it was marked dirty.
// (without block cache this is simply a free + write-if-dirty)
virtual void unfix(Block *block) = 0;
// If the block is marked dirty,
// this forces the block's data to be written to disk synchronously.
virtual int sync(Block *block) = 0;
// Calls sync on each dirty block.
// (currently not implemented for lack of a dirty list)
virtual int sync() = 0;
// Sets the logical blocksize.
// This is the number of bytes that will be read/written per 'Block'.
// Must not match the physical blocksize.
// Returns false if the requested blocksize is not supported.
// (Currently 512, 1024, 2048, and 4096 are supported)
bool set_blocksize(unsigned int blocksize);
unsigned int divide_by_blocksize(unsigned int val) {
return val >> blocksize_bits;
}
unsigned int modulo_blocksize(unsigned int val) {
return val & ((1 << blocksize_bits) - 1);
}
unsigned long divide_by_blocksize(unsigned long val) {
return val >> blocksize_bits;
}
unsigned long modulo_blocksize(unsigned long val) {
return val & ((1 << blocksize_bits) - 1);
}
uint64_t divide_by_blocksize(uint64_t val) {
return val >> blocksize_bits;
}
uint64_t modulo_blocksize(uint64_t val) {
return val & ((1 << blocksize_bits) - 1);
}
uint64_t divide_by_blocksize(off_t val) {
return divide_by_blocksize((uint64_t)val);
}
uint64_t modulo_blocksize(off_t val) {
return modulo_blocksize((uint64_t)val);
}
private:
BlockDevice(const BlockDevice&) = delete;
BlockDevice& operator=(const BlockDevice&) = delete;
};
#include "fs/block.h"
#pragma once
#include "types.h"
#define MAX_SYMLINK_LEN 4095 // excluding the null byte
#define MAX_DEPTH 6 // symlink lookup is currently recursive!
// Posix definitions taken from linux
#define SEEK_SET 0 /* seek relative to beginning of file */
#define SEEK_CUR 1 /* seek relative to current file position */
#define SEEK_END 2 /* seek relative to end of file */
#define S_IFMT 0170000
#define S_IFSOCK 0140000
#define S_IFLNK 0120000
#define S_IFREG 0100000
#define S_IFBLK 0060000
#define S_IFDIR 0040000
#define S_IFCHR 0020000
#define S_IFIFO 0010000
#define S_ISUID 0004000
#define S_ISGID 0002000
#define S_ISVTX 0001000
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
typedef long off_t;
typedef unsigned long ino_t;
typedef unsigned short umode_t;
typedef long time_t;
typedef short uid_t;
typedef char gid_t;
typedef uint32_t nlink_t;
typedef short dev_t;
typedef uint32_t blksize_t;
typedef uint32_t blkcnt_t;
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* Inode number */
umode_t st_mode; /* File type and mode */
nlink_t st_nlink; /* Number of hard links */
uid_t st_uid; /* User ID of owner */
gid_t st_gid; /* Group ID of owner */
dev_t st_rdev; /* Device ID (if special file) */
off_t st_size; /* Total size, in bytes */
blksize_t st_blksize; /* Block size for filesystem I/O */
blkcnt_t st_blocks; /* Number of 512B blocks allocated */
time_t st_atime; /* access timestamp in seconds */
time_t st_mtime; /* modification timestamp in seconds */
time_t st_ctime; /* creation timestamp in seconds */
};
/*
* File types
*
* NOTE! These match bits 12..15 of stat.st_mode
* (ie "(i_mode >> 12) & 15").
*/
#define DT_UNKNOWN 0
#define DT_FIFO 1
#define DT_CHR 2
#define DT_DIR 4
#define DT_BLK 6
#define DT_REG 8
#define DT_LNK 10
#define DT_SOCK 12
#define DT_WHT 14
struct Dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* offset to next structure */
unsigned short d_reclen; /* Size of this dirent */
unsigned char d_type; /* File type */
char d_name[]; /* Filename (null-terminated) */
};
struct DIR {
int fd; /* Directory file pointer */
size_t offset; /* Current offset into the block. */
size_t size; /* Total valid data in the block. */
char buf[4096]; /* Buffer for Dirents */
};
//Open flags
#define O_ACCMODE 00000003
#define O_RDONLY 00000000
#define O_WRONLY 00000001
#define O_RDWR 00000002
#define O_CREAT 00000100
#define O_EXCL 00000200
#include "fs/dir_context.h"
#include "fs/errno.h"
#include "fs/util.h"
bool Readdir_Context::dir_emit(const char *name, size_t name_len, ino_t ino, unsigned char type) {
if (error != 0) {
return false;
}
unsigned short reclen = sizeof(Dirent) + name_len + 1;
if (buf_used + reclen >= buf_size) {
if (buf_used == 0) {
error = -EINVAL;
}
return false;
}
Dirent dirent = {};
dirent.d_ino = ino;
dirent.d_off = 0; // TODO I don't know what this is supposed to be
dirent.d_reclen = reclen;
dirent.d_type = type;
Dirent *user_dirent = (Dirent *)(buf + buf_used);
// @Speed we call copy_to_user three times here, the last time for just one byte
size_t n = copy_to_user(user_dirent, &dirent, sizeof(Dirent));
if (n != sizeof(dirent)) {
error = -EFAULT;
return false;
}
n = copy_to_user(user_dirent->d_name, name, name_len);
if (n != name_len) {
error = -EFAULT;
return false;
}
char null_byte = 0;
n = copy_to_user(&user_dirent->d_name[name_len], &null_byte, 1);
if (n != 1) {
error = -EFAULT;
return false;
}
buf_used += reclen;
return true;
}
#pragma once
#include "types.h"
#include "fs/definitions.h"
class Dir_Context
{
public:
off_t pos; // should be updated by the filesystem while iterating over a directory
int error;
Dir_Context(off_t pos) {
this->pos = pos;
this->error = 0;
}
// Called by the filesystem for each entry while iterating over a directory.
// Returns false to indicate that the filesystem should stop the iteration.
virtual bool dir_emit(const char *name, size_t name_len, ino_t ino, unsigned char type) = 0;
};
struct Dirent;
class Readdir_Context : public Dir_Context
{
public:
char *buf;
size_t buf_used; // in bytes
size_t buf_size; // in bytes
Readdir_Context(off_t pos, Dirent *buf, size_t buf_size) : Dir_Context(pos) {
this->buf = (char *)buf;
this->buf_used = 0;
this->buf_size = buf_size;
}
bool dir_emit(const char *name, size_t name_len, ino_t ino, unsigned char type);
};
#pragma once
#define EPERM 1 /* Operation not permitted */
#define ENOENT 2 /* No such file or directory */
#define ESRCH 3 /* No such process */
#define EINTR 4 /* Interrupted system call */
#define EIO 5 /* I/O error */
#define ENXIO 6 /* No such device or address */
#define E2BIG 7 /* Argument list too long */
#define ENOEXEC 8 /* Exec format error */
#define EBADF 9 /* Bad file number */
#define ECHILD 10 /* No child processes */
#define EAGAIN 11 /* Try again */
#define ENOMEM 12 /* Out of memory */
#define EACCES 13 /* Permission denied */
#define EFAULT 14 /* Bad address */
#define ENOTBLK 15 /* Block device required */
#define EBUSY 16 /* Device or resource busy */
#define EEXIST 17 /* File exists */
#define EXDEV 18 /* Cross-device link */
#define ENODEV 19 /* No such device */
#define ENOTDIR 20 /* Not a directory */
#define EISDIR 21 /* Is a directory */
#define EINVAL 22 /* Invalid argument */
#define ENFILE 23 /* File table overflow */
#define EMFILE 24 /* Too many open files */
#define ENOTTY 25 /* Not a typewriter */
#define ETXTBSY 26 /* Text file busy */
#define EFBIG 27 /* File too large */
#define ENOSPC 28 /* No space left on device */
#define ESPIPE 29 /* Illegal seek */
#define EROFS 30 /* Read-only file system */
#define EMLINK 31 /* Too many links */
#define EPIPE 32 /* Broken pipe */
#define EDOM 33 /* Math argument out of domain of func */
#define ERANGE 34 /* Math result not representable */
#define EDEADLK 35 /* Resource deadlock would occur */
#define ENAMETOOLONG 36 /* File name too long */
#define ENOLCK 37 /* No record locks available */
#define ENOSYS 38 /* Function not implemented */
#define ENOTEMPTY 39 /* Directory not empty */
#define ELOOP 40 /* Too many symbolic links encountered */
#define EWOULDBLOCK EAGAIN /* Operation would block */
#define ENOMSG 42 /* No message of desired type */
#define EIDRM 43 /* Identifier removed */
#define ECHRNG 44 /* Channel number out of range */
#define EL2NSYNC 45 /* Level 2 not synchronized */
#define EL3HLT 46 /* Level 3 halted */
#define EL3RST 47 /* Level 3 reset */
#define ELNRNG 48 /* Link number out of range */
#define EUNATCH 49 /* Protocol driver not attached */
#define ENOCSI 50 /* No CSI structure available */
#define EL2HLT 51 /* Level 2 halted */
#define EBADE 52 /* Invalid exchange */
#define EBADR 53 /* Invalid request descriptor */
#define EXFULL 54 /* Exchange full */
#define ENOANO 55 /* No anode */
#define EBADRQC 56 /* Invalid request code */
#define EBADSLT 57 /* Invalid slot */
#define EBFONT 59 /* Bad font file format */
#define ENOSTR 60 /* Device not a stream */
#define ENODATA 61 /* No data available */
#define ETIME 62 /* Timer expired */
#define ENOSR 63 /* Out of streams resources */
#define ENONET 64 /* Machine is not on the network */
#define ENOPKG 65 /* Package not installed */
#define EREMOTE 66 /* Object is remote */
#define ENOLINK 67 /* Link has been severed */
#define EADV 68 /* Advertise error */
#define ESRMNT 69 /* Srmount error */
#define ECOMM 70 /* Communication error on send */
#define EPROTO 71 /* Protocol error */
#define EMULTIHOP 72 /* Multihop attempted */
#define EDOTDOT 73 /* RFS specific error */
#define EBADMSG 74 /* Not a data message */
#define EOVERFLOW 75 /* Value too large for defined data type */
#define ENOTUNIQ 76 /* Name not unique on network */
#define EBADFD 77 /* File descriptor in bad state */
#define EREMCHG 78 /* Remote address changed */
#define ELIBACC 79 /* Can not access a needed shared library */
#define ELIBBAD 80 /* Accessing a corrupted shared library */
#define ELIBSCN 81 /* .lib section in a.out corrupted */
#define ELIBMAX 82 /* Attempting to link in too many shared libraries */
#define ELIBEXEC 83 /* Cannot exec a shared library directly */
#define EILSEQ 84 /* Illegal byte sequence */
#define ERESTART 85 /* Interrupted system call should be restarted */
#define ESTRPIPE 86 /* Streams pipe error */
#define EUSERS 87 /* Too many users */
#define ENOTSOCK 88 /* Socket operation on non-socket */
#define EDESTADDRREQ 89 /* Destination address required */
#define EMSGSIZE 90 /* Message too long */
#define EPROTOTYPE 91 /* Protocol wrong type for socket */
#define ENOPROTOOPT 92 /* Protocol not available */
#define EPROTONOSUPPORT 93 /* Protocol not supported */