diff --git a/CODING b/CODING new file mode 100644 index 00000000..e8bdcfab --- /dev/null +++ b/CODING @@ -0,0 +1,37 @@ +Fiwix kernel coding standards +------------------------------------------------------------------------------- +It's easier on everyone if all authors working on a shared code base are +consistent in the way they write their programs. Fiwix has the following +conventions in its code: + +- Use of snake_case (multi-word names are lower_case_with_underscores) for + everything except for macro and constant names. + +- No space after the name of a function in a call. + For example, printk("hello") not printk ("hello"). + +- No space after keywords "if", "for", "while", "switch". + For example, if(x) not if (x). + +- Space before braces. + For example, if(x) { not if(x){. + +- Space between operands. + For example, for(n = 0; n < 10; n++), not for(n=0;n<10;n++). + +- Beginning-of-line indentation via tabs, not spaces. + +- Preprocessor macros are always UPPERCASE. + +- Pointer types have spaces: (uint16_t *) not (uint16_t*). + +- Comments in code are always as in C89 /* ... */. + +- Multiline comments start always with a capital letter and the last sentence + ends with a period. + +- In a function definition, the function name starts a new line. + Then you can grep -n '^foo' */*.c to find the definition of foo. + +- Functions that take no arguments are declared as f(void) not f(). + diff --git a/Changes b/Changes new file mode 100644 index 00000000..e69de29b diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..d73c45db --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018 Jordi Sanfeliu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..289a295e --- /dev/null +++ b/Makefile @@ -0,0 +1,55 @@ +# fiwix/Makefile +# +# Copyright 2018, Jordi Sanfeliu. All rights reserved. +# Distributed under the terms of the Fiwix License. +# + +TOPDIR := $(shell if [ "$$PWD" != "" ] ; then echo $$PWD ; else pwd ; fi) +INCLUDE = $(TOPDIR)/include + +ARCH = -m32 +CPU = -march=i386 + +DEBUG = -D__DEBUG__ #-D__2DEBUG__ + +CC = $(CROSS_COMPILE)gcc $(ARCH) $(CPU) #$(DEBUG) +LD = $(CROSS_COMPILE)ld + +CFLAGS = -I$(INCLUDE) -Wall -Wstrict-prototypes -ffreestanding -O2 #-Wextra +LDFLAGS = -m elf_i386 -nostartfiles -nostdlib -nodefaultlibs -nostdinc + +DIRS = kernel kernel/syscalls mm fs drivers/block drivers/char lib +OBJS = kernel/kernel.o kernel/syscalls/syscalls.o mm/mm.o fs/fs.o \ + drivers/block/block.o drivers/char/char.o lib/lib.o + +export CC LD CFLAGS LDFLAGS INCLUDE + +all: + @echo "#define UTS_VERSION \"`date`\"" > include/fiwix/version.h + @for n in $(DIRS) ; do (cd $$n ; $(MAKE)) ; done + $(LD) -N -T fiwix.ld $(LDFLAGS) $(OBJS) -o fiwix + nm fiwix | sort | gzip -9c > System.map.gz + +clean: + @for n in $(DIRS) ; do (cd $$n ; $(MAKE) clean) ; done + rm -f *.o fiwix System.map.gz + +floppy: + mkfs.minix -n 30 /dev/fd0 + mount -t minix /dev/fd0 /mnt/floppy + @mkdir -p /mnt/floppy/boot/grub + @echo "(fd0) /dev/fd0" > /mnt/floppy/boot/grub/device.map + @grub-install --root-directory=/mnt/floppy /dev/fd0 + @tools/MAKEBOOTDISK + @cp -prf tools/etc/* /mnt/floppy/etc + @cp fiwix /mnt/floppy/boot + @cp System.map.gz /mnt/floppy/boot + @cp tools/install.sh /mnt/floppy/sbin + umount /mnt/floppy + +floppy_update: + mount -t minix /dev/fd0 /mnt/floppy + cp fiwix /mnt/floppy/boot + cp System.map.gz /mnt/floppy/boot + umount /mnt/floppy + diff --git a/README b/README new file mode 100644 index 00000000..c7ad97bf --- /dev/null +++ b/README @@ -0,0 +1,140 @@ + Fiwix kernel release 1.0.0 + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Fiwix is an operating system kernel, written by Jordi Sanfeliu from scratch, +based on the UNIX architecture and fully focused on being POSIX compatible. +It is designed and developed mainly as a hobby OS but also for educational +purposes, therefore the kernel code is kept as simple as possible. + +It runs on the i386 (x86 32bit) hardware architecture and is compatible with +a good base of existing GNU applications. It offers many UNIX-like features: + + - Mostly written in C language (Assembler only used in the needed parts). + - GRUB Multiboot Specification v1 compliant. + - Full 32bit protected mode non-preemptive kernel. + - For i386 processors and higher. + - Preemptive multitasking. + - Protected task environment (independent memory address per process). + - Interrupt and exception handling. + - POSIX-compliant (mostly). + - Process groups, sessions and job control. + - Interprocess communication with pipes and signals. + - BSD file locking mechanism (POSIX restricted to file and advisory only). + - Virtual memory management up to 4GB (1GB physical only and no swapping yet). + - Demand paging with Copy-On-Write feature. + - Linux 2.0 ABI system calls compatibility (mostly). + - ELF-386 executable format support (statically and dynamically linked). + - Round Robin based scheduler algorithm (no priorities yet). + - VFS abstraction layer. + - Minix v1 and v2 filesystem support. + - EXT2 filesystem support (read only) with 1KB, 2KB and 4KB block sizes. + - Linux-like PROC filesystem support (read only). + - PIPE pseudo-filesystem support. + - ISO9660 filesystem support with Rock Ridge extensions. + - RAMdisk device support. + - SVGAlib based applications support. + - Keyboard driver with Linux keymaps support. + - Parallel port printer driver support. + - Floppy disk device driver and DMA management. + - IDE/ATA hard disk device driver. + - IDE/ATA ATAPI CDROM device driver. + +Fiwix is distributed under the terms of the MIT License, see the LICENSE file +for more details. + + +COMPILING +=============================================================================== +Before compiling you might want to tweak the kernel configuration by changing +the values in the 'include/fiwix/config.h' file. + +The command needed to make a full compilation of the Fiwix kernel is: + +make clean ; make + +This will create the files 'fiwix' (the kernel itself) and 'System.map.gz' (the +symbol table) in the root directory of the source code tree. + +Keep in mind that the kernel doesn't do anything on its own, you need to create +a user-space environment to make use of it. Upon booting, the kernel mounts the +root filesystem and tries to run '/sbin/init' on it, so you need to provide this +program yourself. + + +TESTING +=============================================================================== +To create a complete bootable floppy disk you need to download the Fiwix Test +Suite archive and symlink the directory 'tools/' into the root directory of the +kernel source code. Then insert a floppy disk into the drive and then type the +following: + +make floppy + +If you only want to update an existing floppy disk with a newer or modified +kernel version, then type the following: + +make floppy_update + + +If you don't have a floppy drive but a bootable CDROM IDE/ATA drive, you can +create your own Fiwix Installation CDROM by using your current operating system +(i.e. GNU/Linux). To do this, you might want to use the scripts and tools that +come with the Fiwix Test Suite archive. + +The scripts to create such bootable images cannot be executed under Fiwix +because they need support for the loop device and the ISO9660 creation tools. + +To create your own bootable CDROM ISO image you must do the following steps: + +1. Download the Fiwix Installation CDROM ISO image. +2. Download the Fiwix floppy image or create a new one with 'make_image'. +3. Edit the 'make_cdrom' script and adjust the values of the $INSTALLCD and + $FIWIX_VERSION variables. +4. Execute './make_cdrom'. + + +INSTALLING +=============================================================================== +Please keep in mind that this is a kernel in its very early stages and may well +have serious bugs and broken features which have not yet been identified or +resolved. + +Let me repeat that. + +Please keep in mind that this is a kernel in its very early stages and may well +have serious bugs and broken features which have not yet been identified or +resolved. + + ***************************** + *** USE AT YOUR OWN RISK! *** + ***************************** + + +You can proceed to install the Fiwix OS on a hard disk either once booted from +the floppy or from the Live CDROM. If you chosen the former, you will also need +the Live CDROM inserted in order to install the packages that form all the +system environment. + +I recommend using QEMU or VMware Player, but if you want to use it on a real +hardware you will need a PC with either a bootable floppy 1.44MB drive or an +IDE/ATAPI CDROM drive. + +The minimal requirements to use Fiwix are as follows: + + - Standard IBM PC-AT architecture. + - i386 processor or higher. + - 2MB of RAM memory (64MB recommended). + - Floppy disk (3.5", 1.44MB) or IDE/ATAPI CDROM. + - 500MB IDE Hard disk (1GB recommended). + +Let the system boot either from a floppy or a CDROM, and when you are ready +just type: + +install.sh + + +Happy hacking. + +-- +Copyright (C) 2018, Jordi Sanfeliu. +http://www.fiwix.org diff --git a/THANKS b/THANKS new file mode 100644 index 00000000..954d848d --- /dev/null +++ b/THANKS @@ -0,0 +1,13 @@ +THANKS! + +A huge THANKS to all people who created their own hobby, and not so hobby, +operating systems and made them freely available on Internet. + +From those simple "Hello world!\n" kernels to a real self-hosting operating +system, their projects were a invaluable source of information and inspiration +to create the Fiwix kernel. + +A special thanks to OSDEV Community for their tutorials, documents, wikis, etc. + +Jordi Sanfeliu +http://www.fiwix.org diff --git a/docs/kernel-parameters.txt b/docs/kernel-parameters.txt new file mode 100644 index 00000000..c7db3c92 --- /dev/null +++ b/docs/kernel-parameters.txt @@ -0,0 +1,18 @@ +Fiwix kernel parameters +======================= + +The following is a list of the current kernel parameters: + +console= Set the output console device + Options: /dev/tty[1..12] + +noramdisk Disable RAM disk driver + +ramdisksize= Size of the RAM disk device in kilobytes (KB) + +root= Root filesystem + Options: /dev/fd0, /dev/hda1, ... + +rootfstype= Set the root filesystem type + Options: minix, ext2, iso9660 + diff --git a/docs/kmem_layout.txt b/docs/kmem_layout.txt new file mode 100644 index 00000000..cf6791f9 --- /dev/null +++ b/docs/kmem_layout.txt @@ -0,0 +1,78 @@ + Fiwix Kernel Memory Address Space + ================================= + +0x00000000 + . + . +0x00090000 + +-------------------+ + | 4KB kpage_dir | this page will be reused once the definitive + | only used at boot | kpage_dir is installed + +-------------------+ + . +0x00100000 + +-------------------+ + | .text section | kernel binary (fiwix) + +-------------------+ + | .data section | + +-------------------+ + | .bss section | + +-------------------+ ++ PAGE_SIZE + +-------------------+ this page prevents stack reaching .bss section + | NULL | this page *shouldn't be* mapped so it will GPF, + +-------------------+ its purpose is to capture out-of-bounds addresses ++ PAGE_SIZE /\ + +-------------------+ || + | stack pointer | grows downwards + +-------------------+ kernel has only 4KB space for its own stack ++ PAGE_SIZE + +-------------------+ + | kpage_dir | + +-------------------+ ++ PAGE_SIZE + +-------------------+ + | kpage_table | + +-------------------+ +0x.....000 (page aligned) + +-------------------+ + | proc_table | + +-------------------+ +0x.....000 (page aligned) + +-------------------+ + | buffer_table | + +-------------------+ +0x.....000 (page aligned) + +-------------------+ + | buffer_hash_table | + +-------------------+ +0x.....000 (page aligned) + +-------------------+ + | inode_table | + +-------------------+ +0x.....000 (page aligned) + +-------------------+ + | inode_hash_table | + +-------------------+ +0x.....000 (page aligned) + +-------------------+ + | fd_table | + +-------------------+ +0x.....000 (page aligned) + +-------------------+ + | mount_table | + +-------------------+ +0x.....000 (page aligned) + +-------------------+ + | RAMdisk | + | (default is 4MB) | + +-------------------+ +0x.....000 (page aligned) + +-------------------+ + | page_hash_table | + +-------------------+ +0x.....000 (page aligned) + +-------------------+ + | page_table | + +-------------------+ + diff --git a/drivers/block/Makefile b/drivers/block/Makefile new file mode 100644 index 00000000..91e057c6 --- /dev/null +++ b/drivers/block/Makefile @@ -0,0 +1,19 @@ +# fiwix/drivers/block/Makefile +# +# Copyright 2018, Jordi Sanfeliu. All rights reserved. +# Distributed under the terms of the Fiwix License. +# + +.S.o: + $(CC) -traditional -I$(INCLUDE) -c -o $@ $< +.c.o: + $(CC) $(CFLAGS) -c -o $@ $< + +OBJS = dma.o floppy.o part.o ide.o ide_hd.o ide_cd.o ramdisk.o + +block: $(OBJS) + $(LD) $(LDFLAGS) -r $(OBJS) -o block.o + +clean: + rm -f *.o + diff --git a/drivers/block/dma.c b/drivers/block/dma.c new file mode 100644 index 00000000..b03aded7 --- /dev/null +++ b/drivers/block/dma.c @@ -0,0 +1,93 @@ +/* + * fiwix/drivers/block/dma.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +/* + * DMA Channel Page Address Count + * --------------------------------- + * 0 (8 bit) 87h 0h 1h + * 1 (8 bit) 83h 2h 3h + * 2 (8 bit) 81h 4h 5h + * 3 (8 bit) 82h 6h 7h + * 4 (16 bit) 8Fh C0h C2h + * 5 (16 bit) 8Bh C4h C6h + * 6 (16 bit) 89h C8h CAh + * 7 (16 bit) 8Ah CCh CEh + */ + +#define LOW_BYTE(addr) (addr & 0x00FF) +#define HIGH_BYTE(addr) ((addr & 0xFF00) >> 8) + +unsigned char dma_mask[DMA_CHANNELS] = + { 0x0A, 0x0A, 0x0A, 0x0A, 0xD4, 0xD4, 0xD4, 0xD4 }; +unsigned char dma_mode[DMA_CHANNELS] = + { 0x0B, 0x0B, 0x0B, 0x0B, 0xD6, 0xD6, 0xD6, 0xD6 }; +unsigned char dma_clear[DMA_CHANNELS] = + { 0x0C, 0x0C, 0x0C, 0x0C, 0xD8, 0xD8, 0xD8, 0xD8 }; +unsigned char dma_page[DMA_CHANNELS] = + { 0x87, 0x83, 0x81, 0x82, 0x8F, 0x8B, 0x89, 0x8A }; +unsigned char dma_address[DMA_CHANNELS] = + { 0x00, 0x02, 0x04, 0x06, 0xC0, 0xC4, 0xC8, 0xCC }; +unsigned char dma_count[DMA_CHANNELS] = + { 0x01, 0x03, 0x05, 0x07, 0xC2, 0xC6, 0xCA, 0xCE }; + + +void start_dma(int channel, void *address, unsigned int count, int mode) +{ + /* setup (mask) the DMA channel */ + outport_b(dma_mask[channel], DMA_MASK_CHANNEL | channel); + + /* clear any data transfers that are currently executing */ + outport_b(dma_clear[channel], 0); + + /* set the specified mode */ + outport_b(dma_mode[channel], mode | channel); + + /* set the offset address */ + outport_b(dma_address[channel], LOW_BYTE((unsigned int)address)); + outport_b(dma_address[channel], HIGH_BYTE((unsigned int)address)); + + /* set the physical page */ + outport_b(dma_page[channel], (unsigned int)address >> 16); + + /* the true (internal) length sent to the DMA is actually length + 1 */ + count--; + + /* set the length of the data */ + outport_b(dma_count[channel], LOW_BYTE(count)); + outport_b(dma_count[channel], HIGH_BYTE(count)); + + /* clear the mask */ + outport_b(dma_mask[channel], DMA_UNMASK_CHANNEL | channel); +} + +int dma_register(int channel, char *dev_name) +{ + if(dma_resources[channel]) { + return 1; + } + dma_resources[channel] = dev_name; + return 0; +} + +int dma_unregister(int channel) +{ + if(!dma_resources[channel]) { + return 1; + } + + dma_resources[channel] = NULL; + return 0; +} + +void dma_init(void) +{ + memset_b(dma_resources, NULL, sizeof(dma_resources)); +} diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c new file mode 100644 index 00000000..b04a4c8f --- /dev/null +++ b/drivers/block/floppy.c @@ -0,0 +1,858 @@ +/* + * fiwix/drivers/block/floppy.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WAIT_MOTOR_OFF (3 * HZ) /* time waiting to turn the motor off */ +#define WAIT_FDC WAIT_MOTOR_OFF + +#define INVALID_TRACK -1 + +#define DEV_TYPE_SHIFT 2 /* right shift to match with the floppy + type when minor > 3 */ + +static int need_reset = 0; +static int fdc_wait_interrupt = 0; +static int fdc_timeout = 0; +static unsigned char fdc_results[MAX_FDC_RESULTS]; +static struct resource floppy_resource = { NULL, NULL }; + +static struct fddt fdd_type[] = { +/* + * R (data rate): 0 = 500Kb/s, 2 = 250Kb/s, 3 = 1Mb/s + * SPEC(IFY) 0xAF: SRT = 6ms, HUT = 240ms (500Kb/s) + * SPEC(IFY) 0xD7: SRT = 6ms, HUT = 240ms (250Kb/s) + * SPEC(IFY) 0xDF: SRT = 3ms, HUT = 240ms (500Kb/s) + * Head Load Time 0x02: HLT = 4ms (500Kb/s), Non-DMA = 0 (DMA enabled) + * + * SIZE KB T S H G_RW G_FM R SPEC HLT NAME + * ---------------------------------------------------------------- */ + { 0, 0, 0, 0, 0, 0x00, 0x00, 0, 0x00, 0x00, NULL }, + { 720, 360, 40, 9, 2, 0x2A, 0x50, 2, 0xD7, 0x02, "360KB 5.25\"" }, + { 2400, 1200, 80, 15, 2, 0x2A, 0x50, 0, 0xAF, 0x02, "1.2MB 5.25\"" }, + { 1440, 720, 80, 9, 2, 0x1B, 0x54, 2, 0xD7, 0x02, "720KB 3.5\"" }, + { 2880, 1440, 80, 18, 2, 0x1B, 0x54, 0, 0xAF, 0x02, "1.44MB 3.5\"" }, +/* { 5760, 2880, 80, 36, 2, 0x38, 0x53, 3, 0xDF, 0x02, "2.88MB 3.5\"" },*/ +}; + +/* maximum size of a track for floppy types of 1.44MB */ +extern char _fdc_transfer_area[BPS * 2 * 18]; + +struct fdd_status { + char type; /* floppy disk drive type */ + char motor; + char recalibrated; + char current_track; +}; + +static struct fdd_status fdd_status[] = { + { 0, 0, 0, INVALID_TRACK }, + { 0, 0, 0, INVALID_TRACK }, +}; + +static unsigned char current_fdd = 0; +static struct fddt *current_fdd_type; +static unsigned int fdd_sizes[256]; + +static struct fs_operations fdc_driver_fsop = { + 0, + 0, + + fdc_open, + fdc_close, + NULL, /* read */ + NULL, /* write */ + fdc_ioctl, + fdc_lseek, + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lockup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + fdc_read, + fdc_write, + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +static struct device floppy_device = { + "floppy", + FLOPPY_IRQ, + FDC_MAJOR, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + BLKSIZE_1K, + &fdd_sizes, + &fdc_driver_fsop, +}; + +static int fdc_in(void) +{ + int n; + unsigned char status; + + if(need_reset) { + return -1; + } + + for(n = 0; n < 10000; n++) { + status = inport_b(FDC_MSR) & (FDC_RQM | FDC_DIO); + if(status == FDC_RQM) { + return 0; + } + if(status == (FDC_RQM | FDC_DIO)) { + return inport_b(FDC_DATA); + } + } + need_reset = 1; + printk("WARNING: %s(): fd%d: timeout on %s.\n", __FUNCTION__, current_fdd, floppy_device.name); + return -1; +} + +static void fdc_out(unsigned char value) +{ + int n; + unsigned char status; + + if(need_reset) { + return; + } + + for(n = 0; n < 10000; n++) { + status = inport_b(FDC_MSR) & (FDC_RQM | FDC_DIO); + if(status == FDC_RQM) { + outport_b(FDC_DATA, value); + return; + } + } + + need_reset = 1; + printk("WARNING: %s(): fd%d: unable to send byte 0x%x on %s.\n", __FUNCTION__, current_fdd, value, floppy_device.name); +} + +static void fdc_get_results(void) +{ + int n; + + memset_b(fdc_results, 0, sizeof(fdc_results)); + for(n = 0; n < MAX_FDC_RESULTS; n++) { + fdc_results[n] = fdc_in(); + } + return; +} + +static int fdc_motor_on(void) +{ + struct callout_req creq; + int errno; + + if(fdd_status[current_fdd].motor) { + return 0; + } + + /* select floppy disk drive and turn on its motor */ + outport_b(FDC_DOR, (FDC_DRIVE0 << current_fdd) | FDC_DMA_ENABLE | FDC_ENABLE | current_fdd); + fdd_status[current_fdd].motor = 1; + fdd_status[!current_fdd].motor = 0; + + /* fixed spin-up time of 500ms for 3.5" and 5.25" */ + creq.fn = fdc_timer; + creq.arg = FDC_TR_MOTOR; + add_callout(&creq, HZ / 2); + sleep(&fdc_motor_on, PROC_UNINTERRUPTIBLE); + + errno = 0; + + /* check for a disk change */ + if(inport_b(FDC_DIR) & 0x80) { + errno = 1; + } + + return errno; +} + +static void do_motor_off(unsigned int fdd) +{ + outport_b(FDC_DOR, FDC_DMA_ENABLE | FDC_ENABLE | fdd); + fdd_status[fdd].motor = 0; + fdd_status[0].motor = fdd_status[1].motor = 0; +} + +static void fdc_motor_off(void) +{ + struct callout_req creq; + + creq.fn = do_motor_off; + creq.arg = current_fdd; + add_callout(&creq, WAIT_FDC); +} + +static void fdc_reset(void) +{ + int n; + struct callout_req creq; + + need_reset = 0; + + fdc_wait_interrupt = FDC_RESET; + outport_b(FDC_DOR, 0); /* enter in reset mode */ +/* outport_b(FDC_DOR, FDC_DMA_ENABLE); */ + for(n = 0; n < 1000; n++) { /* recovery time */ + NOP(); + } + outport_b(FDC_DOR, FDC_DMA_ENABLE | FDC_ENABLE); + + creq.fn = fdc_timer; + creq.arg = FDC_TR_DEFAULT; + add_callout(&creq, WAIT_FDC); + sleep(&irq_floppy, PROC_UNINTERRUPTIBLE); + if(fdc_timeout) { + need_reset = 1; + printk("WARNING: %s(): fd%d: timeout on %s.\n", __FUNCTION__, current_fdd, floppy_device.name); + } + del_callout(&creq); + + fdd_status[0].motor = fdd_status[1].motor = 0; + fdd_status[current_fdd].recalibrated = 0; + + /* assumes drive polling mode is ON (by default) */ + for(n = 0; n < 4; n++) { + fdc_out(FDC_SENSEI); + fdc_get_results(); + } + + /* keeps controller informed on the drive about to use */ + fdc_out(FDC_SPECIFY); + fdc_out(current_fdd_type->spec); + fdc_out(current_fdd_type->hlt); + + /* set data rate */ + outport_b(FDC_CCR, current_fdd_type->rate); +} + +static int fdc_recalibrate(void) +{ + struct callout_req creq; + + if(need_reset) { + return 1; + } + + fdc_wait_interrupt = FDC_RECALIBRATE; + fdc_motor_on(); + fdc_out(FDC_RECALIBRATE); + fdc_out(current_fdd); + + if(need_reset) { + return 1; + } + + creq.fn = fdc_timer; + creq.arg = FDC_TR_DEFAULT; + add_callout(&creq, WAIT_FDC); + sleep(&irq_floppy, PROC_UNINTERRUPTIBLE); + if(fdc_timeout) { + need_reset = 1; + printk("WARNING: %s(): fd%d: timeout on %s.\n", __FUNCTION__, current_fdd, floppy_device.name); + return 1; + } + + del_callout(&creq); + fdc_out(FDC_SENSEI); + fdc_get_results(); + + /* PCN must be 0 indicating a successful position to track 0 */ + if((fdc_results[ST0] & (ST0_IC | ST0_SE | ST0_UC | ST0_NR)) != ST0_RECALIBRATE || fdc_results[ST_PCN]) { + need_reset = 1; + printk("WARNING: %s(): fd%d: unable to recalibrate on %s.\n", __FUNCTION__, current_fdd, floppy_device.name); + return 1; + } + + fdd_status[current_fdd].current_track = INVALID_TRACK; + fdd_status[current_fdd].recalibrated = 1; + fdc_motor_off(); + return 0; +} + +static int fdc_seek(int track, int head) +{ + struct callout_req creq; + + if(need_reset) { + return 1; + } + + if(!fdd_status[current_fdd].recalibrated) { + if(fdc_recalibrate()) { + return 1; + } + } + + if(fdd_status[current_fdd].current_track == track) { + return 0; + } + + fdc_wait_interrupt = FDC_SEEK; + fdc_motor_on(); + fdc_out(FDC_SEEK); + fdc_out((head << 2) | current_fdd); + fdc_out(track); + + if(need_reset) { + return 1; + } + + creq.fn = fdc_timer; + creq.arg = FDC_TR_DEFAULT; + add_callout(&creq, WAIT_FDC); + sleep(&irq_floppy, PROC_UNINTERRUPTIBLE); + if(fdc_timeout) { + need_reset = 1; + printk("WARNING: %s(): fd%d: timeout on %s.\n", __FUNCTION__, current_fdd, floppy_device.name); + return 1; + } + + del_callout(&creq); + fdc_out(FDC_SENSEI); + fdc_get_results(); + + if((fdc_results[ST0] & (ST0_IC | ST0_SE | ST0_UC | ST0_NR)) != ST0_SEEK || fdc_results[ST_PCN] != track) { + need_reset = 1; + printk("WARNING: %s(): fd%d: unable to seek on %s.\n", __FUNCTION__, current_fdd, floppy_device.name); + return 1; + } + + fdc_motor_off(); + fdd_status[current_fdd].current_track = track; + return 0; +} + +static int fdc_get_chip(void) +{ + unsigned char version, fifo, id; + + fdc_out(FDC_VERSION); + version = fdc_in(); + fdc_out(FDC_LOCK); + fifo = fdc_in(); + fdc_out(FDC_PARTID); + id = fdc_in(); + + if(version == 0x80) { + if(fifo == 0x80) { + printk("(NEC D765/Intel 8272A/compatible)\n"); + return 0; + } + if(fifo == 0) { + printk("(Intel 82072)\n"); + return 0; + } + } + + if(version == 0x81) { + printk("(Very Early Intel 82077/compatible)\n"); + return 0; + } + + if(version == 0x90) { + if(fifo == 0x80) { + printk("(Old Intel 82077, no FIFO)\n"); + return 0; + } + if(fifo == 0) { + if(id == 0x80) { + printk("(New Intel 82077)\n"); + return 0; + } + if(id == 0x41) { + printk("(Intel 82078)\n"); + return 0; + } + if(id == 0x73) { + printk("(National Semiconductor PC87306)\n"); + return 0; + } + printk("(Intel 82078 compatible)\n"); + return 0; + } + printk("(NEC 72065B)\n"); + return 0; + } + + if(version == 0xA0) { + printk("(SMC FDC37c65C+)\n"); + return 0; + } + printk("(unknown controller chip)\n"); + return 1; +} + +static int fdc_block2chs(__blk_t block, int blksize, int *cyl, int *head, int *sector) +{ + int spb = blksize / FDC_SECTSIZE; + + *cyl = (block * spb) / (current_fdd_type->spt * current_fdd_type->heads); + *head = ((block * spb) % (current_fdd_type->spt * current_fdd_type->heads)) / current_fdd_type->spt; + *sector = (((block * spb) % (current_fdd_type->spt * current_fdd_type->heads)) % current_fdd_type->spt) + 1; + + if(*cyl >= current_fdd_type->tracks || *head >= current_fdd_type->heads || *sector > current_fdd_type->spt) { + return 1; + } + + return 0; +} + +static void set_current_fdd_type(int minor) +{ + current_fdd = minor & 1; + + /* minors 0 and 1 are directly assigned */ + if(minor < 2) { + current_fdd_type = &fdd_type[(int)fdd_status[current_fdd].type]; + } else { + current_fdd_type = &fdd_type[minor >> DEV_TYPE_SHIFT]; + } +} + +void irq_floppy(void) +{ + if(!fdc_wait_interrupt) { + printk("WARNING: %s(): fd%d: unexpected interrupt on %s.\n", __FUNCTION__, current_fdd, floppy_device.name); + need_reset = 1; + } else { + fdc_timeout = fdc_wait_interrupt = 0; + wakeup(&irq_floppy); + } +} + +void fdc_timer(unsigned int reason) +{ + switch(reason) { + case FDC_TR_DEFAULT: + fdc_timeout = 1; + fdc_wait_interrupt = 0; + wakeup(&irq_floppy); + break; + case FDC_TR_MOTOR: + wakeup(&fdc_motor_on); + break; + } +} + +int fdc_open(struct inode *i, struct fd *fd_table) +{ + unsigned char minor; + + minor = MINOR(i->rdev); + if(!TEST_MINOR(floppy_device.minors, minor)) { + return -ENXIO; + } + + lock_resource(&floppy_resource); + set_current_fdd_type(minor); + unlock_resource(&floppy_resource); + + return 0; +} + +int fdc_close(struct inode *i, struct fd *fd_table) +{ + unsigned char minor; + + minor = MINOR(i->rdev); + if(!TEST_MINOR(floppy_device.minors, minor)) { + return -ENXIO; + } + + lock_resource(&floppy_resource); + set_current_fdd_type(minor); + unlock_resource(&floppy_resource); + + return 0; +} + +int fdc_read(__dev_t dev, __blk_t block, char *buffer, int blksize) +{ + unsigned char minor; + unsigned int sectors_read; + int cyl, head, sector; + int retries; + struct callout_req creq; + struct device *d; + + minor = MINOR(dev); + if(!TEST_MINOR(floppy_device.minors, minor)) { + return -ENXIO; + } + + if(!blksize) { + if(!(d = get_device(BLK_DEV, MAJOR(dev)))) { + return -EINVAL; + } + blksize = d->blksize; + } + blksize = blksize ? blksize : BLKSIZE_1K; + + lock_resource(&floppy_resource); + set_current_fdd_type(minor); + + if(fdc_block2chs(block, blksize, &cyl, &head, §or)) { + printk("WARNING: %s(): fd%d: invalid block number %d on %s device %d,%d.\n", __FUNCTION__, current_fdd, block, floppy_device.name, MAJOR(dev), MINOR(dev)); + unlock_resource(&floppy_resource); + return -EINVAL; + } + + for(retries = 0; retries < MAX_FDC_ERR; retries++) { + if(need_reset) { + fdc_reset(); + } + if(fdc_motor_on()) { + printk("%s(): %s disk was changed in device %d,%d!\n", __FUNCTION__, floppy_device.name, MAJOR(dev), MINOR(dev)); + invalidate_buffers(dev); + fdd_status[current_fdd].recalibrated = 0; + } + + if(fdc_seek(cyl, head)) { + printk("WARNING: %s(): fd%d: seek error on %s device %d,%d during read operation.\n", __FUNCTION__, current_fdd, floppy_device.name, MAJOR(dev), MINOR(dev)); + continue; + } + + start_dma(FLOPPY_DMA, _fdc_transfer_area, blksize, DMA_MODE_WRITE | DMA_MODE_SINGLE); + + /* send READ command */ + fdc_wait_interrupt = FDC_READ; + fdc_out(FDC_READ); + fdc_out((head << 2) | current_fdd); + fdc_out(cyl); + fdc_out(head); + fdc_out(sector); + fdc_out(2); /* sector size is 512 bytes */ + fdc_out(current_fdd_type->spt); + fdc_out(current_fdd_type->gpl1); + fdc_out(0xFF); /* sector size is 512 bytes */ + + if(need_reset) { + printk("WARNING: %s(): fd%d: needs reset on %s device %d,%d during read operation.\n", __FUNCTION__, current_fdd, floppy_device.name, MAJOR(dev), MINOR(dev)); + continue; + } + creq.fn = fdc_timer; + creq.arg = FDC_TR_DEFAULT; + add_callout(&creq, WAIT_FDC); + sleep(&irq_floppy, PROC_UNINTERRUPTIBLE); + if(fdc_timeout) { + need_reset = 1; + printk("WARNING: %s(): fd%d: timeout on %s device %d,%d.\n", __FUNCTION__, current_fdd, floppy_device.name, MAJOR(dev), MINOR(dev)); + continue; + } + del_callout(&creq); + fdc_get_results(); + if(fdc_results[ST0] & (ST0_IC | ST0_UC | ST0_NR)) { + need_reset = 1; + continue; + } + break; + } + + if(retries >= MAX_FDC_ERR) { + printk("WARNING: %s(): fd%d: error on %s device %d,%d during read operation,\n", __FUNCTION__, current_fdd, floppy_device.name, MAJOR(dev), MINOR(dev)); + printk("\tblock=%d, sector=%d, cylinder/head=%d/%d\n", block, sector, cyl, head); + unlock_resource(&floppy_resource); + fdc_motor_off(); + return -EIO; + } + + fdc_motor_off(); + sectors_read = (fdc_results[ST_CYL] - cyl) * (current_fdd_type->heads * current_fdd_type->spt); + sectors_read += (fdc_results[ST_HEAD] - head) * current_fdd_type->spt; + sectors_read += fdc_results[ST_SECTOR] - sector; + if(sectors_read * BPS != blksize) { + printk("WARNING: %s(): fd%d: read error on %s device %d,%d (%d sectors read).\n", __FUNCTION__, current_fdd, floppy_device.name, MAJOR(dev), MINOR(dev), sectors_read); + printk("\tblock=%d, sector=%d, cylinder/head=%d/%d\n", block, sector, cyl, head); + unlock_resource(&floppy_resource); + fdc_motor_off(); + return -EIO; + } + + memcpy_b(buffer, (void *)_fdc_transfer_area, blksize); + + unlock_resource(&floppy_resource); + return sectors_read * BPS; +} + +int fdc_write(__dev_t dev, __blk_t block, char *buffer, int blksize) +{ + unsigned char minor; + unsigned int sectors_written; + int cyl, head, sector; + int retries; + struct callout_req creq; + struct device *d; + + minor = MINOR(dev); + if(!TEST_MINOR(floppy_device.minors, minor)) { + return -ENXIO; + } + + if(!blksize) { + if(!(d = get_device(BLK_DEV, MAJOR(dev)))) { + return -EINVAL; + } + blksize = d->blksize; + } + blksize = blksize ? blksize : BLKSIZE_1K; + + lock_resource(&floppy_resource); + set_current_fdd_type(minor); + + if(fdc_block2chs(block, blksize, &cyl, &head, §or)) { + printk("WARNING: %s(): fd%d: invalid block number %d on %s device %d,%d.\n", __FUNCTION__, current_fdd, block, floppy_device.name, MAJOR(dev), MINOR(dev)); + unlock_resource(&floppy_resource); + return -EINVAL; + } + + for(retries = 0; retries < MAX_FDC_ERR; retries++) { + if(need_reset) { + fdc_reset(); + } + if(fdc_motor_on()) { + printk("%s(): %s disk was changed in device %d,%d!\n", __FUNCTION__, floppy_device.name, MAJOR(dev), MINOR(dev)); + invalidate_buffers(dev); + fdd_status[current_fdd].recalibrated = 0; + } + + if(fdc_seek(cyl, head)) { + printk("WARNING: %s(): fd%d: seek error on %s device %d,%d during write operation.\n", __FUNCTION__, current_fdd, floppy_device.name, MAJOR(dev), MINOR(dev)); + continue; + } + + start_dma(FLOPPY_DMA, _fdc_transfer_area, blksize, DMA_MODE_READ | DMA_MODE_SINGLE); + memcpy_b((void *)_fdc_transfer_area, buffer, blksize); + + /* send WRITE command */ + fdc_wait_interrupt = FDC_WRITE; + fdc_out(FDC_WRITE); + fdc_out((head << 2) | current_fdd); + fdc_out(cyl); + fdc_out(head); + fdc_out(sector); + fdc_out(2); /* sector size is 512 bytes */ + fdc_out(current_fdd_type->spt); + fdc_out(current_fdd_type->gpl1); + fdc_out(0xFF); /* sector size is 512 bytes */ + + if(need_reset) { + printk("WARNING: %s(): fd%d: needs reset on %s device %d,%d during write operation.\n", __FUNCTION__, current_fdd, floppy_device.name, MAJOR(dev), MINOR(dev)); + continue; + } + creq.fn = fdc_timer; + creq.arg = FDC_TR_DEFAULT; + add_callout(&creq, WAIT_FDC); + sleep(&irq_floppy, PROC_UNINTERRUPTIBLE); + if(fdc_timeout) { + need_reset = 1; + printk("WARNING: %s(): fd%d: timeout on %s device %d,%d.\n", __FUNCTION__, current_fdd, floppy_device.name, MAJOR(dev), MINOR(dev)); + continue; + } + del_callout(&creq); + fdc_get_results(); + if(fdc_results[ST1] & ST1_NW) { + unlock_resource(&floppy_resource); + fdc_motor_off(); + return -EROFS; + } + if(fdc_results[ST0] & (ST0_IC | ST0_UC | ST0_NR)) { + need_reset = 1; + continue; + } + break; + } + + if(retries >= MAX_FDC_ERR) { + printk("WARNING: %s(): fd%d: error on %s device %d,%d during write operation,\n", __FUNCTION__, current_fdd, floppy_device.name, MAJOR(dev), MINOR(dev)); + printk("\tblock=%d, sector=%d, cylinder/head=%d/%d\n", block, sector, cyl, head); + unlock_resource(&floppy_resource); + fdc_motor_off(); + return -EIO; + } + + fdc_motor_off(); + sectors_written = (fdc_results[ST_CYL] - cyl) * (current_fdd_type->heads * current_fdd_type->spt); + sectors_written += (fdc_results[ST_HEAD] - head) * current_fdd_type->spt; + sectors_written += fdc_results[ST_SECTOR] - sector; + if(sectors_written * BPS != blksize) { + printk("WARNING: %s(): fd%d: write error on %s device %d,%d (%d sectors written).\n", __FUNCTION__, current_fdd, floppy_device.name, MAJOR(dev), MINOR(dev), sectors_written); + printk("\tblock=%d, sector=%d, cylinder/head=%d/%d\n", block, sector, cyl, head); + unlock_resource(&floppy_resource); + fdc_motor_off(); + return -EIO; + } + + unlock_resource(&floppy_resource); + return sectors_written * BPS; +} + +int fdc_ioctl(struct inode *i, int cmd, unsigned long int arg) +{ + unsigned char minor; + struct hd_geometry *geom; + int errno; + + minor = MINOR(i->rdev); + if(!TEST_MINOR(floppy_device.minors, minor)) { + return -ENXIO; + } + + lock_resource(&floppy_resource); + set_current_fdd_type(minor); + unlock_resource(&floppy_resource); + + switch(cmd) { + case HDIO_GETGEO: + if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(struct hd_geometry)))) { + return errno; + } + geom = (struct hd_geometry *)arg; + geom->heads = current_fdd_type->heads; + geom->sectors = current_fdd_type->spt; + geom->cylinders = current_fdd_type->tracks; + geom->start = 0; + break; + case BLKRRPART: + break; + case BLKGETSIZE: + if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int)))) { + return errno; + } + *(int *)arg = fdd_sizes[MINOR(i->rdev)] * 2; + break; + default: + return -EINVAL; + } + return 0; +} + +int fdc_lseek(struct inode *i, __off_t offset) +{ + unsigned char minor; + + minor = MINOR(i->rdev); + if(!TEST_MINOR(floppy_device.minors, minor)) { + return -ENXIO; + } + + lock_resource(&floppy_resource); + set_current_fdd_type(minor); + unlock_resource(&floppy_resource); + + return offset; +} + +void floppy_init(void) +{ + short int cmosval, master, slave; + + cmosval = cmos_read(CMOS_FDDTYPE); + set_current_fdd_type(0); /* sets /dev/fd0 by default */ + + /* the high nibble describes the 'master' floppy drive */ + master = cmosval >> 4; + if(master) { + if(!register_irq(FLOPPY_IRQ, floppy_device.name, irq_floppy)) { + enable_irq(FLOPPY_IRQ); + } + printk("fd0 0x%04X-0x%04X %2d ", FDC_SRA, FDC_CCR, FLOPPY_IRQ); + printk("%s ", fdd_type[master].name); + fdd_status[0].type = fdd_status[1].type = master; + SET_MINOR(floppy_device.minors, 0); + SET_MINOR(floppy_device.minors, 4); + SET_MINOR(floppy_device.minors, 8); + SET_MINOR(floppy_device.minors, 12); + SET_MINOR(floppy_device.minors, 16); + fdd_sizes[0] = fdd_type[master].sizekb; + fdd_sizes[4] = fdd_type[1].sizekb; + fdd_sizes[8] = fdd_type[2].sizekb; + fdd_sizes[12] = fdd_type[3].sizekb; + fdd_sizes[16] = fdd_type[4].sizekb; + fdc_reset(); + fdc_get_chip(); + } + + /* the low nibble is for the 'slave' floppy drive */ + slave = cmosval & 0x0F; + if(slave) { + if(!master) { + if(!register_irq(FLOPPY_IRQ, floppy_device.name, irq_floppy)) { + enable_irq(FLOPPY_IRQ); + } + } + printk("fd1 0x%04X-0x%04X %2d ", FDC_SRA, FDC_CCR, FLOPPY_IRQ); + printk("%s ", fdd_type[slave].name); + fdd_status[1].type = slave; + SET_MINOR(floppy_device.minors, 1); + SET_MINOR(floppy_device.minors, 5); + SET_MINOR(floppy_device.minors, 9); + SET_MINOR(floppy_device.minors, 13); + SET_MINOR(floppy_device.minors, 17); + fdd_sizes[1] = fdd_type[slave].sizekb; + fdd_sizes[5] = fdd_type[1].sizekb; + fdd_sizes[9] = fdd_type[2].sizekb; + fdd_sizes[13] = fdd_type[3].sizekb; + fdd_sizes[17] = fdd_type[4].sizekb; + if(!master) { + fdc_get_chip(); + } else { + printk("\n"); + } + } + + if(master || slave) { + need_reset = 1; + dma_init(); + if(dma_register(FLOPPY_DMA, floppy_device.name)) { + printk("WARNING: %s(): fd%d: unable to register DMA channel on %s.\n", __FUNCTION__, current_fdd, floppy_device.name); + } else { + if(!register_device(BLK_DEV, &floppy_device)) { + do_motor_off(current_fdd); + } + } + } +} diff --git a/drivers/block/ide.c b/drivers/block/ide.c new file mode 100644 index 00000000..0d5ad46e --- /dev/null +++ b/drivers/block/ide.c @@ -0,0 +1,833 @@ +/* + * fiwix/drivers/block/ide.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int ide0_need_reset = 0; +int ide0_wait_interrupt = 0; +int ide0_timeout = 0; +int ide1_need_reset = 0; +int ide1_wait_interrupt = 0; +int ide1_timeout = 0; + +struct ide ide_table[NR_IDE_CTRLS] = { + { IDE_PRIMARY, IDE0_BASE, IDE0_CTRL, IDE0_IRQ, + { + { IDE_MASTER, "hda", IDE0_MAJOR, 0, -1, NULL, NULL, NULL, NULL, NULL, { NULL }, {{ NULL }} }, + { IDE_SLAVE, "hdb", IDE0_MAJOR, 0, -1, NULL, NULL, NULL, NULL, NULL, { NULL }, {{ NULL }} } + } + }, + { IDE_SECONDARY, IDE1_BASE, IDE1_CTRL, IDE1_IRQ, + { + { IDE_MASTER, "hdc", IDE1_MAJOR, 0, -1, NULL, NULL, NULL, NULL, NULL, { NULL }, {{ NULL }} }, + { IDE_SLAVE, "hdd", IDE1_MAJOR, 0, -1, NULL, NULL, NULL, NULL, NULL, { NULL }, {{ NULL }} } + } + } +}; + +static char *ide_ctrl_name[] = { "primary", "secondary" }; +static char *ide_drv_name[] = { "master", "slave" }; + +static unsigned int ide0_sizes[256]; +static unsigned int ide1_sizes[256]; + +static struct fs_operations ide_driver_fsop = { + 0, + 0, + + ide_open, + ide_close, + NULL, /* read */ + NULL, /* write */ + ide_ioctl, + NULL, /* lseek */ + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lockup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + ide_read, + ide_write, + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +static struct device ide0_device = { + "ide0", + IDE0_IRQ, + IDE0_MAJOR, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + 0, + &ide0_sizes, + &ide_driver_fsop, +}; + +static struct device ide1_device = { + "ide1", + IDE1_IRQ, + IDE1_MAJOR, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + 0, + &ide1_sizes, + &ide_driver_fsop, +}; + +static int ide_identify(struct ide *ide, int drive) +{ + short int status, *buffer; + struct callout_req creq; + + if((status = ide_drvsel(ide, drive, IDE_CHS_MODE, 0))) { + /* some controllers return 0xFF to indicate a non-drive condition */ + if(status == 0xFF) { + return status; + } + printk("WARNING: %s(): error on device '%s'.\n", __FUNCTION__, ide->drive[drive].dev_name); + ide_error(ide, status); + return status; + } + + outport_b(ide->base + IDE_COMMAND, (ide->drive[drive].flags & DEVICE_IS_ATAPI) ? ATA_IDENTIFY_PACKET : ATA_IDENTIFY); + if(ide->channel == IDE_PRIMARY) { + ide0_wait_interrupt = ide->base; + creq.fn = ide0_timer; + creq.arg = 0; + add_callout(&creq, WAIT_FOR_IDE); + sleep(&irq_ide0, PROC_UNINTERRUPTIBLE); + if(ide0_timeout) { + status = inport_b(ide->base + IDE_STATUS); + if((status & (IDE_STAT_RDY | IDE_STAT_DRQ)) != (IDE_STAT_RDY | IDE_STAT_DRQ)) { + return 1; + } + } + del_callout(&creq); + } + if(ide->channel == IDE_SECONDARY) { + ide1_wait_interrupt = ide->base; + creq.fn = ide1_timer; + creq.arg = 0; + add_callout(&creq, WAIT_FOR_IDE); + sleep(&irq_ide1, PROC_UNINTERRUPTIBLE); + if(ide1_timeout) { + status = inport_b(ide->base + IDE_STATUS); + if((status & (IDE_STAT_RDY | IDE_STAT_DRQ)) != (IDE_STAT_RDY | IDE_STAT_DRQ)) { + return 1; + } + } + del_callout(&creq); + } + + status = inport_b(ide->base + IDE_STATUS); + if((status & (IDE_STAT_RDY | IDE_STAT_DRQ)) != (IDE_STAT_RDY | IDE_STAT_DRQ)) { + return 1; + } + + if(!(buffer = (void *)kmalloc())) { + return 1; + } + + inport_sw(ide->base + IDE_DATA, (void *)buffer, IDE_HD_SECTSIZE / sizeof(short int)); + memcpy_b(&ide->drive[drive].ident, (void *)buffer, sizeof(struct ide_drv_ident)); + kfree((unsigned int)buffer); + + if(ide->drive[drive].ident.gen_config == IDE_SUPPORTS_CFA) { + ide->drive[drive].flags |= DEVICE_IS_CFA; + } + + if(ide->drive[drive].flags & DEVICE_IS_ATAPI) { + if(((ide->drive[drive].ident.gen_config >> 8) & 0x1F) == ATAPI_IS_CDROM) { + ide->drive[drive].flags |= DEVICE_IS_CDROM; + } + if(ide->drive[drive].ident.gen_config & 0x3) { + printk("WARNING: %s(): packet size must be 16 bytes!\n"); + } + } + + /* only bits 0-7 are relevant */ + ide->drive[drive].ident.rw_multiple &= 0xFF; + return 0; +} + +static void get_device_size(struct ide_drv *drive) +{ + if(drive->ident.capabilities & IDE_HAS_LBA) { + drive->lba_cyls = drive->ident.logic_cyls; + drive->lba_heads = drive->ident.logic_heads; + drive->lba_factor = 0; + + while(drive->lba_cyls > 1023) { + if(drive->lba_heads < 255) { + drive->lba_cyls >>= 1; + drive->lba_heads <<= 1; + } else { + break; + } + drive->lba_factor++; + } + drive->nr_sects = drive->ident.tot_sectors | (drive->ident.tot_sectors2 << 16); + } + + /* some old disk drives (ATA or ATA2) don't specify total sectors */ + if(!(drive->ident.capabilities & IDE_HAS_LBA)) { + if(drive->nr_sects == 0) { + drive->nr_sects = drive->ident.logic_cyls * drive->ident.logic_heads * drive->ident.logic_spt; + } + } + +} + +static int get_udma(struct ide *ide, int drive) +{ + int udma; + + if(ide->drive[drive].ident.fields_validity & IDE_HAS_UDMA) { + if((ide->drive[drive].ident.ultradma >> 13) & 1) { + udma = 5; + } else if((ide->drive[drive].ident.ultradma >> 12) & 1) { + udma = 4; + } else if((ide->drive[drive].ident.ultradma >> 11) & 1) { + udma = 3; + } else if((ide->drive[drive].ident.ultradma >> 10) & 1) { + udma = 2; + } else if((ide->drive[drive].ident.ultradma >> 9) & 1) { + udma = 1; + } else { + udma = 0; + } + } else { + udma = -1; + } + return udma; +} + +static void ide_results(struct ide *ide, int drive) +{ + unsigned int cyl, hds, sect; + __loff_t capacity; + int udma; + int udma_speed[] = { 16, 25, 33, 44, 66, 100 }; + + cyl = ide->drive[drive].ident.logic_cyls; + hds = ide->drive[drive].ident.logic_heads; + sect = ide->drive[drive].ident.logic_spt; + + udma = get_udma(ide, drive); + /* + * After knowing if the device is UDMA capable we could choose between + * the PIO transfer mode or the UDMA transfer mode. + * FIXME: Currently only PIO mode is supported. + */ + + capacity = (__loff_t)ide->drive[drive].nr_sects * BPS; + capacity = capacity / 1024 / 1024; + + printk("%s 0x%04X-0x%04X %2d ", ide->drive[drive].dev_name, ide->base, ide->base + IDE_BASE_LEN, ide->irq); + swap_asc_word(ide->drive[drive].ident.model_number, 40); + printk("%s %s ", ide_ctrl_name[ide->channel], ide_drv_name[ide->drive[drive].drive]); + + if(!(ide->drive[drive].flags & DEVICE_IS_ATAPI)) { + printk("ATA"); + } else { + printk("ATAPI"); + } + + if(ide->drive[drive].flags & DEVICE_IS_CFA) { + printk(" CFA"); + } + + if(ide->drive[drive].flags & DEVICE_IS_DISK) { + printk(" DISK drive %dMB\n", (unsigned int)capacity); + printk(" model=%s\n", ide->drive[drive].ident.model_number); + if(ide->drive[drive].nr_sects < IDE_MIN_LBA) { + printk(" CHS=%d/%d/%d", cyl, hds, sect); + } else { + ide->drive[drive].flags |= DEVICE_REQUIRES_LBA; + printk(" sectors=%d", ide->drive[drive].nr_sects); + } + printk(" cache=%dKB", ide->drive[drive].ident.buffer_cache >> 1); + } + + if(ide->drive[drive].flags & DEVICE_IS_CDROM) { + printk(" CDROM drive\n"); + printk(" model=%s\n", ide->drive[drive].ident.model_number); + printk(" cache=%dKB", ide->drive[drive].ident.buffer_cache >> 1); + } + + if(udma >= 0) { + printk(" UDMA%d(%d)", udma, udma_speed[udma]); + } + if(ide->drive[drive].ident.capabilities & IDE_HAS_LBA) { + ide->drive[drive].flags |= DEVICE_REQUIRES_LBA; + printk(" LBA"); + } + + printk("\n"); + + if(ide->drive[drive].ident.rw_multiple > 1) { + ide->drive[drive].flags |= DEVICE_HAS_RW_MULTIPLE; + } + + /* + printk("\n"); + printk("%s -> %s\n", ide->drive[drive].dev_name, ide->drive[drive].flags & DEVICE_IS_ATAPI ? "ATAPI" : "ATA"); + printk("general conf = %d (%b) (0x%x)\n", ide->drive[drive].ident.gen_config, ide->drive[drive].ident.gen_config, ide->drive[drive].ident.gen_config); + printk("logic_cyls = %d (%b)\n", ide->drive[drive].ident.logic_cyls, ide->drive[drive].ident.logic_cyls); + printk("reserved2 = %d (%b)\n", ide->drive[drive].ident.reserved2, ide->drive[drive].ident.reserved2); + printk("logic_heads = %d (%b)\n", ide->drive[drive].ident.logic_heads, ide->drive[drive].ident.logic_heads); + printk("retired4 = %d (%b)\n", ide->drive[drive].ident.retired4, ide->drive[drive].ident.retired4); + printk("retired5 = %d (%b)\n", ide->drive[drive].ident.retired5, ide->drive[drive].ident.retired5); + printk("logic_spt = %d (%b)\n", ide->drive[drive].ident.logic_spt, ide->drive[drive].ident.logic_spt); + printk("retired7 = %d (%b)\n", ide->drive[drive].ident.retired7, ide->drive[drive].ident.retired7); + printk("retired8 = %d (%b)\n", ide->drive[drive].ident.retired8, ide->drive[drive].ident.retired8); + printk("retired9 = %d (%b)\n", ide->drive[drive].ident.retired9, ide->drive[drive].ident.retired9); + printk("serial number = '%s'\n", ide->drive[drive].ident.serial_number); + printk("vendor spec20 = %d (%b)\n", ide->drive[drive].ident.vendor_spec20, ide->drive[drive].ident.vendor_spec20); + printk("buffer cache = %d (%b)\n", ide->drive[drive].ident.buffer_cache, ide->drive[drive].ident.buffer_cache); + printk("vendor spec22 = %d (%b)\n", ide->drive[drive].ident.vendor_spec22, ide->drive[drive].ident.vendor_spec22); + printk("firmware rev = '%s'\n", ide->drive[drive].ident.firmware_rev); + printk("model number = '%s'\n", ide->drive[drive].ident.model_number); + printk("rw multiple = %d (%b)\n", ide->drive[drive].ident.rw_multiple, ide->drive[drive].ident.rw_multiple); + printk("reserved48 = %d (%b)\n", ide->drive[drive].ident.reserved48, ide->drive[drive].ident.reserved48); + printk("capabilities = %d (%b)\n", ide->drive[drive].ident.capabilities, ide->drive[drive].ident.capabilities); + printk("reserved50 = %d (%b)\n", ide->drive[drive].ident.reserved50, ide->drive[drive].ident.reserved50); + printk("pio mode = %d (%b)\n", ide->drive[drive].ident.pio_mode, ide->drive[drive].ident.pio_mode); + printk("dma mode = %d (%b)\n", ide->drive[drive].ident.dma_mode, ide->drive[drive].ident.dma_mode); + printk("fields validi = %d (%b)\n", ide->drive[drive].ident.fields_validity, ide->drive[drive].ident.fields_validity); + printk("cur log cyls = %d (%b)\n", ide->drive[drive].ident.cur_log_cyls, ide->drive[drive].ident.cur_log_cyls); + printk("cur log heads = %d (%b)\n", ide->drive[drive].ident.cur_log_heads, ide->drive[drive].ident.cur_log_heads); + printk("cur log spt = %d (%b)\n", ide->drive[drive].ident.cur_log_spt, ide->drive[drive].ident.cur_log_spt); + printk("cur capacity = %d (%b)\n", ide->drive[drive].ident.cur_capacity | (ide->drive[drive].ident.cur_capacity2 << 16), ide->drive[drive].ident.cur_capacity | (ide->drive[drive].ident.cur_capacity2 << 16)); + printk("mult sect set = %d (%b)\n", ide->drive[drive].ident.mult_sect_set, ide->drive[drive].ident.mult_sect_set); + printk("tot sectors = %d (%b)\n", ide->drive[drive].ident.tot_sectors | (ide->drive[drive].ident.tot_sectors2 << 16), ide->drive[drive].ident.tot_sectors | (ide->drive[drive].ident.tot_sectors2 << 16)); + printk("singleword dma= %d (%b)\n", ide->drive[drive].ident.singleword_dma, ide->drive[drive].ident.singleword_dma); + printk("multiword dma = %d (%b)\n", ide->drive[drive].ident.multiword_dma, ide->drive[drive].ident.multiword_dma); + printk("adv pio modes = %d (%b)\n", ide->drive[drive].ident.adv_pio_modes, ide->drive[drive].ident.adv_pio_modes); + printk("min multiword = %d (%b)\n", ide->drive[drive].ident.min_multiword, ide->drive[drive].ident.min_multiword); + printk("rec multiword = %d (%b)\n", ide->drive[drive].ident.rec_multiword, ide->drive[drive].ident.rec_multiword); + printk("min pio wo fc = %d (%b)\n", ide->drive[drive].ident.min_pio_wo_fc, ide->drive[drive].ident.min_pio_wo_fc); + printk("min pio w fc = %d (%b)\n", ide->drive[drive].ident.min_pio_w_fc, ide->drive[drive].ident.min_pio_w_fc); + printk("reserved69 = %d (%b)\n", ide->drive[drive].ident.reserved69, ide->drive[drive].ident.reserved69); + printk("reserved70 = %d (%b)\n", ide->drive[drive].ident.reserved70, ide->drive[drive].ident.reserved70); + printk("reserved71 = %d (%b)\n", ide->drive[drive].ident.reserved71, ide->drive[drive].ident.reserved71); + printk("reserved72 = %d (%b)\n", ide->drive[drive].ident.reserved72, ide->drive[drive].ident.reserved72); + printk("reserved73 = %d (%b)\n", ide->drive[drive].ident.reserved73, ide->drive[drive].ident.reserved73); + printk("reserved74 = %d (%b)\n", ide->drive[drive].ident.reserved74, ide->drive[drive].ident.reserved74); + printk("queue depth = %d (%b)\n", ide->drive[drive].ident.queue_depth, ide->drive[drive].ident.queue_depth); + printk("reserved76 = %d (%b)\n", ide->drive[drive].ident.reserved76, ide->drive[drive].ident.reserved76); + printk("reserved77 = %d (%b)\n", ide->drive[drive].ident.reserved77, ide->drive[drive].ident.reserved77); + printk("reserved78 = %d (%b)\n", ide->drive[drive].ident.reserved78, ide->drive[drive].ident.reserved78); + printk("reserved79 = %d (%b)\n", ide->drive[drive].ident.reserved79, ide->drive[drive].ident.reserved79); + printk("major version = %d (%b)\n", ide->drive[drive].ident.majorver, ide->drive[drive].ident.majorver); + printk("minor version = %d (%b)\n", ide->drive[drive].ident.minorver, ide->drive[drive].ident.minorver); + printk("cmdset1 = %d (%b)\n", ide->drive[drive].ident.cmdset1, ide->drive[drive].ident.cmdset1); + printk("cmdset2 = %d (%b)\n", ide->drive[drive].ident.cmdset2, ide->drive[drive].ident.cmdset2); + printk("cmdsf ext = %d (%b)\n", ide->drive[drive].ident.cmdsf_ext, ide->drive[drive].ident.cmdsf_ext); + printk("cmdsf enable1 = %d (%b)\n", ide->drive[drive].ident.cmdsf_enable1, ide->drive[drive].ident.cmdsf_enable1); + printk("cmdsf enable2 = %d (%b)\n", ide->drive[drive].ident.cmdsf_enable2, ide->drive[drive].ident.cmdsf_enable2); + printk("cmdsf default = %d (%b)\n", ide->drive[drive].ident.cmdsf_default, ide->drive[drive].ident.cmdsf_default); + printk("ultra dma = %d (%b)\n", ide->drive[drive].ident.ultradma, ide->drive[drive].ident.ultradma); + printk("reserved89 = %d (%b)\n", ide->drive[drive].ident.reserved89, ide->drive[drive].ident.reserved89); + printk("reserved90 = %d (%b)\n", ide->drive[drive].ident.reserved90, ide->drive[drive].ident.reserved90); + printk("current apm = %d (%b)\n", ide->drive[drive].ident.curapm, ide->drive[drive].ident.curapm); + */ +} + +void irq_ide0(void) +{ + if(!ide0_wait_interrupt) { + printk("WARNING: %s(): unexpected interrupt!\n", __FUNCTION__); + ide0_need_reset = 1; + } else { + ide0_timeout = ide0_wait_interrupt = 0; + wakeup(&irq_ide0); + } +} + +void irq_ide1(void) +{ + if(!ide1_wait_interrupt) { + printk("WARNING: %s(): unexpected interrupt!\n", __FUNCTION__); + ide1_need_reset = 1; + } else { + ide1_timeout = ide1_wait_interrupt = 0; + wakeup(&irq_ide1); + } +} + +void ide0_timer(unsigned int arg) +{ + ide0_timeout = 1; + ide0_wait_interrupt = 0; + wakeup(&irq_ide0); +} + +void ide1_timer(unsigned int arg) +{ + ide1_timeout = 1; + ide1_wait_interrupt = 0; + wakeup(&irq_ide1); +} + +void ide_error(struct ide *ide, int status) +{ + int error; + + if(status & IDE_STAT_ERR) { + error = inport_b(ide->base + IDE_ERROR); + if(error) { + printk("error=0x%x [", error); + } + if(error & IDE_ERR_AMNF) { + printk("address mark not found, "); + } + if(error & IDE_ERR_TK0NF) { + printk("track 0 not found (no media) or media error, "); + } + if(error & IDE_ERR_ABRT) { + printk("command aborted, "); + } + if(error & IDE_ERR_MCR) { + printk("media change requested, "); + } + if(error & IDE_ERR_IDNF) { + printk("id mark not found, "); + } + if(error & IDE_ERR_MC) { + printk("media changer, "); + } + if(error & IDE_ERR_UNC) { + printk("uncorrectable data, "); + } + if(error & IDE_ERR_BBK) { + printk("bad block, "); + } + printk("]"); + } + if(status & IDE_STAT_DWF) { + printk("device fault, "); + } + if(status & IDE_STAT_BSY) { + printk("device busy, "); + } + printk("\n"); +} + +void ide_delay(void) +{ + int n; + + for(n = 0; n < 10000; n++) { + NOP(); + } +} + +void ide_wait400ns(struct ide *ide) +{ + int n; + + /* wait 400ns */ + for(n = 0; n < 4; n++) { + inport_b(ide->ctrl + IDE_ALT_STATUS); + } +} + +int ide_ready(struct ide *ide) +{ + int n, retries, status; + + SET_IDE_RDY_RETR(retries); + for(n = 0; n < retries; n++) { + status = inport_b(ide->ctrl + IDE_ALT_STATUS); + if(!(status & IDE_STAT_BSY)) { + return 0; + } + ide_delay(); + } + + inport_b(ide->base + IDE_STATUS); /* clear any pending interrupt */ + return status; +} + +int ide_drvsel(struct ide *ide, int drive, int mode, unsigned char lba24_head) +{ + int n; + int status; + + for(n = 0; n < MAX_IDE_ERR; n++) { + if((status = ide_ready(ide))) { + continue; + } + break; + } + if(status) { + return status; + } + + outport_b(ide->base + IDE_DRVHD, (mode + (drive << 4)) | lba24_head); + ide_wait400ns(ide); + + for(n = 0; n < MAX_IDE_ERR; n++) { + if((status = ide_ready(ide))) { + continue; + } + break; + } + return status; +} + +int ide_softreset(struct ide *ide) +{ + int error; + + error = 0; + + outport_b(ide->base + IDE_DRVHD, IDE_CHS_MODE); + ide_delay(); + + outport_b(ide->ctrl + IDE_DEV_CTRL, IDE_DEVCTR_SRST | IDE_DEVCTR_NIEN); + ide_delay(); + outport_b(ide->ctrl + IDE_DEV_CTRL, 0); + ide_delay(); + + outport_b(ide->base + IDE_DRVHD, IDE_CHS_MODE); + ide_delay(); + if(ide_ready(ide)) { + printk("WARNING: %s(): reset error on IDE(%d:0).\n", __FUNCTION__, ide->channel); + error = 1; + } else { + /* device is disk by default */ + ide->drive[IDE_MASTER].flags |= DEVICE_IS_DISK; + + /* check if it's an ATAPI device */ + if(inport_b(ide->base + IDE_SECCNT) == 1 && inport_b(ide->base + IDE_SECNUM) == 1) { + if(inport_b(ide->base + IDE_LCYL) == 0x14 && inport_b(ide->base + IDE_HCYL) == 0xEB) { + ide->drive[IDE_MASTER].flags &= ~DEVICE_IS_DISK; + ide->drive[IDE_MASTER].flags |= DEVICE_IS_ATAPI; + } + } + } + + outport_b(ide->base + IDE_DRVHD, IDE_CHS_MODE + (1 << 4)); + ide_delay(); + if(ide_ready(ide)) { + printk("WARNING: %s(): reset error on IDE(%d:1).\n", __FUNCTION__, ide->channel); + outport_b(ide->base + IDE_DRVHD, IDE_CHS_MODE); + ide_delay(); + ide_ready(ide); + error |= (1 << 4); + } + + outport_b(ide->ctrl + IDE_DEV_CTRL, 0); + ide_delay(); + if(error > 1) { + return error; + } + + /* device is disk by default */ + ide->drive[IDE_SLAVE].flags |= DEVICE_IS_DISK; + + /* check if it's an ATAPI device */ + if(inport_b(ide->base + IDE_SECCNT) == 1 && inport_b(ide->base + IDE_SECNUM) == 1) { + if(inport_b(ide->base + IDE_LCYL) == 0x14 && inport_b(ide->base + IDE_HCYL) == 0xEB) { + ide->drive[IDE_SLAVE].flags &= ~DEVICE_IS_DISK; + ide->drive[IDE_SLAVE].flags |= DEVICE_IS_ATAPI; + } + } + + return error; +} + +struct ide * get_ide_controller(__dev_t dev) +{ + int controller; + + if(MAJOR(dev) == IDE0_MAJOR) { + controller = IDE_PRIMARY; + } else { + if(MAJOR(dev) == IDE1_MAJOR) { + controller = IDE_SECONDARY; + } else { + return NULL; + } + } + return &ide_table[controller]; +} + +int get_ide_drive(__dev_t dev) +{ + int drive; + + drive = MINOR(dev); + if(drive) { + if(drive & (1 << IDE_SLAVE_MSF)) { + drive = IDE_SLAVE; + } else { + drive = IDE_MASTER; + } + } + return drive; +} + +int ide_open(struct inode *i, struct fd *fd_table) +{ + int drive; + struct ide *ide; + struct device *d; + + if(!(ide = get_ide_controller(i->rdev))) { + return -EINVAL; + } + + if(!(d = get_device(BLK_DEV, MAJOR(i->rdev)))) { + return -ENXIO; + } + if(!TEST_MINOR(d->minors, MINOR(i->rdev))) { + return -ENXIO; + } + + drive = get_ide_drive(i->rdev); + if(ide->drive[drive].fsop && ide->drive[drive].fsop->open) { + return ide->drive[drive].fsop->open(i, fd_table); + } + return -EINVAL; +} + +int ide_close(struct inode *i, struct fd *fd_table) +{ + int drive; + struct ide *ide; + struct device *d; + + if(!(ide = get_ide_controller(i->rdev))) { + return -EINVAL; + } + + if(!(d = get_device(BLK_DEV, MAJOR(i->rdev)))) { + return -ENXIO; + } + if(!TEST_MINOR(d->minors, MINOR(i->rdev))) { + return -ENXIO; + } + + drive = get_ide_drive(i->rdev); + if(ide->drive[drive].fsop && ide->drive[drive].fsop->close) { + return ide->drive[drive].fsop->close(i, fd_table); + } + return -EINVAL; +} + +int ide_read(__dev_t dev, __blk_t block, char *buffer, int blksize) +{ + int drive; + struct ide *ide; + struct device *d; + + if(!(ide = get_ide_controller(dev))) { + printk("%s(): no ide controller!\n", __FUNCTION__); + return -EINVAL; + } + + if(!(d = get_device(BLK_DEV, MAJOR(dev)))) { + return -ENXIO; + } + if(!TEST_MINOR(d->minors, MINOR(dev))) { + return -ENXIO; + } + + drive = get_ide_drive(dev); + if(ide->drive[drive].fsop && ide->drive[drive].fsop->read_block) { + return ide->drive[drive].fsop->read_block(dev, block, buffer, blksize); + } + printk("WARNING: %s(): device %d,%d does not have the read_block() method!\n", __FUNCTION__, MAJOR(dev), MINOR(dev)); + return -EINVAL; +} + +int ide_write(__dev_t dev, __blk_t block, char *buffer, int blksize) +{ + int drive; + struct ide *ide; + struct device *d; + + if(!(ide = get_ide_controller(dev))) { + printk("%s(): no ide controller!\n", __FUNCTION__); + return -EINVAL; + } + + if(!(d = get_device(BLK_DEV, MAJOR(dev)))) { + return -ENXIO; + } + if(!TEST_MINOR(d->minors, MINOR(dev))) { + return -ENXIO; + } + + drive = get_ide_drive(dev); + if(ide->drive[drive].fsop && ide->drive[drive].fsop->write_block) { + return ide->drive[drive].fsop->write_block(dev, block, buffer, blksize); + } + printk("WARNING: %s(): device %d,%d does not have the write_block() method!\n", __FUNCTION__, MAJOR(dev), MINOR(dev)); + return -EINVAL; +} + +int ide_ioctl(struct inode *i, int cmd, unsigned long int arg) +{ + int drive; + struct ide *ide; + struct device *d; + + if(!(ide = get_ide_controller(i->rdev))) { + return -EINVAL; + } + + if(!(d = get_device(BLK_DEV, MAJOR(i->rdev)))) { + return -ENXIO; + } + if(!TEST_MINOR(d->minors, MINOR(i->rdev))) { + return -ENXIO; + } + + drive = get_ide_drive(i->rdev); + if(ide->drive[drive].fsop && ide->drive[drive].fsop->ioctl) { + return ide->drive[drive].fsop->ioctl(i, cmd, arg); + } + return -EINVAL; +} + +void ide_init(void) +{ + int devices, errno; + struct ide *ide; + + if(!register_irq(IDE0_IRQ, ide0_device.name, irq_ide0)) { + enable_irq(IDE0_IRQ); + } + devices = 0; + + ide = &ide_table[IDE_PRIMARY]; + errno = ide_softreset(ide); + if(!(errno & 1)) { + if(!(ide_identify(ide, IDE_MASTER))) { + get_device_size(&ide->drive[IDE_MASTER]); + ide_results(ide, IDE_MASTER); + register_device(BLK_DEV, &ide0_device); + if(ide->drive[IDE_MASTER].flags & DEVICE_IS_DISK) { + if(!ide_hd_init(ide, IDE_MASTER)) { + devices++; + } + } + if(ide->drive[IDE_MASTER].flags & DEVICE_IS_CDROM) { + if(!ide_cd_init(ide, IDE_MASTER)) { + devices++; + } + } + } + } + if(!(errno & 0x10)) { + if(!(ide_identify(ide, IDE_SLAVE))) { + get_device_size(&ide->drive[IDE_SLAVE]); + ide_results(ide, IDE_SLAVE); + if(!devices) { + register_device(BLK_DEV, &ide0_device); + } + if(ide->drive[IDE_SLAVE].flags & DEVICE_IS_DISK) { + if(!ide_hd_init(ide, IDE_SLAVE)) { + devices++; + } + } + if(ide->drive[IDE_SLAVE].flags & DEVICE_IS_CDROM) { + if(!ide_cd_init(ide, IDE_SLAVE)) { + devices++; + } + } + } + } + if(!devices) { + disable_irq(IDE0_IRQ); + unregister_irq(IDE0_IRQ); + } + + if(!register_irq(IDE1_IRQ, ide1_device.name, irq_ide1)) { + enable_irq(IDE1_IRQ); + } + devices = 0; + ide = &ide_table[IDE_SECONDARY]; + errno = ide_softreset(ide); + if(!(errno & 1)) { + if(!(ide_identify(ide, IDE_MASTER))) { + get_device_size(&ide->drive[IDE_MASTER]); + ide_results(ide, IDE_MASTER); + register_device(BLK_DEV, &ide1_device); + if(ide->drive[IDE_MASTER].flags & DEVICE_IS_DISK) { + if(!ide_hd_init(ide, IDE_MASTER)) { + devices++; + } + } + if(ide->drive[IDE_MASTER].flags & DEVICE_IS_CDROM) { + if(!ide_cd_init(ide, IDE_MASTER)) { + devices++; + } + } + } + } + if(!(errno & 0x10)) { + if(!(ide_identify(ide, IDE_SLAVE))) { + get_device_size(&ide->drive[IDE_SLAVE]); + ide_results(ide, IDE_SLAVE); + if(!devices) { + register_device(BLK_DEV, &ide1_device); + } + if(ide->drive[IDE_SLAVE].flags & DEVICE_IS_DISK) { + if(!ide_hd_init(ide, IDE_SLAVE)) { + devices++; + } + } + if(ide->drive[IDE_SLAVE].flags & DEVICE_IS_CDROM) { + if(!ide_cd_init(ide, IDE_SLAVE)) { + devices++; + } + } + } + } + if(!devices) { + disable_irq(IDE1_IRQ); + unregister_irq(IDE1_IRQ); + } +} diff --git a/drivers/block/ide_cd.c b/drivers/block/ide_cd.c new file mode 100644 index 00000000..933671cb --- /dev/null +++ b/drivers/block/ide_cd.c @@ -0,0 +1,528 @@ +/* + * fiwix/drivers/block/ide_cd.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* default size of 1GB is enough to read a whole CDROM */ +#define CDROM_DEFAULT_SIZE (1024 * 1024) /* in KBs */ + +static struct resource ide_cd_resource = { NULL, NULL }; + +static struct fs_operations ide_cd_driver_fsop = { + 0, + 0, + + ide_cd_open, + ide_cd_close, + NULL, /* read */ + NULL, /* write */ + ide_cd_ioctl, + NULL, /* lseek */ + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lockup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + ide_cd_read, + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +static char *sense_key_err[] = { + "NO SENSE", + "RECOVERED ERROR", + "NOT READY", + "MEDIUM ERROR", + "HARDWARE ERROR", + "ILLEGAL REQUEST", + "UNIT ATTENTION", + "DATA PROTECT", + "RESERVED", + "RESERVED", + "RESERVED", + "ABORTED COMMAND", + "MISCOMPARE", + "RESERVED" +}; + +enum { + RS_NO_SENSE, + RS_RECOVERED_ERROR, + RS_NOT_READY, + RS_MEDIUM_ERROR, + RS_HARDWARE_ERROR, + RS_ILLEGAL_REQUEST, + RS_UNIT_ATTENTION, + RS_DATA_PROTECT, + RS_RESERVED1, + RS_RESERVED2, + RS_RESERVED3, + RS_ABORTED_COMMAND, + RS_MISCOMPARE, + RS_RESERVED4 +}; + +static int send_packet_command(unsigned char *pkt, struct ide *ide, int drive, int blksize) +{ + int n, retries, status; + + outport_b(ide->ctrl + IDE_DEV_CTRL, 0); + ide_delay(); + outport_b(ide->base + IDE_DRVHD, IDE_CHS_MODE); + ide_delay(); + if(ide_drvsel(ide, drive, IDE_CHS_MODE, 0)) { + printk("WARNING: %s(): %s: drive not ready to receive PACKET command.\n", __FUNCTION__, ide->drive[drive].dev_name); + return 1; + } + + CLI(); + outport_b(ide->base + IDE_FEATURES, 0); + outport_b(ide->base + IDE_SECCNT, 0); + outport_b(ide->base + IDE_SECNUM, 0); + outport_b(ide->base + IDE_LCYL, blksize & 0xFF); + outport_b(ide->base + IDE_HCYL, blksize >> 8); + outport_b(ide->base + IDE_DRVHD, drive << 4); + outport_b(ide->base + IDE_COMMAND, ATA_PACKET); + ide_wait400ns(ide); + +/* + * NOTE: Some devices prior to ATA/ATAPI-4 assert INTRQ if enabled at this + * point. See IDENTIFY PACKET DEVICE, word 0, bits 5-6 to determine if an + * interrupt will occur. + */ + SET_IDE_RDY_RETR(retries); + + for(n = 0; n < retries; n++) { + status = inport_b(ide->base + IDE_STATUS); + if((status & (IDE_STAT_DRQ | IDE_STAT_BSY)) == IDE_STAT_DRQ) { + break; + } + ide_delay(); + } + if(n >= retries) { + printk("WARNING: %s(): %s: drive not ready to receive command packet (retries = %d).\n", __FUNCTION__, ide->drive[drive].dev_name, n); + return 1; + } + + outport_sw(ide->base + IDE_DATA, pkt, 12 / sizeof(short int)); + return 0; +} + +static int atapi_read_data(__dev_t dev, char *data, struct ide *ide, int blksize, int offset) +{ + int errno, status; + char *buffer; + int retries, bytes; + struct callout_req creq; + + for(retries = 0; retries < MAX_IDE_ERR; retries++) { + if(ide->channel == IDE_PRIMARY) { + ide0_wait_interrupt = ide->base; + creq.fn = ide0_timer; + creq.arg = 0; + add_callout(&creq, WAIT_FOR_IDE); + sleep(&irq_ide0, PROC_UNINTERRUPTIBLE); + if(ide0_timeout) { + status = inport_b(ide->base + IDE_STATUS); + if((status & (IDE_STAT_RDY | IDE_STAT_DRQ)) != (IDE_STAT_RDY | IDE_STAT_DRQ)) { + continue; + } + } + del_callout(&creq); + } + if(ide->channel == IDE_SECONDARY) { + ide1_wait_interrupt = ide->base; + creq.fn = ide1_timer; + creq.arg = 0; + add_callout(&creq, WAIT_FOR_IDE); + sleep(&irq_ide1, PROC_UNINTERRUPTIBLE); + if(ide1_timeout) { + status = inport_b(ide->base + IDE_STATUS); + if((status & (IDE_STAT_RDY | IDE_STAT_DRQ)) != (IDE_STAT_RDY | IDE_STAT_DRQ)) { + continue; + } + } + del_callout(&creq); + } + status = inport_b(ide->base + IDE_STATUS); + if(status & IDE_STAT_ERR) { + continue; + } + + if((status & (IDE_STAT_DRQ | IDE_STAT_BSY)) == 0) { + break; + } + + bytes = (inport_b(ide->base + IDE_HCYL) << 8) + inport_b(ide->base + IDE_LCYL); + if(!bytes || bytes > blksize) { + break; + } + + bytes = MAX(bytes, IDE_CD_SECTSIZE); /* read more than 2048 bytes is not supported */ + buffer = data + offset; + inport_sw(ide->base + IDE_DATA, (void *)buffer, bytes / sizeof(short int)); + } + + if(status & IDE_STAT_ERR) { + errno = inport_b(ide->base + IDE_ERROR); + printk("WARNING: %s(): error on cdrom device %d,%d, status=0x%x error=0x%x,\n", __FUNCTION__, MAJOR(dev), MINOR(dev), status, errno); + return 1; + } + + if(retries >= MAX_IDE_ERR) { + printk("WARNING: %s(): timeout on cdrom device %d,%d, status=0x%x.\n", __FUNCTION__, MAJOR(dev), MINOR(dev), status); + /* a reset may be required at this moment */ + return 1; + } + return 0; +} + +static int atapi_cmd_testunit(struct ide *ide, int drive) +{ + unsigned char pkt[12]; + + pkt[0] = ATAPI_TEST_UNIT; + pkt[1] = NULL; + pkt[2] = NULL; + pkt[3] = NULL; + pkt[4] = NULL; + pkt[5] = NULL; + pkt[6] = NULL; + pkt[7] = NULL; + pkt[8] = NULL; + pkt[9] = NULL; + pkt[10] = NULL; + pkt[11] = NULL; + return send_packet_command(pkt, ide, drive, 0); +} + +static int atapi_cmd_reqsense(struct ide *ide, int drive) +{ + unsigned char pkt[12]; + + pkt[0] = ATAPI_REQUEST_SENSE; + pkt[1] = NULL; + pkt[2] = NULL; + pkt[3] = NULL; + pkt[4] = 252; /* this command can send up to 252 bytes */ + pkt[5] = NULL; + pkt[6] = NULL; + pkt[7] = NULL; + pkt[8] = NULL; + pkt[9] = NULL; + pkt[10] = NULL; + pkt[11] = NULL; + return send_packet_command(pkt, ide, drive, 0); +} + +static int atapi_cmd_startstop(int action, struct ide *ide, int drive) +{ + unsigned char pkt[12]; + + pkt[0] = ATAPI_START_STOP; + pkt[1] = NULL; + pkt[2] = NULL; + pkt[3] = NULL; + pkt[4] = action; + pkt[5] = NULL; + pkt[6] = NULL; + pkt[7] = NULL; + pkt[8] = NULL; + pkt[9] = NULL; + pkt[10] = NULL; + pkt[11] = NULL; + return send_packet_command(pkt, ide, drive, 0); +} + +static int atapi_cmd_mediumrm(int action, struct ide *ide, int drive) +{ + unsigned char pkt[12]; + + pkt[0] = ATAPI_MEDIUM_REMOVAL; + pkt[1] = NULL; + pkt[2] = NULL; + pkt[3] = NULL; + pkt[4] = action; + pkt[5] = NULL; + pkt[6] = NULL; + pkt[7] = NULL; + pkt[8] = NULL; + pkt[9] = NULL; + pkt[10] = NULL; + pkt[11] = NULL; + return send_packet_command(pkt, ide, drive, 0); +} + +static int request_sense(char *buffer, __dev_t dev, struct ide *ide, int drive) +{ + int errcode; + int sense_key, sense_asc; + + errcode = inport_b(ide->base + IDE_ERROR); + sense_key = (errcode & 0xF0) >> 4; + printk("\tSense Key code indicates a '%s' condition.\n", sense_key_err[sense_key & 0xF]); + errcode = atapi_cmd_reqsense(ide, drive); + printk("reqsense() returned %d\n", errcode); + errcode = atapi_read_data(dev, buffer, ide, BLKSIZE_2K, 0); + printk("atapi_read_data() returned %d\n", errcode); + errcode = (int)(buffer[0] & 0x7F); + sense_key = (int)(buffer[2] & 0xF); + sense_asc = (int)(buffer[12] & 0xFF); + printk("errcode = %x\n", errcode); + printk("sense_key = %x\n", sense_key); + printk("sense_asc = %x\n", sense_asc); + return errcode; +} + +void ide_cd_timer(unsigned int arg) +{ + wakeup(&ide_cd_open); +} + +int ide_cd_open(struct inode *i, struct fd *fd_table) +{ + int minor; + int drive; + char *buffer; + int errcode; + int sense_key, sense_asc; + int retries; + struct ide *ide; + + if(!(ide = get_ide_controller(i->rdev))) { + return -EINVAL; + } + + minor = MINOR(i->rdev); + drive = get_ide_drive(i->rdev); + if(drive) { + minor &= ~(1 << IDE_SLAVE_MSF); + } + + CLI(); + lock_resource(&ide_cd_resource); + + if(!(buffer = (void *)kmalloc())) { + unlock_resource(&ide_cd_resource); + return -ENOMEM; + } + + if((errcode = atapi_cmd_testunit(ide, drive))) { + printk("WARNING: %s(): cdrom device %d,%d is not ready for TEST_UNIT, error %d.\n", __FUNCTION__, MAJOR(i->rdev), MINOR(i->rdev), errcode); + request_sense(buffer, i->rdev, ide, drive); + } + + for(retries = 0; retries < MAX_CD_ERR; retries++) { + if(!(errcode = atapi_cmd_startstop(CD_LOAD, ide, drive))) { + break; + } + printk("WARNING: %s(): cdrom device %d,%d is not ready for CD_LOAD, error %d.\n", __FUNCTION__, MAJOR(i->rdev), MINOR(i->rdev), errcode); + atapi_read_data(i->rdev, buffer, ide, BLKSIZE_2K, 0); + errcode = request_sense(buffer, i->rdev, ide, drive); + sense_key = (errcode & 0xF0) >> 4; + /* trying to eject on slim drives may lead to an illegal request */ + if(!sense_key || sense_key == RS_ILLEGAL_REQUEST) { + break; + } + if(errcode == 0x70 || errcode == 0x71) { + sense_key = (int)(buffer[2] & 0xF); + sense_asc = (int)(buffer[12] & 0xFF); + if(sense_key == RS_NOT_READY && sense_asc == ASC_NO_MEDIUM) { + kfree((unsigned int)buffer); + unlock_resource(&ide_cd_resource); + return -ENOMEDIUM; + } + } + } + + if(retries == MAX_CD_ERR) { + if(sense_key == RS_NOT_READY) { + kfree((unsigned int)buffer); + unlock_resource(&ide_cd_resource); + return -ENOMEDIUM; + } + } + + if(atapi_cmd_mediumrm(CD_LOCK_MEDIUM, ide, drive)) { + printk("WARNING: %s(): error on cdrom device %d,%d while trying to lock medium.\n", __FUNCTION__, MAJOR(i->rdev), MINOR(i->rdev), ATAPI_MEDIUM_REMOVAL); + request_sense(buffer, i->rdev, ide, drive); + } + + /* this line just to catch interrupt */ + atapi_read_data(i->rdev, buffer, ide, BLKSIZE_2K, 0); + kfree((unsigned int)buffer); + + unlock_resource(&ide_cd_resource); + return 0; +} + +int ide_cd_close(struct inode *i, struct fd *fd_table) +{ + int drive; + char *buffer; + struct ide *ide; + + if(!(ide = get_ide_controller(i->rdev))) { + return -EINVAL; + } + + if(!(buffer = (void *)kmalloc())) { + return -ENOMEM; + } + + drive = get_ide_drive(i->rdev); + + /* FIXME: only if device usage == 0 */ + invalidate_buffers(i->rdev); + + if(atapi_cmd_mediumrm(CD_UNLOCK_MEDIUM, ide, drive)) { + printk("WARNING: %s(): error on cdrom device %d,%d during 0x%x command.\n", __FUNCTION__, MAJOR(i->rdev), MINOR(i->rdev), ATAPI_MEDIUM_REMOVAL); + } + + /* this line just to catch interrupt */ + atapi_read_data(i->rdev, buffer, ide, BLKSIZE_2K, 0); + kfree((unsigned int)buffer); + + return 0; +} + +int ide_cd_read(__dev_t dev, __blk_t block, char *buffer, int blksize) +{ + int drive; + int sectors_to_read; + int n, retries; + unsigned char pkt[12]; + struct ide *ide; + + if(!(ide = get_ide_controller(dev))) { + return -EINVAL; + } + + drive = get_ide_drive(dev); + blksize = BLKSIZE_2K; + sectors_to_read = blksize / IDE_CD_SECTSIZE; + + pkt[0] = ATAPI_READ10; + pkt[1] = NULL; + pkt[2] = (block >> 24) & 0xFF; + pkt[3] = (block >> 16) & 0xFF; + pkt[4] = (block >> 8) & 0xFF; + pkt[5] = block & 0xFF; + pkt[6] = NULL; + pkt[7] = (sectors_to_read >> 8) & 0xFF; + pkt[8] = sectors_to_read & 0xFF; + pkt[9] = NULL; + pkt[10] = NULL; + pkt[11] = NULL; + + lock_resource(&ide_cd_resource); + for(n = 0; n < sectors_to_read; n++, block++) { + for(retries = 0; retries < MAX_CD_ERR; retries++) { + if(send_packet_command(pkt, ide, drive, blksize)) { + printk("\tblock=%d, offset=%d\n", block, block * blksize); + unlock_resource(&ide_cd_resource); + return -EIO; + } + if(atapi_read_data(dev, buffer, ide, blksize, n * IDE_CD_SECTSIZE)) { + int errcode; + int sense_key; + errcode = inport_b(ide->base + IDE_ERROR); + sense_key = (errcode & 0xF0) >> 4; + printk("\tSense Key code indicates a '%s' condition.\n", sense_key_err[sense_key & 0xF]); + if(sense_key) { + continue; + } + } + break; + } + if(retries == MAX_CD_ERR) { + printk("\tblock=%d, offset=%d\n", block, block * blksize); + unlock_resource(&ide_cd_resource); + return -EIO; + } + + } + unlock_resource(&ide_cd_resource); + return sectors_to_read * IDE_CD_SECTSIZE; +} + +int ide_cd_ioctl(struct inode *i, int cmd, unsigned long int arg) +{ + struct ide *ide; + + if(!(ide = get_ide_controller(i->rdev))) { + return -EINVAL; + } + + switch(cmd) { + default: + return -EINVAL; + break; + } + + return 0; +} + +int ide_cd_init(struct ide *ide, int drive) +{ + struct device *d; + + ide->drive[drive].fsop = &ide_cd_driver_fsop; + + if(!(d = get_device(BLK_DEV, ide->drive[drive].major))) { + return -EINVAL; + } + if(drive == IDE_MASTER) { + ide->drive[drive].minor_shift = IDE_MASTER_MSF; + SET_MINOR(d->minors, 0); + ((unsigned int *)d->device_data)[0] = CDROM_DEFAULT_SIZE; + } else { + ide->drive[drive].minor_shift = IDE_SLAVE_MSF; + SET_MINOR(d->minors, 1 << IDE_SLAVE_MSF); + ((unsigned int *)d->device_data)[1 << IDE_SLAVE_MSF] = CDROM_DEFAULT_SIZE; + } + + return 0; +} diff --git a/drivers/block/ide_hd.c b/drivers/block/ide_hd.c new file mode 100644 index 00000000..c965b247 --- /dev/null +++ b/drivers/block/ide_hd.c @@ -0,0 +1,509 @@ +/* + * fiwix/drivers/block/ide_hd.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct resource ide_hd_resource = { NULL, NULL }; + +static struct fs_operations ide_hd_driver_fsop = { + 0, + 0, + + ide_hd_open, + ide_hd_close, + NULL, /* read */ + NULL, /* write */ + ide_hd_ioctl, + NULL, /* lseek */ + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lockup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + ide_hd_read, + ide_hd_write, + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +static void assign_minors(__dev_t rdev, struct ide *ide, struct partition *part) +{ + int n; + int drive, minor; + struct device *d; + + minor = 0; + drive = get_ide_drive(rdev); + + if(ide->channel == IDE_PRIMARY) { + if(!(d = get_device(BLK_DEV, IDE0_MAJOR))) { + return; + } + } else if(ide->channel == IDE_SECONDARY) { + if(!(d = get_device(BLK_DEV, IDE1_MAJOR))) { + return; + } + } else { + printk("WARNING: %s(): invalid device %d,%d.\n", __FUNCTION__, MAJOR(rdev), MINOR(rdev)); + return; + } + + for(n = 0; n < NR_PARTITIONS; n++) { + if(drive == IDE_MASTER) { + minor = (1 << ide->drive[drive].minor_shift) + n; + } + if(drive == IDE_SLAVE) { + minor = (1 << ide->drive[drive].minor_shift) + n + 1; + } + CLEAR_MINOR(d->minors, minor); + if(part[n].type) { + SET_MINOR(d->minors, minor); + ((unsigned int *)d->device_data)[minor] = part[n].nr_sects / 2; + } + } +} + +static __off_t block2sector(__off_t offset, int blksize, struct partition *part, int minor) +{ + __off_t sector; + + sector = offset * (blksize / IDE_HD_SECTSIZE); + if(minor) { + sector += part[minor - 1].startsect; + } + return sector; +} + +static void sector2chs(__off_t offset, int *cyl, int *head, int *sector, struct ide_drv_ident *ident) +{ + *cyl = offset / (ident->logic_spt * ident->logic_heads); + *head = (offset / ident->logic_spt) % ident->logic_heads; + *sector = (offset % ident->logic_spt) + 1; +} + +int ide_hd_open(struct inode *i, struct fd *fd_table) +{ + return 0; +} + +int ide_hd_close(struct inode *i, struct fd *fd_table) +{ + sync_buffers(i->rdev); + return 0; +} + +int ide_hd_read(__dev_t dev, __blk_t block, char *buffer, int blksize) +{ + int minor; + int drive; + int sectors_to_read, cmd; + int n, status, r, retries; + int cyl, head, sector; + __off_t offset; + struct ide *ide; + struct ide_drv_ident *ident; + struct partition *part; + struct callout_req creq; + + if(!(ide = get_ide_controller(dev))) { + return -EINVAL; + } + + minor = MINOR(dev); + if((drive = get_ide_drive(dev))) { + minor &= ~(1 << IDE_SLAVE_MSF); + } + + SET_IDE_RDY_RETR(retries); + + blksize = blksize ? blksize : BLKSIZE_1K; + sectors_to_read = MIN(blksize, PAGE_SIZE) / IDE_HD_SECTSIZE; + + ident = &ide->drive[drive].ident; + part = ide->drive[drive].part_table; + offset = block2sector(block, blksize, part, minor); + + CLI(); + lock_resource(&ide_hd_resource); + + n = 0; + + while(n < sectors_to_read) { + if(ide->drive[drive].flags & DEVICE_HAS_RW_MULTIPLE) { + outport_b(ide->base + IDE_SECCNT, sectors_to_read); + cmd = ATA_READ_MULTIPLE_PIO; + } else { + outport_b(ide->base + IDE_SECCNT, 1); + cmd = ATA_READ_PIO; + } + + if(ide->drive[drive].flags & DEVICE_REQUIRES_LBA) { + outport_b(ide->base + IDE_SECNUM, offset & 0xFF); + outport_b(ide->base + IDE_LCYL, (offset >> 8) & 0xFF); + outport_b(ide->base + IDE_HCYL, (offset >> 16) & 0xFF); + if(ide_drvsel(ide, drive, IDE_LBA_MODE, (offset >> 24) & 0x0F)) { + printk("WARNING: %s(): %s: drive not ready.\n", __FUNCTION__, ide->drive[drive].dev_name); + unlock_resource(&ide_hd_resource); + return -EIO; + } + } else { + sector2chs(offset, &cyl, &head, §or, ident); + outport_b(ide->base + IDE_SECNUM, sector); + outport_b(ide->base + IDE_LCYL, cyl); + outport_b(ide->base + IDE_HCYL, (cyl >> 8)); + if(ide_drvsel(ide, drive, IDE_CHS_MODE, head)) { + printk("WARNING: %s(): %s: drive not ready.\n", __FUNCTION__, ide->drive[drive].dev_name); + unlock_resource(&ide_hd_resource); + return -EIO; + } + } + outport_b(ide->base + IDE_COMMAND, cmd); + if(ide->channel == IDE_PRIMARY) { + ide0_wait_interrupt = ide->base; + creq.fn = ide0_timer; + creq.arg = 0; + add_callout(&creq, WAIT_FOR_IDE); + sleep(&irq_ide0, PROC_UNINTERRUPTIBLE); + if(!ide0_timeout) { + del_callout(&creq); + } + } + if(ide->channel == IDE_SECONDARY) { + ide1_wait_interrupt = ide->base; + creq.fn = ide1_timer; + creq.arg = 0; + add_callout(&creq, WAIT_FOR_IDE); + sleep(&irq_ide1, PROC_UNINTERRUPTIBLE); + if(!ide1_timeout) { + del_callout(&creq); + } + } + for(r = 0; r < retries; r++) { + status = inport_b(ide->base + IDE_STATUS); + if(!(status & IDE_STAT_BSY) && (status & IDE_STAT_DRQ)) { + break; + } + ide_delay(); + } + if(status & IDE_STAT_ERR) { + printk("WARNING: %s(): %s: error on hard disk dev %d,%d during read.\n", __FUNCTION__, ide->drive[drive].dev_name, MAJOR(dev), MINOR(dev)); + printk("\tstatus=0x%x ", status); + ide_error(ide, status); + printk("\tblock %d, sector %d.\n", block, offset); + inport_b(ide->base + IDE_STATUS); /* clear any pending interrupt */ + unlock_resource(&ide_hd_resource); + return -EIO; + } + + if(cmd == ATA_READ_MULTIPLE_PIO) { + inport_sw(ide->base + IDE_DATA, (void *)buffer, (IDE_HD_SECTSIZE * sectors_to_read) / sizeof(short int)); + break; + } + inport_sw(ide->base + IDE_DATA, (void *)buffer, IDE_HD_SECTSIZE / sizeof(short int)); + inport_b(ide->ctrl + IDE_ALT_STATUS); /* ignore results */ + inport_b(ide->base + IDE_STATUS); /* clear any pending interrupt */ + n++; + offset++; + buffer += IDE_HD_SECTSIZE; + } + inport_b(ide->ctrl + IDE_ALT_STATUS); /* ignore results */ + inport_b(ide->base + IDE_STATUS); /* clear any pending interrupt */ + unlock_resource(&ide_hd_resource); + return sectors_to_read * IDE_HD_SECTSIZE; +} + +int ide_hd_write(__dev_t dev, __blk_t block, char *buffer, int blksize) +{ + int minor; + int drive; + int sectors_to_write, cmd; + int n, status, r, retries; + int cyl, head, sector; + __off_t offset; + struct ide *ide; + struct ide_drv_ident *ident; + struct partition *part; + struct callout_req creq; + + if(!(ide = get_ide_controller(dev))) { + return -EINVAL; + } + + minor = MINOR(dev); + if((drive = get_ide_drive(dev))) { + minor &= ~(1 << IDE_SLAVE_MSF); + } + + SET_IDE_RDY_RETR(retries); + + blksize = blksize ? blksize : BLKSIZE_1K; + sectors_to_write = MIN(blksize, PAGE_SIZE) / IDE_HD_SECTSIZE; + + ident = &ide->drive[drive].ident; + part = ide->drive[drive].part_table; + offset = block2sector(block, blksize, part, minor); + + CLI(); + lock_resource(&ide_hd_resource); + + n = 0; + + while(n < sectors_to_write) { + if(ide->drive[drive].flags & DEVICE_HAS_RW_MULTIPLE) { + outport_b(ide->base + IDE_SECCNT, sectors_to_write); + cmd = ATA_WRITE_MULTIPLE_PIO; + } else { + outport_b(ide->base + IDE_SECCNT, 1); + cmd = ATA_WRITE_PIO; + } + + if(ide->drive[drive].flags & DEVICE_REQUIRES_LBA) { + outport_b(ide->base + IDE_SECNUM, offset & 0xFF); + outport_b(ide->base + IDE_LCYL, (offset >> 8) & 0xFF); + outport_b(ide->base + IDE_HCYL, (offset >> 16) & 0xFF); + if(ide_drvsel(ide, drive, IDE_LBA_MODE, (offset >> 24) & 0x0F)) { + printk("WARNING: %s(): %s: drive not ready.\n", __FUNCTION__, ide->drive[drive].dev_name); + unlock_resource(&ide_hd_resource); + return -EIO; + } + } else { + sector2chs(offset, &cyl, &head, §or, ident); + outport_b(ide->base + IDE_SECNUM, sector); + outport_b(ide->base + IDE_LCYL, cyl); + outport_b(ide->base + IDE_HCYL, (cyl >> 8)); + if(ide_drvsel(ide, drive, IDE_CHS_MODE, head)) { + printk("WARNING: %s(): %s: drive not ready.\n", __FUNCTION__, ide->drive[drive].dev_name); + unlock_resource(&ide_hd_resource); + return -EIO; + } + } + outport_b(ide->base + IDE_COMMAND, cmd); + for(r = 0; r < retries; r++) { + status = inport_b(ide->base + IDE_STATUS); + if(!(status & IDE_STAT_BSY) && (status & IDE_STAT_DRQ)) { + break; + } + ide_delay(); + } + if(status & IDE_STAT_ERR) { + printk("WARNING: %s(): %s: error on hard disk dev %d,%d during write.\n", __FUNCTION__, ide->drive[drive].dev_name, MAJOR(dev), MINOR(dev)); + printk("\tstatus=0x%x ", status); + ide_error(ide, status); + printk("\tblock %d, sector %d.\n", block, offset); + inport_b(ide->base + IDE_STATUS); /* clear any pending interrupt */ + unlock_resource(&ide_hd_resource); + return -EIO; + } + + if(cmd == ATA_WRITE_MULTIPLE_PIO) { + outport_sw(ide->base + IDE_DATA, (void *)buffer, (IDE_HD_SECTSIZE * sectors_to_write) / sizeof(short int)); + } else { + outport_sw(ide->base + IDE_DATA, (void *)buffer, IDE_HD_SECTSIZE / sizeof(short int)); + } + if(ide->channel == IDE_PRIMARY) { + ide0_wait_interrupt = ide->base; + creq.fn = ide0_timer; + creq.arg = 0; + add_callout(&creq, WAIT_FOR_IDE); + sleep(&irq_ide0, PROC_UNINTERRUPTIBLE); + if(!ide0_timeout) { + del_callout(&creq); + } + } + if(ide->channel == IDE_SECONDARY) { + ide1_wait_interrupt = ide->base; + creq.fn = ide1_timer; + creq.arg = 0; + add_callout(&creq, WAIT_FOR_IDE); + sleep(&irq_ide1, PROC_UNINTERRUPTIBLE); + if(!ide1_timeout) { + del_callout(&creq); + } + } + + inport_b(ide->ctrl + IDE_ALT_STATUS); /* ignore results */ + inport_b(ide->base + IDE_STATUS); /* clear any pending interrupt */ + if(cmd == ATA_WRITE_MULTIPLE_PIO) { + break; + } + n++; + offset++; + buffer += IDE_HD_SECTSIZE; + } + inport_b(ide->ctrl + IDE_ALT_STATUS); /* ignore results */ + inport_b(ide->base + IDE_STATUS); /* clear any pending interrupt */ + unlock_resource(&ide_hd_resource); + return sectors_to_write * IDE_HD_SECTSIZE; +} + +int ide_hd_ioctl(struct inode *i, int cmd, unsigned long int arg) +{ + int minor; + int drive; + struct ide *ide; + struct ide_drv_ident *ident; + struct partition *part; + struct hd_geometry *geom; + int errno; + + if(!(ide = get_ide_controller(i->rdev))) { + return -EINVAL; + } + + minor = MINOR(i->rdev); + drive = get_ide_drive(i->rdev); + if(drive) { + minor &= ~(1 << IDE_SLAVE_MSF); + } + + ident = &ide->drive[drive].ident; + part = ide->drive[drive].part_table; + + switch(cmd) { + case HDIO_GETGEO: + if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(struct hd_geometry)))) { + return errno; + } + geom = (struct hd_geometry *)arg; + geom->cylinders = ident->logic_cyls; + geom->heads = (char)ident->logic_heads; + geom->sectors = (char)ident->logic_spt; + geom->start = 0; + if(minor) { + geom->start = part[minor - 1].startsect; + } + break; + case BLKGETSIZE: + if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int)))) { + return errno; + } + if(!minor) { + *(int *)arg = (unsigned int)ide->drive[drive].nr_sects; + } else { + *(int *)arg = (unsigned int)ide->drive[drive].part_table[minor - 1].nr_sects; + } + break; + case BLKFLSBUF: + sync_buffers(i->rdev); + invalidate_buffers(i->rdev); + break; + case BLKRRPART: + read_msdos_partition(i->rdev, part); + assign_minors(i->rdev, ide, part); + break; + default: + return -EINVAL; + break; + } + + return 0; +} + +int ide_hd_init(struct ide *ide, int drive) +{ + int n; + __dev_t rdev; + struct device *d; + struct partition *part; + + rdev = 0; + ide->drive[drive].fsop = &ide_hd_driver_fsop; + part = ide->drive[drive].part_table; + + if(ide->channel == IDE_PRIMARY) { + if(!(d = get_device(BLK_DEV, IDE0_MAJOR))) { + return -EINVAL; + } + if(drive == IDE_MASTER) { + rdev = MKDEV(IDE0_MAJOR, drive); + ide->drive[drive].minor_shift = IDE_MASTER_MSF; + SET_MINOR(d->minors, 0); + ((unsigned int *)d->device_data)[0] = ide->drive[drive].nr_sects / 2; + } else { + rdev = MKDEV(IDE0_MAJOR, 1 << IDE_SLAVE_MSF); + ide->drive[drive].minor_shift = IDE_SLAVE_MSF; + SET_MINOR(d->minors, 1 << IDE_SLAVE_MSF); + ((unsigned int *)d->device_data)[1 << IDE_SLAVE_MSF] = ide->drive[drive].nr_sects / 2; + } + } else if(ide->channel == IDE_SECONDARY) { + if(!(d = get_device(BLK_DEV, IDE1_MAJOR))) { + return -EINVAL; + } + if(drive == IDE_MASTER) { + rdev = MKDEV(IDE1_MAJOR, drive); + ide->drive[drive].minor_shift = IDE_MASTER_MSF; + SET_MINOR(d->minors, 0); + ((unsigned int *)d->device_data)[0] = ide->drive[drive].nr_sects / 2; + } else { + rdev = MKDEV(IDE1_MAJOR, 1 << IDE_SLAVE_MSF); + ide->drive[drive].minor_shift = IDE_SLAVE_MSF; + SET_MINOR(d->minors, 1 << IDE_SLAVE_MSF); + ((unsigned int *)d->device_data)[1 << IDE_SLAVE_MSF] = ide->drive[drive].nr_sects / 2; + } + } else { + printk("WARNING: %s(): invalid drive number %d.\n", __FUNCTION__, drive); + return 1; + } + + read_msdos_partition(rdev, part); + assign_minors(rdev, ide, part); + printk(" partition summary: "); + for(n = 0; n < NR_PARTITIONS; n++) { + if(part[n].type) { + printk("%s%d ", ide->drive[drive].dev_name, n + 1); + } + } + printk("\n"); + + outport_b(ide->ctrl + IDE_DEV_CTRL, IDE_DEVCTR_NIEN); + if(ide->drive[drive].flags & DEVICE_HAS_RW_MULTIPLE) { + outport_b(ide->base + IDE_SECCNT, BLKSIZE_1K / IDE_HD_SECTSIZE); + outport_b(ide->base + IDE_COMMAND, ATA_SET_MULTIPLE_MODE); + ide_wait400ns(ide); + while(inport_b(ide->base + IDE_STATUS) & IDE_STAT_BSY); + } + outport_b(ide->ctrl + IDE_DEV_CTRL, IDE_DEVCTR_DRQ); + + return 0; +} diff --git a/drivers/block/part.c b/drivers/block/part.c new file mode 100644 index 00000000..85f4f728 --- /dev/null +++ b/drivers/block/part.c @@ -0,0 +1,34 @@ +/* + * fiwix/drivers/block/part.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +int read_msdos_partition(__dev_t dev, struct partition *part) +{ + char *buffer; + + if(!(buffer = (void *)kmalloc())) { + return -ENOMEM; + } + + if(ide_hd_read(dev, PARTITION_BLOCK, buffer, BLKSIZE_1K) <= 0) { + printk("WARNING: %s(): unable to read partition block in device %d,%d.\n", __FUNCTION__, MAJOR(dev), MINOR(dev)); + kfree((unsigned int)buffer); + return -EIO; + } + + memcpy_b(part, (void *)(buffer + MBR_CODE_SIZE), sizeof(struct partition) * NR_PARTITIONS); + kfree((unsigned int)buffer); + return 0; +} diff --git a/drivers/block/ramdisk.c b/drivers/block/ramdisk.c new file mode 100644 index 00000000..04be68f3 --- /dev/null +++ b/drivers/block/ramdisk.c @@ -0,0 +1,186 @@ +/* + * fiwix/drivers/block/ramdisk.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned int rd_sizes[256]; + +static struct fs_operations ramdisk_driver_fsop = { + 0, + 0, + + ramdisk_open, + ramdisk_close, + NULL, /* read */ + NULL, /* write */ + ramdisk_ioctl, + ramdisk_lseek, + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lockup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + ramdisk_read, + ramdisk_write, + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +static struct device ramdisk_device = { + "ramdisk", + -1, + RAMDISK_MAJOR, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + BLKSIZE_1K, + &rd_sizes, + &ramdisk_driver_fsop, +}; + +static struct ramdisk * get_ramdisk(int minor) +{ + if(TEST_MINOR(ramdisk_device.minors, minor)) { + return &ramdisk_table[minor]; + } + return NULL; +} + +int ramdisk_open(struct inode *i, struct fd *fd_table) +{ + if(!get_ramdisk(MINOR(i->rdev))) { + return -ENXIO; + } + return 0; +} + +int ramdisk_close(struct inode *i, struct fd *fd_table) +{ + if(!get_ramdisk(MINOR(i->rdev))) { + return -ENXIO; + } + return 0; +} + +int ramdisk_read(__dev_t dev, __blk_t block, char *buffer, int blksize) +{ + int size; + __off_t offset; + struct ramdisk *ramdisk; + + if(!(ramdisk = get_ramdisk(MINOR(dev)))) { + return -ENXIO; + } + + size = rd_sizes[MINOR(dev)] * 1024; + offset = block * blksize; + blksize = MIN(blksize, size - offset); + memcpy_b((void *)buffer, ramdisk->addr + offset, blksize); + return blksize; +} + +int ramdisk_write(__dev_t dev, __blk_t block, char *buffer, int blksize) +{ + int size; + __off_t offset; + struct ramdisk *ramdisk; + + if(!(ramdisk = get_ramdisk(MINOR(dev)))) { + return -ENXIO; + } + + size = rd_sizes[MINOR(dev)] * 1024; + offset = block * blksize; + blksize = MIN(blksize, size - offset); + memcpy_b((void *)ramdisk->addr + offset, buffer, blksize); + return blksize; +} + +int ramdisk_ioctl(struct inode *i, int cmd, unsigned long int arg) +{ + struct hd_geometry *geom; + int errno; + + if(!get_ramdisk(MINOR(i->rdev))) { + return -ENXIO; + } + + switch(cmd) { + case HDIO_GETGEO: + if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(struct hd_geometry)))) { + return errno; + } + geom = (struct hd_geometry *)arg; + geom->heads = 63; + geom->sectors = 16; + geom->cylinders = rd_sizes[MINOR(i->rdev)] * 1024 / BPS; + geom->cylinders /= (geom->heads * geom->sectors); + geom->start = 0; + break; + case BLKRRPART: + break; + case BLKGETSIZE: + if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int)))) { + return errno; + } + *(int *)arg = rd_sizes[MINOR(i->rdev)] * 2; + break; + default: + return -EINVAL; + } + return 0; +} + +int ramdisk_lseek(struct inode *i, __off_t offset) +{ + return offset; +} + +void ramdisk_init(void) +{ + int n; + struct ramdisk *ramdisk; + + if(!_noramdisk) { + for(n = 0; n < RAMDISK_MINORS; n++) { + SET_MINOR(ramdisk_device.minors, n); + rd_sizes[n] = _ramdisksize; + ramdisk = get_ramdisk(n); + memset_b((void *)ramdisk->addr, NULL, _ramdisksize * 1024); + } + printk("ram0 - %2d RAMdisk(s) of %dKB size, %dKB blocksize\n", RAMDISK_MINORS, _ramdisksize, BLKSIZE_1K / 1024); + register_device(BLK_DEV, &ramdisk_device); + } +} diff --git a/drivers/char/Makefile b/drivers/char/Makefile new file mode 100644 index 00000000..a0a98cf0 --- /dev/null +++ b/drivers/char/Makefile @@ -0,0 +1,19 @@ +# fiwix/drivers/char/Makefile +# +# Copyright 2018, Jordi Sanfeliu. All rights reserved. +# Distributed under the terms of the Fiwix License. +# + +.S.o: + $(CC) -traditional -I$(INCLUDE) -c -o $@ $< +.c.o: + $(CC) $(CFLAGS) -c -o $@ $< + +OBJS = console.o tty.o tty_queue.o vt.o defkeymap.o keyboard.o memdev.o lp.o + +char: $(OBJS) + $(LD) $(LDFLAGS) -r $(OBJS) -o char.o + +clean: + rm -f *.o + diff --git a/drivers/char/console.c b/drivers/char/console.c new file mode 100644 index 00000000..35fa8cee --- /dev/null +++ b/drivers/char/console.c @@ -0,0 +1,1168 @@ +/* + * fiwix/drivers/char/console.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CSI_J_CUR2END 0 /* clear from cursor to end of screen */ +#define CSI_J_STA2CUR 1 /* clear from start of screen to cursor */ +#define CSI_J_SCREEN 2 /* clear entire screen */ + +#define CSI_K_CUR2END 0 /* clear from cursor to end of line */ +#define CSI_K_STA2CUR 1 /* clear from start of line to cursor */ +#define CSI_K_LINE 2 /* clear entire line */ + +#define CSE vc->esc = 0 /* Code Set End */ + +#define ON 1 +#define OFF 0 + +#define SCROLL_UP 1 +#define SCROLL_DOWN 2 + +/* VT100 ID string generated by Z or [c */ +#define VT100ID "\033[?1;2c" + +/* VT100 report status generated by [5n */ +#define DEVICE_OK "\033[0n" +#define DEVICE_NOT_OK "\033[3n" + +/* ISO/IEC 8859-1:1998 (aka latin1, IBM819, CP819), same as in Linux */ +static const char *iso8859 = + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\377\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376" + "\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250" + "\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376" + "\376\245\376\376\376\376\231\376\350\376\376\376\232\376\376\341" + "\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213" + "\376\244\225\242\223\376\224\366\355\227\243\226\201\376\376\230" +; + +unsigned short int *video_base_address; +short int current_cons; +unsigned char screen_is_off = 0; +int buf_y, buf_top; + +struct vconsole vc[NR_VCONSOLES + 1]; +unsigned short int vcbuf[VC_BUF_SIZE]; + +static struct fs_operations tty_driver_fsop = { + 0, + 0, + + tty_open, + tty_close, + tty_read, + tty_write, + tty_ioctl, + tty_lseek, + NULL, /* readdir */ + NULL, /* mmap */ + tty_select, + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lookup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +static struct device tty_device = { + "vconsole", + KEYBOARD_IRQ, + VCONSOLES_MAJOR, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + 0, + NULL, + &tty_driver_fsop, +}; + +static struct device console_device = { + "console", + KEYBOARD_IRQ, + SYSCON_MAJOR, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + 0, + NULL, + &tty_driver_fsop, +}; + +unsigned short int ansi_color_table[] = { + COLOR_BLACK, + COLOR_RED, + COLOR_GREEN, + COLOR_BROWN, + COLOR_BLUE, + COLOR_MAGENTA, + COLOR_CYAN, + COLOR_WHITE +}; + +static void update_curpos(struct vconsole *vc) +{ + unsigned short int curpos; + + if(vc->has_focus) { + curpos = (vc->y * vc->columns) + vc->x; + outport_b(video_port + CRT_INDEX, CRT_CURSOR_POS_HI); + outport_b(video_port + CRT_DATA, (curpos >> 8) & 0xFF); + outport_b(video_port + CRT_INDEX, CRT_CURSOR_POS_LO); + outport_b(video_port + CRT_DATA, (curpos & 0xFF)); + } +} + +static void show_cursor(int mode) +{ + int status; + + switch(mode) { + case ON: + outport_b(video_port + CRT_INDEX, CRT_CURSOR_STR); + status = inport_b(video_port + CRT_DATA); + outport_b(video_port + CRT_DATA, status & CURSOR_MASK); + break; + case OFF: + outport_b(video_port + CRT_INDEX, CRT_CURSOR_STR); + status = inport_b(video_port + CRT_DATA); + outport_b(video_port + CRT_DATA, status | CURSOR_DISABLE); + break; + } +} + +static void get_curpos(struct vconsole *vc) +{ + unsigned short int curpos; + + outport_b(video_port + CRT_INDEX, CRT_CURSOR_POS_HI); + curpos = inport_b(video_port + CRT_DATA) << 8; + outport_b(video_port + CRT_INDEX, CRT_CURSOR_POS_LO); + curpos |= inport_b(video_port + CRT_DATA); + + vc->x = curpos % vc->columns; + vc->y = curpos / vc->columns; +} + +static void adjust(struct vconsole *vc, int x, int y) +{ + if(x < 0) { + x = 0; + } + if(x >= vc->columns) { + x = vc->columns - 1; + } + if(y < 0) { + y = 0; + } + if(y >= vc->lines) { + y = vc->lines - 1; + } + vc->x = x; + vc->y = y; +} + +static void delete_char(struct vconsole *vc) +{ + int n, from; + + from = (vc->y * vc->columns) + vc->x; + n = vc->x; + while(++n < vc->columns) { + memcpy_w(vc->vidmem + from, vc->vidmem + from + 1, 1); + from++; + } + memset_w(vc->vidmem + from, BLANK_MEM, 1); +} + +static void insert_char(struct vconsole *vc) +{ + int n, from; + unsigned short int tmp, last_char; + + from = (vc->y * vc->columns) + vc->x; + n = vc->x + 1; + last_char = BLANK_MEM; + while(++n < vc->columns) { + memcpy_w(&tmp, vc->vidmem + from, 1); + memset_w(vc->vidmem + from, last_char, 1); + last_char = tmp; + from++; + } +} + +/* FIXME: scrolling inside a text window (ESC[5;8r) is not supported yet */ +static void scroll_screen(struct vconsole *vc, int top, int mode) +{ + int n, count, from; + + switch(mode) { + case SCROLL_DOWN: + count = (vc->columns * (vc->lines - top - 1)) * 2; + from = top * vc->columns; + top = (top + 1) * vc->columns; + memcpy_b(vc->vidmem + from, vc->vidmem + top, count); + memset_w(vc->vidmem + from + (count / 2), BLANK_MEM, (top * 2) / sizeof(unsigned short int)); + break; + case SCROLL_UP: + count = vc->columns * 2; + for(n = vc->lines - 1; n >= top; n--) { + memcpy_b(vc->vidmem + (vc->columns * (n + 1)), vc->vidmem + (vc->columns * n), count); + } + memset_w(vc->vidmem + (top * vc->columns), BLANK_MEM, count / sizeof(unsigned short int)); + break; + } + return; +} + +static void cr(struct vconsole *vc) +{ + vc->x = 0; +} + +static void lf(struct vconsole *vc) +{ + if(vc->y == vc->lines) { + scroll_screen(vc, 0, SCROLL_DOWN); + } else { + vc->y++; + } +} + +static void ri(struct vconsole *vc) +{ + if(vc->y == 0) { + scroll_screen(vc, vc->y, SCROLL_UP); + } else { + vc->y--; + } +} + +static void csi_J(struct vconsole *vc, int mode) +{ + int from, count; + + switch(mode) { + case CSI_J_CUR2END: /* Erase Down [J */ + from = (vc->y * vc->columns) + vc->x; + count = (SCREEN_SIZE - from) / sizeof(unsigned short int); + break; + case CSI_J_STA2CUR: /* Erase Up [1J */ + from = 0; + count = (((vc->y * vc->columns) + vc->x) * 2) / sizeof(unsigned short int); + break; + case CSI_J_SCREEN: /* Erase Screen [2J */ + from = 0; + count = SCREEN_SIZE / sizeof(unsigned short int); + break; + default: + return; + } + memset_w(vc->vidmem + from, vc->color_attr, count); +} + +static void csi_K(struct vconsole *vc, int mode) +{ + int from, count; + + switch(mode) { + case CSI_K_CUR2END: /* Erase End of Line [K */ + from = (vc->y * vc->columns) + vc->x; + count = ((vc->columns - vc->x) * 2) / sizeof(unsigned short int); + break; + case CSI_K_STA2CUR: /* Erase Start of Line [1K */ + from = vc->y * vc->columns; + count = (vc->x * 2) / sizeof(unsigned short int); + break; + case CSI_K_LINE: /* Erase Line [2K */ + from = vc->y * vc->columns; + count = (vc->columns * 2) / sizeof(unsigned short int); + break; + default: + return; + } + memset_w(vc->vidmem + from, vc->color_attr, count); +} + +static void csi_L(struct vconsole *vc, int count) +{ + if(count > vc->lines) { + count = vc->lines; + } + while(count--) { + scroll_screen(vc, vc->y, SCROLL_UP); + } +} + +static void csi_M(struct vconsole *vc, int count) +{ + if(count > vc->lines) { + count = vc->lines; + } + while(count--) { + scroll_screen(vc, vc->y, SCROLL_DOWN); + } +} + +static void csi_P(struct vconsole *vc, int count) +{ + if(count > vc->columns) { + count = vc->columns; + } + while(count--) { + delete_char(vc); + } +} + +static void csi_at(struct vconsole *vc, int count) +{ + if(count > vc->columns) { + count = vc->columns; + } + while(count--) { + insert_char(vc); + } +} + +static void default_color_attr(struct vconsole *vc) +{ + vc->color_attr = DEF_MODE; + vc->bold = 0; + vc->underline = 0; + vc->blink = 0; + vc->reverse = 0; +} + +static void csi_m(struct vconsole *vc) +{ + if(vc->reverse) { + vc->color_attr = ((vc->color_attr & 0x7000) >> 4) | ((vc->color_attr & 0x0700) << 4) | (vc->color_attr & 0x8800); + } + + switch(vc->parmv1) { + case COLOR_NORMAL: + default_color_attr(vc); + break; + case COLOR_BOLD: + vc->bold = 1; + break; + case COLOR_BOLD_OFF: + vc->bold = 0; + break; + case COLOR_BLINK: + vc->blink = 1; + break; + case COLOR_REVERSE: + vc->reverse = 1; + break; + case 21: + case 22: + vc->bold = 1; + break; + case 25: + vc->blink = 0; + break; + case 27: + vc->reverse = 0; + break; + } + if(vc->parmv1 >= 30 && vc->parmv1 <= 37) { + vc->color_attr = (vc->color_attr & 0xF8FF) | (ansi_color_table[vc->parmv1 - 30]); + } + if(vc->parmv1 >= 40 && vc->parmv1 <= 47) { + vc->color_attr = (vc->color_attr & 0x8FFF) | ((ansi_color_table[vc->parmv1 - 40]) << 4); + } + if(vc->parmv2 >= 30 && vc->parmv2 <= 37) { + vc->color_attr = (vc->color_attr & 0xF8FF) | (ansi_color_table[vc->parmv2 - 30]); + } + if(vc->parmv2 >= 40 && vc->parmv2 <= 47) { + vc->color_attr = (vc->color_attr & 0x8FFF) | ((ansi_color_table[vc->parmv2 - 40]) << 4); + } + if(vc->bold) { + vc->color_attr |= 0x0800; + } + if(vc->blink) { + vc->color_attr |= 0x8000; + } + if(vc->reverse) { + vc->color_attr = ((vc->color_attr & 0x7000) >> 4) | ((vc->color_attr & 0x0700) << 4) | (vc->color_attr & 0x8800); + } +} + +static void init_vt(struct vconsole *vc) +{ + vc->vt_mode.mode = VT_AUTO; + vc->vt_mode.waitv = 0; + vc->vt_mode.relsig = 0; + vc->vt_mode.acqsig = 0; + vc->vt_mode.frsig = 0; + vc->vc_mode = KD_TEXT; + vc->tty->pid = 0; + vc->switchto_tty = -1; +} + +static void insert_seq(struct tty *tty, char *buf, int count) +{ + while(count--) { + tty_queue_putchar(tty, &tty->read_q, *(buf++)); + } + tty->input(tty); +} + +static void echo_char(struct vconsole *vc, unsigned char *buf, unsigned int count) +{ + int n; + unsigned char ch; + + if(vc->has_focus) { + if(buf_top) { + vconsole_restore(vc); + show_cursor(ON); + buf_top = 0; + } + } + + while(count--) { + ch = *buf++; + if(ch == NULL) { + continue; + + } else if(ch == '\b') { + if(vc->x) { + vc->x--; + } + + } else if(ch == '\a') { + vconsole_beep(); + + } else if(ch == '\r') { + cr(vc); + + } else if(ch == '\n') { + cr(vc); + vc->y++; + if(vc->has_focus) { + buf_y++; + } + + } else if(ch == '\t') { + while(vc->x < (vc->columns - 1)) { + if(vc->tab_stop[++vc->x]) { + break; + } + } +/* vc->x += TAB_SIZE - (vc->x % TAB_SIZE); */ + vc->check_x = 1; + + } else { + if((vc->x == vc->columns - 1) && vc->check_x) { + vc->x = 0; + vc->y++; + if(vc->has_focus) { + buf_y++; + } + } + if(vc->y >= vc->lines) { + scroll_screen(vc, 0, SCROLL_DOWN); + vc->y--; + } + ch = iso8859[ch]; + vc->vidmem[(vc->y * vc->columns) + vc->x] = vc->color_attr | ch; + if(vc->has_focus) { + vcbuf[(buf_y * vc->columns) + vc->x] = vc->color_attr | ch; + } + if(vc->x < vc->columns - 1) { + vc->check_x = 0; + vc->x++; + } else { + vc->check_x = 1; + } + } + if(vc->y >= vc->lines) { + scroll_screen(vc, 0, SCROLL_DOWN); + vc->y--; + } + if(vc->has_focus) { + if(buf_y >= VC_BUF_LINES) { + memcpy_b(vcbuf, vcbuf + SCREEN_COLS, VC_BUF_SIZE - (SCREEN_COLS * 2)); + for(n = (SCREEN_COLS * (VC_BUF_LINES - 1)); n < (SCREEN_COLS * VC_BUF_LINES); n++) { + vcbuf[n] = BLANK_MEM; + } + buf_y--; + } + } + } + update_curpos(vc); +} + +void vconsole_reset(struct tty *tty) +{ + int n; + struct vconsole *vc; + + vc = (struct vconsole *)tty->driver_data; + + vc->lines = SCREEN_LINES; + vc->columns = SCREEN_COLS; + vc->check_x = 0; + vc->led_status = 0; + set_leds(vc->led_status); + vc->scrlock = vc->numlock = vc->capslock = 0; + vc->esc = vc->sbracket = vc->semicolon = vc->question = 0; + vc->parmv1 = vc->parmv2 = 0; + default_color_attr(vc); + vc->insert_mode = 0; + vc->saved_x = vc->saved_y = 0; + + for(n = 0; n < MAX_TAB_COLS; n++) { + if(!(n % TAB_SIZE)) { + vc->tab_stop[n] = 1; + } else { + vc->tab_stop[n] = 0; + } + } + + termios_reset(tty); + vc->tty->winsize.ws_row = vc->lines; + vc->tty->winsize.ws_col = vc->columns; + vc->tty->winsize.ws_xpixel = 0; + vc->tty->winsize.ws_ypixel = 0; + vc->tty->lnext = 0; + + init_vt(vc); + vc->blanked = 0; + update_curpos(vc); +} + +void vconsole_write(struct tty *tty) +{ + int n; + unsigned char ch; + int numeric; + struct vconsole *vc; + + vc = (struct vconsole *)tty->driver_data; + + if(buf_top) { + vconsole_restore(vc); + buf_top = 0; + show_cursor(ON); + update_curpos(vc); + } + + numeric = 0; + + while(!vc->scrlock && tty->write_q.count > 0) { + ch = tty_queue_getchar(&tty->write_q); + + if(vc->esc) { + if(vc->sbracket) { + if(IS_NUMERIC(ch)) { + numeric = 1; + if(vc->semicolon) { + vc->parmv2 *= 10; + vc->parmv2 += ch - '0'; + } else { + vc->parmv1 *= 10; + vc->parmv1 += ch - '0'; + } + continue; + } + switch(ch) { + case ';': + vc->semicolon = 1; + vc->parmv2 = 0; + continue; + case '?': + vc->question = 1; + continue; + case 'A': /* Cursor Up [{COUNT}A */ + vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; + adjust(vc, vc->x, vc->y - vc->parmv1); + CSE; + continue; + case 'B': /* Cursor Down [{COUNT}B */ + vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; + adjust(vc, vc->x, vc->y + vc->parmv1); + CSE; + continue; + case 'C': /* Cursor Forward [{COUNT}C */ + vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; + adjust(vc, vc->x + vc->parmv1, vc->y); + CSE; + continue; + case 'D': /* Cursor Backward [{COUNT}D */ + vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; + adjust(vc, vc->x - vc->parmv1, vc->y); + CSE; + continue; + case 'E': /* Cursor Next Line(s) [{COUNT}E */ + vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; + adjust(vc, 0, vc->y + vc->parmv1); + CSE; + continue; + case 'F': /* Cursor Previous Line(s) [{COUNT}F */ + vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; + adjust(vc, 0, vc->y - vc->parmv1); + CSE; + continue; + case 'G': /* Cursor Horizontal Position [{NUM1}G */ + case '`': + vc->parmv1 = vc->parmv1 ? vc->parmv1 - 1 : vc->parmv1; + adjust(vc, vc->parmv1, vc->y); + CSE; + continue; + case 'H': /* Cursor Home [{ROW};{COLUMN}H */ + case 'f': /* Force Cursor Position [{ROW};{COLUMN}f */ + vc->parmv1 = vc->parmv1 ? vc->parmv1 - 1 : vc->parmv1; + vc->parmv2 = vc->parmv2 ? vc->parmv2 - 1 : vc->parmv2; + adjust(vc, vc->parmv2, vc->parmv1); + CSE; + continue; + case 'J': /* Erase (Down/Up/Screen) [J */ + csi_J(vc, vc->parmv1); + CSE; + continue; + case 'K': /* Erase (End of/Start of/) Line [K */ + csi_K(vc, vc->parmv1); + CSE; + continue; + case 'L': /* Insert Line(s) [{COUNT}L */ + vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; + csi_L(vc, vc->parmv1); + CSE; + continue; + case 'M': /* Delete Line(s) [{COUNT}M */ + vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; + csi_M(vc, vc->parmv1); + CSE; + continue; + case 'P': /* Delete Character(s) [{COUNT}P */ + vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; + csi_P(vc, vc->parmv1); + CSE; + continue; + case '@': /* Insert Character(s) [{COUNT}@ */ + vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; + csi_at(vc, vc->parmv1); + CSE; + continue; + case 'c': /* Query Device Code [c */ + if(!numeric) { + insert_seq(tty, VT100ID, 7); + } + CSE; + continue; + case 'd': /* Cursor Vertical Position [{NUM1}d */ + vc->parmv1 = vc->parmv1 ? vc->parmv1 - 1 : vc->parmv1; + adjust(vc, vc->x, vc->parmv1); + CSE; + continue; + case 'g': + switch(vc->parmv1) { + case 0: /* Clear Tab [g */ + vc->tab_stop[vc->x] = 0; + break; + case 3: /* Clear All Tabs [3g */ + for(n = 0; n < MAX_TAB_COLS; n++) + vc->tab_stop[n] = 0; + break; + } + CSE; + continue; + case 'h': + if(vc->question) { + switch(vc->parmv1) { + case 25: /* Switch Cursor Visible [?25h */ + show_cursor(ON); + break; + case 4: + vc->insert_mode = ON; /* not used */ + break; + } + } + CSE; + continue; + case 'l': + if(vc->question) { + switch(vc->parmv1) { + case 25: /* Switch Cursor Invisible [?25l */ + show_cursor(OFF); + break; + case 4: + vc->insert_mode = OFF; /* not used */ + break; + } + } + CSE; + continue; + case 'm': /* Character Attributes {NUM1}{NUM2}m */ + csi_m(vc); + CSE; + continue; + case 'n': + if(!vc->question) { + switch(vc->parmv1) { + case 5: /* Query Device Status [5n */ + insert_seq(tty, DEVICE_OK, 4); + break; + case 6: /* Query Cursor Position [6n */ + { + char curpos[8]; + char len; + len = sprintk(curpos, "\033[%d;%dR", vc->y, vc->x); + insert_seq(tty, curpos, len); + } + break; + } + } + CSE; + continue; + case 'r': /* Scroll Screen [r / [{start};{end}r */ + if(!vc->parmv1) { + vc->parmv1++; + } + if(!vc->parmv2) { + vc->parmv2 = SCREEN_LINES; + } + if(vc->parmv1 < vc->parmv2 && vc->parmv2 <= SCREEN_LINES) { + /* FIXME: text window not supported yet */ + adjust(vc, 0, 0); + } + CSE; + continue; + case 's': /* Save Cursor [s */ + vc->saved_x = vc->x; + vc->saved_y = vc->y; + CSE; + continue; + case 'u': /* Restore Cursor [u */ + vc->x = vc->saved_x; + vc->y = vc->saved_y; + CSE; + continue; + default: + CSE; + break; + } + } else { + switch(ch) { + case '[': + vc->sbracket = 1; + vc->semicolon = 0; + vc->question = 0; + vc->parmv1 = vc->parmv2 = 0; + continue; + case '7': /* Save Cursor & Attrs 7 */ + vc->saved_x = vc->x; + vc->saved_y = vc->y; + CSE; + continue; + case '8': /* Restore Cursor & Attrs 8 */ + vc->x = vc->saved_x; + vc->y = vc->saved_y; + CSE; + continue; + case 'D': /* Scroll Down D */ + lf(vc); + CSE; + continue; + case 'E': /* Move To Next Line E */ + cr(vc); + lf(vc); + CSE; + continue; + case 'H': /* Set Tab H */ + vc->tab_stop[vc->x] = 1; + CSE; + continue; + case 'M': /* Scroll Up M */ + ri(vc); + CSE; + continue; + case 'Z': /* Identify Terminal Z */ + insert_seq(tty, VT100ID, 7); + CSE; + continue; + case 'c': /* Reset Device c */ + vconsole_reset(vc->tty); + vc->x = vc->y = 0; + csi_J(vc, CSI_J_SCREEN); + CSE; + continue; + default: + CSE; + break; + } + } + } + switch(ch) { + case '\033': + vc->esc = 1; + vc->sbracket = 0; + vc->semicolon = 0; + vc->question = 0; + vc->parmv1 = vc->parmv2 = 0; + continue; + default: + echo_char(vc, &ch, 1); + continue; + } + } + if(vc->vc_mode != KD_GRAPHICS) { + update_curpos(vc); + } + wakeup(&tty->write_q); +} + +void vconsole_select(int new_cons) +{ + new_cons++; + if(current_cons != new_cons) { + if(vc[current_cons].vt_mode.mode == VT_PROCESS) { + if(!kill_pid(vc[current_cons].tty->pid, vc[current_cons].vt_mode.acqsig)) { + vc[current_cons].switchto_tty = new_cons; + return; + } + init_vt(&vc[current_cons]); + } + if(vc[current_cons].vc_mode == KD_GRAPHICS) { + return; + } + vconsole_select_final(new_cons); + } +} + +void vconsole_select_final(int new_cons) +{ + if(current_cons != new_cons) { + if(vc[new_cons].vt_mode.mode == VT_PROCESS) { + if(kill_pid(vc[new_cons].tty->pid, vc[new_cons].vt_mode.acqsig)) { + init_vt(&vc[new_cons]); + } + } + if(buf_top) { + vconsole_restore(&vc[current_cons]); + buf_top = 0; + update_curpos(&vc[current_cons]); + } + if(vc[current_cons].vc_mode != KD_GRAPHICS) { + vconsole_save(&vc[current_cons]); + } + vc[current_cons].vidmem = vc[current_cons].scrbuf; + vc[current_cons].has_focus = 0; + vc[new_cons].vidmem = video_base_address; + vc[new_cons].has_focus = 1; + vconsole_restore(&vc[new_cons]); + current_cons = new_cons; + set_leds(vc[current_cons].led_status); + update_curpos(&vc[current_cons]); + + buf_y = vc[current_cons].y; + buf_top = 0; + memset_w(vcbuf, BLANK_MEM, VC_BUF_SIZE / sizeof(unsigned short int)); + memcpy_b(vcbuf, vc[current_cons].vidmem, SCREEN_SIZE); + show_cursor(ON); + } +} + +void vconsole_save(struct vconsole *vc) +{ + memcpy_b(vc->scrbuf, vc->vidmem, SCREEN_SIZE); +} + +void vconsole_restore(struct vconsole *vc) +{ + memcpy_b(vc->vidmem, vc->scrbuf, SCREEN_SIZE); +} + +void vconsole_buffer_scrl(int mode) +{ + int buf_line = buf_y; + + if(buf_line <= SCREEN_LINES) { + return; + } + if(mode == VC_BUF_UP) { + if(buf_top < 0) { + return; + } + if(!buf_top) { + vconsole_save(&vc[current_cons]); + buf_top = (buf_line - SCREEN_LINES + 1) * SCREEN_COLS; + buf_top -= (SCREEN_LINES / 2) * SCREEN_COLS; + } else { + buf_top -= (SCREEN_LINES / 2) * SCREEN_COLS; + } + if(buf_top < 0) { + buf_top = 0; + } + memcpy_b(vc[current_cons].vidmem, vcbuf + buf_top, SCREEN_SIZE); + if(!buf_top) { + buf_top = -1; + } + show_cursor(OFF); + return; + } + if(mode == VC_BUF_DOWN) { + if(!buf_top) { + return; + } + if(buf_top == buf_line * SCREEN_COLS) { + return; + } + if(buf_top < 0) { + buf_top = 0; + } + buf_top += (SCREEN_LINES / 2) * SCREEN_COLS; + if(buf_top >= (buf_line - SCREEN_LINES + 1) * SCREEN_COLS) { + vconsole_restore(&vc[current_cons]); + buf_top = 0; + show_cursor(ON); + update_curpos(&vc[current_cons]); + return; + } + memcpy_b(vc[current_cons].vidmem, vcbuf + buf_top, SCREEN_SIZE); + return; + } +} + +void blank_screen(struct vconsole *vc) +{ + if(vc->blanked) { + return; + } + vconsole_save(vc); + memset_w(vc->vidmem, BLANK_MEM, SCREEN_SIZE / sizeof(short int)); + vc->blanked = 1; + show_cursor(OFF); +} + +void unblank_screen(struct vconsole *vc) +{ + if(!vc->blanked) { + return; + } + vconsole_restore(vc); + vc->blanked = 0; + show_cursor(ON); +} + +void screen_on(void) +{ + unsigned long int flags; + struct callout_req creq; + + if(screen_is_off) { + SAVE_FLAGS(flags); CLI(); + inport_b(INPUT_STAT1); + inport_b(0x3BA); + outport_b(ATTR_CONTROLLER, ATTR_CONTROLLER_PAS); + RESTORE_FLAGS(flags); + } + creq.fn = screen_off; + creq.arg = 0; + add_callout(&creq, BLANK_INTERVAL); +} + +void screen_off(unsigned int arg) +{ + unsigned long int flags; + + screen_is_off = 1; + SAVE_FLAGS(flags); CLI(); + inport_b(INPUT_STAT1); + inport_b(0x3BA); + outport_b(ATTR_CONTROLLER, 0); + RESTORE_FLAGS(flags); +} + +void vconsole_start(struct tty *tty) +{ + struct vconsole *vc; + + vc = (struct vconsole *)tty->driver_data; + if(!vc->scrlock) { + return; + } + vc->led_status &= ~SCRLBIT; + vc->scrlock = 0; + set_leds(vc->led_status); +} + +void vconsole_stop(struct tty *tty) +{ + struct vconsole *vc; + + vc = (struct vconsole *)tty->driver_data; + if(vc->scrlock) { + return; + } + vc->led_status |= SCRLBIT; + vc->scrlock = 1; + set_leds(vc->led_status); +} + +void vconsole_beep(void) +{ + struct callout_req creq; + + pit_beep_on(); + creq.fn = pit_beep_off; + creq.arg = 0; + add_callout(&creq, HZ / 8); +} + +void vconsole_deltab(struct tty *tty) +{ + unsigned short int col, n; + unsigned char count; + struct vconsole *vc; + struct cblock *cb; + unsigned char ch; + + vc = (struct vconsole *)tty->driver_data; + cb = tty->cooked_q.head; + col = count = 0; + + while(cb) { + for(n = 0; n < cb->end_off; n++) { + if(n >= cb->start_off) { + ch = cb->data[n]; + if(ch == '\t') { + while(!vc->tab_stop[++col]); + } else { + col++; + if(ISCNTRL(ch) && !ISSPACE(ch) && tty->termios.c_lflag & ECHOCTL) { + col++; + } + } + col %= vc->columns; + } + } + cb = cb->next; + } + count = vc->x - col; + + while(count--) { + tty_queue_putchar(tty, &tty->write_q, '\b'); + } +} + +void console_flush_log_buf(char *buffer, unsigned int count) +{ + char *b; + struct tty *tty; + + tty = get_tty(_syscondev); + b = buffer; + + while(count) { + if(tty_queue_putchar(tty, &tty->write_q, *b) < 0) { + tty->output(tty); + continue; + } + count--; + b++; + } + tty->output(tty); +} + +void vconsole_init(void) +{ + int n; + struct tty *tty; + + printk("console"); + if((*(unsigned short int *)0x410 & 0x30) == 0x30) { + /* monochrome = 0x30 */ + video_base_address = (void *)MONO_ADDR; + video_port = MONO_6845_ADDR; + printk(" 0x%04X-0x%04X - VGA monochrome 80x25", video_port, video_port + 1); + } else { + /* color = 0x00 || 0x20 */ + video_base_address = (void *)COLOR_ADDR; + video_port = COLOR_6845_ADDR; + printk(" 0x%04X-0x%04X - VGA color 80x25", video_port, video_port + 1); + } + + printk(" (%d virtual consoles)\n", NR_VCONSOLES); + screen_on(); + + for(n = 1; n < NR_VCONSOLES + 1; n++) { + if(!register_tty(MKDEV(VCONSOLES_MAJOR, n))) { + tty = get_tty(MKDEV(VCONSOLES_MAJOR, n)); + tty->driver_data = (void *)&vc[n]; + tty->stop = vconsole_stop; + tty->start = vconsole_start; + tty->deltab = vconsole_deltab; + tty->reset = vconsole_reset; + tty->input = do_cook; + tty->output = vconsole_write; + vc[n].tty = tty; + vc[n].vidmem = vc[n].scrbuf; + memset_w(vc[n].scrbuf, BLANK_MEM, SCREEN_SIZE / sizeof(short int)); + vconsole_reset(tty); + termios_reset(tty); + tty_queue_init(tty); + } + } + current_cons = 1; + vc[current_cons].vidmem = video_base_address; + vc[current_cons].has_focus = 1; +// vc[current_cons].count++; /* XXX */ + +/* memset_b(vc[current_cons].vidmem, BLANK_MEM, SCREEN_SIZE); */ + memcpy_b(vcbuf, vc[current_cons].vidmem, SCREEN_SIZE); + + get_curpos(&vc[current_cons]); + update_curpos(&vc[current_cons]); + buf_y = vc[current_cons].y; + buf_top = 0; + + register_device(CHR_DEV, &console_device); + register_device(CHR_DEV, &tty_device); + register_console(console_flush_log_buf); +} diff --git a/drivers/char/defkeymap.c b/drivers/char/defkeymap.c new file mode 100644 index 00000000..c28b174e --- /dev/null +++ b/drivers/char/defkeymap.c @@ -0,0 +1,149 @@ +/* + * fiwix/drivers/char/defkeymap.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include + +#define BS 127 /* backspace */ + +__key_t keymap[NR_MODIFIERS * NR_SCODES] = { +/* + * Standard US keyboard (default keymap) with 16 modifiers + * Shift + * Shift Shift Shift AltGr AltGr + * SCAN Shift Shift AltGr AltGr Shift AltGr AltGr Ctrl Ctrl Ctrl Ctrl + * CODE KEY Base Shift AltGr AltGr Ctrl Ctrl Ctrl Ctrl Alt Alt Alt Alt Alt Alt Alt Alt + * ==================================================================================================================================================== */ +/* 00 - NULL */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 01 - ESC */ C('['), C('['), NULL, NULL, NULL, 0, 0, 0, A('['), 0, 0, 0, 0, 0, 0, 0, +/* 02 - 1 */ '1', '!', NULL, NULL, NULL, 0, 0, 0, A('1'), 0, 0, 0, 0, 0, 0, 0, +/* 03 - 2 */ '2', '@', '@', NULL, NULL, 0, 0, 0, A('2'), 0, 0, 0, 0, 0, 0, 0, +/* 04 - 3 */ '3', '#', NULL, NULL, C('['), 0, 0, 0, A('3'), 0, 0, 0, 0, 0, 0, 0, +/* 05 - 4 */ '4', '$', '$', NULL, C('\\'),0, 0, 0, A('4'), 0, 0, 0, 0, 0, 0, 0, +/* 06 - 5 */ '5', '%', NULL, NULL, C(']'), 0, 0, 0, A('5'), 0, 0, 0, 0, 0, 0, 0, +/* 07 - 6 */ '6', '^', NULL, NULL, C('^'), 0, 0, 0, A('6'), 0, 0, 0, 0, 0, 0, 0, +/* 08 - 7 */ '7', '&', '{', NULL, C('_'), 0, 0, 0, A('7'), 0, 0, 0, 0, 0, 0, 0, +/* 09 - 8 */ '8', '*', '[', NULL, BS, 0, 0, 0, A('8'), 0, 0, 0, 0, 0, 0, 0, +/* 10 - 9 */ '9', '(', ']', NULL, NULL, 0, 0, 0, A('9'), 0, 0, 0, 0, 0, 0, 0, +/* 11 - 0 */ '0', ')', '}', NULL, NULL, 0, 0, 0, A('0'), 0, 0, 0, 0, 0, 0, 0, +/* 12 - -_ */ '-', '_', '\\', NULL, C('_'), 0, 0, 0, A('-'), 0, 0, 0, 0, 0, 0, 0, +/* 13 - =+ */ '=', '+', NULL, NULL, NULL, 0, 0, 0, A('='), 0, 0, 0, 0, 0, 0, 0, +/* 14 - BS */ BS, BS, NULL, NULL, NULL, 0, 0, 0, A(BS), 0, 0, 0, 0, 0, 0, 0, +/* 15 - TAB */ '\t', '\t', NULL, NULL, NULL, 0, 0, 0, A('\t'),0, 0, 0, 0, 0, 0, 0, +/* 16 - q */ L('q'), L('Q'), L('q'), L('Q'), C('Q'), 0, 0, 0, A('q'), 0, 0, 0, 0, 0, 0, 0, +/* 17 - w */ L('w'), L('W'), L('w'), L('W'), C('W'), 0, 0, 0, A('w'), 0, 0, 0, 0, 0, 0, 0, +/* 18 - e */ L('e'), L('E'), L('e'), L('E'), C('E'), 0, 0, 0, A('e'), 0, 0, 0, 0, 0, 0, 0, +/* 19 - r */ L('r'), L('R'), L('r'), L('R'), C('R'), 0, 0, 0, A('r'), 0, 0, 0, 0, 0, 0, 0, +/* 20 - t */ L('t'), L('T'), L('t'), L('T'), C('T'), 0, 0, 0, A('t'), 0, 0, 0, 0, 0, 0, 0, +/* 21 - y */ L('y'), L('Y'), L('y'), L('Y'), C('Y'), 0, 0, 0, A('y'), 0, 0, 0, 0, 0, 0, 0, +/* 22 - u */ L('u'), L('U'), L('u'), L('U'), C('U'), 0, 0, 0, A('u'), 0, 0, 0, 0, 0, 0, 0, +/* 23 - i */ L('i'), L('I'), L('i'), L('I'), C('I'), 0, 0, 0, A('i'), 0, 0, 0, 0, 0, 0, 0, +/* 24 - o */ L('o'), L('O'), L('o'), L('O'), C('O'), 0, 0, 0, A('o'), 0, 0, 0, 0, 0, 0, 0, +/* 25 - p */ L('p'), L('P'), L('p'), L('P'), C('P'), 0, 0, 0, A('p'), 0, 0, 0, 0, 0, 0, 0, +/* 26 - [{ */ '[', '{', NULL, NULL, C('['), 0, 0, 0, A('['), 0, 0, 0, 0, 0, 0, 0, +/* 27 - ]} */ ']', '}', '~', NULL, C(']'), 0, 0, 0, A(']'), 0, 0, 0, 0, 0, 0, 0, +/* 28 - CR */ CR, CR, CR, CR, CR, 0, 0, 0, A(CR), 0, 0, 0, 0, 0, 0, 0, +/* 29 - CTRL */ CTRL, CTRL, CTRL, CTRL, CTRL, 0, 0, 0, CTRL, 0, 0, 0, 0, 0, 0, 0, +/* 30 - a */ L('a'), L('A'), L('a'), L('A'), C('A'), 0, 0, 0, A('a'), 0, 0, 0, 0, 0, 0, 0, +/* 31 - s */ L('s'), L('S'), L('s'), L('S'), C('S'), 0, 0, 0, A('s'), 0, 0, 0, 0, 0, 0, 0, +/* 32 - d */ L('d'), L('D'), L('d'), L('D'), C('D'), 0, 0, 0, A('d'), 0, 0, 0, 0, 0, 0, 0, +/* 33 - f */ L('f'), L('F'), L('f'), L('F'), C('F'), 0, 0, 0, A('f'), 0, 0, 0, 0, 0, 0, 0, +/* 34 - g */ L('g'), L('G'), L('g'), L('G'), C('G'), 0, 0, 0, A('g'), 0, 0, 0, 0, 0, 0, 0, +/* 35 - h */ L('h'), L('H'), L('h'), L('H'), C('H'), 0, 0, 0, A('h'), 0, 0, 0, 0, 0, 0, 0, +/* 36 - j */ L('j'), L('J'), L('j'), L('J'), C('J'), 0, 0, 0, A('j'), 0, 0, 0, 0, 0, 0, 0, +/* 37 - k */ L('k'), L('K'), L('k'), L('K'), C('K'), 0, 0, 0, A('k'), 0, 0, 0, 0, 0, 0, 0, +/* 38 - l */ L('l'), L('L'), L('l'), L('L'), C('L'), 0, 0, 0, A('l'), 0, 0, 0, 0, 0, 0, 0, +/* 39 - ;: */ ';', ':', NULL, NULL, NULL, 0, 0, 0, A(';'), 0, 0, 0, 0, 0, 0, 0, +/* 40 - '" */ '\'', '"', NULL, NULL, C('G'), 0, 0, 0, A('\''),0, 0, 0, 0, 0, 0, 0, +/* 41 - `~ */ '`', '~', NULL, NULL, NULL, 0, 0, 0, A('`'), 0, 0, 0, 0, 0, 0, 0, +/* 42 - LSHF */ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, 0, 0, 0, SHIFT, 0, 0, 0, 0, 0, 0, 0, +/* 43 - \| */ '\\', '|', NULL, NULL, C('\\'),0, 0, 0, A('\\'),0, 0, 0, 0, 0, 0, 0, +/* 44 - z */ L('z'), L('Z'), L('z'), L('Z'), C('Z'), 0, 0, 0, A('z'), 0, 0, 0, 0, 0, 0, 0, +/* 45 - x */ L('x'), L('X'), L('x'), L('X'), C('X'), 0, 0, 0, A('x'), 0, 0, 0, 0, 0, 0, 0, +/* 46 - c */ L('c'), L('C'), L('c'), L('C'), C('C'), 0, 0, 0, A('c'), 0, 0, 0, 0, 0, 0, 0, +/* 47 - v */ L('v'), L('V'), L('v'), L('V'), C('V'), 0, 0, 0, A('v'), 0, 0, 0, 0, 0, 0, 0, +/* 48 - b */ L('b'), L('B'), L('b'), L('B'), C('B'), 0, 0, 0, A('b'), 0, 0, 0, 0, 0, 0, 0, +/* 49 - n */ L('n'), L('N'), L('n'), L('N'), C('N'), 0, 0, 0, A('n'), 0, 0, 0, 0, 0, 0, 0, +/* 50 - m */ L('m'), L('M'), L('m'), L('M'), C('M'), 0, 0, 0, A('m'), 0, 0, 0, 0, 0, 0, 0, +/* 51 - ,< */ ',', '<', NULL, NULL, NULL, 0, 0, 0, A(','), 0, 0, 0, 0, 0, 0, 0, +/* 52 - .> */ '.', '>', NULL, NULL, NULL, 0, 0, 0, A('.'), 0, 0, 0, 0, 0, 0, 0, +/* 53 - /? */ SLASH, '?', NULL, NULL, BS, 0, 0, 0, A('/'), 0, 0, 0, 0, 0, 0, 0, +/* 54 - RSHF */ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, 0, 0, 0, SHIFT, 0, 0, 0, 0, 0, 0, 0, +/* 55 - * */ ASTSK, ASTSK, ASTSK, ASTSK, ASTSK, 0, 0, 0, ASTSK, 0, 0, 0, 0, 0, 0, 0, +/* 56 - ALT */ ALT, ALT, ALT, ALT, ALT, 0, 0, 0, ALT, 0, 0, 0, 0, 0, 0, 0, +/* 57 - SPC */ ' ', ' ', NULL, NULL, NULL, 0, 0, 0, A(' '), 0, 0, 0, 0, 0, 0, 0, +/* 58 - CAPS */ CAPS, CAPS, CAPS, CAPS, CAPS, 0, 0, 0, CAPS, 0, 0, 0, 0, 0, 0, 0, +/* 59 - F1 */ F1, SF1, NULL, NULL, F1, 0, 0, 0, AF1, 0, 0, 0, 0, 0, 0, 0, +/* 60 - F2 */ F2, SF2, NULL, NULL, F2, 0, 0, 0, AF2, 0, 0, 0, 0, 0, 0, 0, +/* 61 - F3 */ F3, SF3, NULL, NULL, F3, 0, 0, 0, AF3, 0, 0, 0, 0, 0, 0, 0, +/* 62 - F4 */ F4, SF4, NULL, NULL, F4, 0, 0, 0, AF4, 0, 0, 0, 0, 0, 0, 0, +/* 63 - F5 */ F5, SF5, NULL, NULL, F5, 0, 0, 0, AF5, 0, 0, 0, 0, 0, 0, 0, +/* 64 - F6 */ F6, SF6, NULL, NULL, F6, 0, 0, 0, AF6, 0, 0, 0, 0, 0, 0, 0, +/* 65 - F7 */ F7, SF7, NULL, NULL, F7, 0, 0, 0, AF7, 0, 0, 0, 0, 0, 0, 0, +/* 66 - F8 */ F8, SF8, NULL, NULL, F8, 0, 0, 0, AF8, 0, 0, 0, 0, 0, 0, 0, +/* 67 - F9 */ F9, SF9, NULL, NULL, F9, 0, 0, 0, AF9, 0, 0, 0, 0, 0, 0, 0, +/* 68 - F10 */ F10, SF10, NULL, NULL, F10, 0, 0, 0, AF10, 0, 0, 0, 0, 0, 0, 0, +/* 69 - NUMS */ NUMS, NUMS, NUMS, NUMS, NUMS, 0, 0, 0, NUMS, 0, 0, 0, 0, 0, 0, 0, +/* 70 - SCRL */ SCRL, SCRL3, SCRL2, NULL, SCRL4, 0, 0, 0, SCRL, 0, 0, 0, 0, 0, 0, 0, +/* 71 - HOME/7 */ HOME, HOME, HOME, HOME, HOME, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 72 - UP /8 */ UP, UP, UP, UP, UP, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 73 - PGUP/9 */ PGUP, PGUP, PGUP, PGUP, PGUP, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 74 - MINUS */ MINUS, MINUS, MINUS, MINUS, MINUS, 0, 0, 0, MINUS, 0, 0, 0, 0, 0, 0, 0, +/* 75 - LEFT/4 */ LEFT, LEFT, LEFT, LEFT, LEFT, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 76 - MID /5 */ MID, MID, MID, MID, MID, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 77 - RIGH/6 */ RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 78 - PLUS */ PLUS, PLUS, PLUS, PLUS, PLUS, 0, 0, 0, PLUS, 0, 0, 0, 0, 0, 0, 0, +/* 79 - END /1 */ END, END, END, END, END, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 80 - DOWN/2 */ DOWN, DOWN, DOWN, DOWN, DOWN, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 81 - PGDN/3 */ PGDN, PGDN, PGDN, PGDN, PGDN, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 82 - INS /0 */ INS, INS, INS, INS, INS, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 83 - DEL /. */ DEL, DEL, DEL, DEL, DEL, 0, 0, 0, DEL, 0, 0, 0, 0, 0, 0, 0, +/* 84 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 85 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 86 - <> */ '<', '>', '|', NULL, NULL, 0, 0, 0, A('<'), 0, 0, 0, 0, 0, 0, 0, +/* 87 - F11 */ SF1, SF1, NULL, NULL, F11, 0, 0, 0, AF11, 0, 0, 0, 0, 0, 0, 0, +/* 88 - F12 */ SF2, SF2, NULL, NULL, F12, 0, 0, 0, AF12, 0, 0, 0, 0, 0, 0, 0, +/* 89 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 90 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 91 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 92 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 93 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 94 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 95 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 96 - */ ENTER, ENTER, ENTER, ENTER, ENTER, 0, 0, 0, ENTER, 0, 0, 0, 0, 0, 0, 0, +/* 97 - */ CTRL, CTRL, CTRL, CTRL, CTRL, 0, 0, 0, CTRL, 0, 0, 0, 0, 0, 0, 0, +/* 98 - */ SLASH, SLASH, SLASH, SLASH, SLASH, 0, 0, 0, SLASH, 0, 0, 0, 0, 0, 0, 0, +/* 99 - */ NULL, NULL, NULL, NULL, C('\\'),0, 0, 0, C('\\'),0, 0, 0, 0, 0, 0, 0, +/* 100 - */ ALTGR, ALTGR, ALTGR, ALTGR, ALTGR, 0, 0, 0, ALTGR, 0, 0, 0, 0, 0, 0, 0, +/* 101 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 102 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 103 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 104 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 105 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 106 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 107 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 108 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 109 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 110 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 111 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 112 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 113 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 114 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 115 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 116 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 117 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 118 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 119 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 120 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 121 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 122 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 123 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 124 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 125 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 126 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +/* 127 - */ NULL, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, +}; diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c new file mode 100644 index 00000000..ca7a5466 --- /dev/null +++ b/drivers/char/keyboard.c @@ -0,0 +1,686 @@ +/* + * fiwix/drivers/char/keyboard.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KB_DATA 0x60 /* I/O data port */ +#define KBC_COMMAND 0x64 /* command/control port */ +#define KBC_STATUS 0x64 /* status register port */ + +/* + * PS/2 System Control Port A + * -------------------------------- + * bit 7 -> fixed disk activity led + * bit 6 -> fixed disk activity led + * bit 5 -> reserved + * bit 4 -> watchdog timer status + * bit 3 -> security lock latch + * bit 2 -> reserved + * bit 1 -> alternate gate A20 + * bit 0 -> alternate hot reset + */ +#define PS2_SYSCTRL_A 0x92 /* PS/2 system control port A (write) */ + +#define KB_CMD_RESET 0xFF /* keyboard reset */ +#define KB_CMD_ENABLE 0xF4 /* keyboard enable scanning */ +#define KB_CMD_DISABLE 0xF5 /* keyboard disable scanning */ +#define KB_CMD_IDENTIFY 0xF2 /* keyboard identify (for PS/2 only) */ +#define KB_CMD_ECHO 0xEE /* echo (for diagnostics only) */ + +#define KBC_CMD_RECV_CONFIG 0x20 /* read controller's config byte */ +#define KBC_CMD_SEND_CONFIG 0x60 /* write controller's config byte */ +#define KBC_CMD_SELF_TEST 0xAA /* self-test command */ +#define KBC_CMD_PS2_1_TEST 0xAB /* first PS/2 interface test command */ +#define KBC_CMD_PS2_2_TEST 0xA9 /* second PS/2 interface test command */ +#define KBC_CMD_DISABLE_PS2_1 0xAD /* disable first PS/2 port */ +#define KBC_CMD_ENABLE_PS2_1 0xAE /* enable first PS/2 port */ +#define KBC_CMD_DISABLE_PS2_2 0xA7 /* disable second PS/2 port (if any) */ +#define KBC_CMD_ENABLE_PS2_2 0xA8 /* enable second PS/2 port (if any) */ +#define KBC_CMD_GET_IFACE 0xCA /* get interface type (AT or MCA) */ +#define KBC_CMD_HOTRESET 0xFE /* Hot Reset */ + +/* flags of the status register */ +#define KB_STR_OUTBUSY 0x01 /* output buffer full, don't read yet */ +#define KB_STR_INBUSY 0x02 /* input buffer full, don't write yet */ +#define KB_STR_TXTMOUT 0x20 /* transmit time-out error */ +#define KB_STR_RXTMOUT 0x40 /* receive time-out error */ +#define KB_STR_PARERR 0X80 /* parity error */ +#define KB_STR_COMMERR (KB_STR_TXTMOUT | KB_STR_RXTMOUT) + +#define KB_RESET_OK 0xAA /* self-test passed */ +#define KB_ACK 0xFA /* acknowledge */ +#define KB_SETLED 0xED /* set/reset status indicators (LEDs) */ +#define KB_RATE 0xF3 /* set typematic rate/delay */ +#define DELAY_250 0x00 /* typematic delay at 250ms (default) */ +#define DELAY_500 0x40 /* typematic delay at 500ms */ +#define DELAY_750 0x80 /* typematic delay at 750ms */ +#define DELAY_1000 0xC0 /* typematic delay at 1000ms */ +#define RATE_30 0x00 /* typematic rate at 30.0 reports/sec (default) */ + +#define EXTKEY 0xE0 /* extended key (AltGr, Ctrl-Print, etc.) */ + +__key_t *keymap_line; + +static unsigned char leds = 0; +static unsigned char shift = 0; +static unsigned char altgr = 0; +static unsigned char ctrl = 0; +static unsigned char alt = 0; +static unsigned char extkey = 0; +static unsigned char deadkey = 0; + +static unsigned char do_scrl_buf = 0; +static char do_switch_console = -1; + +unsigned char kb_identify[2] = {0, 0}; +char ps2_active_ports = 0; +char ps2_supp_ports = 0; +char ps2_iface = 0; +short int current_cons; +char ctrl_alt_del = 1; +char any_key_to_reboot = 0; + +struct diacritic *diacr; +static char *diacr_chars = "`'^ \""; +struct diacritic grave_table[NR_DIACR] = { + { 'A', '\300' }, + { 'E', '\310' }, + { 'I', '\314' }, + { 'O', '\322' }, + { 'U', '\331' }, + { 'a', '\340' }, + { 'e', '\350' }, + { 'i', '\354' }, + { 'o', '\362' }, + { 'u', '\371' }, +}; +struct diacritic acute_table[NR_DIACR] = { + { 'A', '\301' }, + { 'E', '\311' }, + { 'I', '\315' }, + { 'O', '\323' }, + { 'U', '\332' }, + { 'a', '\341' }, + { 'e', '\351' }, + { 'i', '\355' }, + { 'o', '\363' }, + { 'u', '\372' }, +}; +struct diacritic circm_table[NR_DIACR] = { + { 'A', '\302' }, + { 'E', '\312' }, + { 'I', '\316' }, + { 'O', '\324' }, + { 'U', '\333' }, + { 'a', '\342' }, + { 'e', '\352' }, + { 'i', '\356' }, + { 'o', '\364' }, + { 'u', '\373' }, +}; +struct diacritic diere_table[NR_DIACR] = { + { 'A', '\304' }, + { 'E', '\313' }, + { 'I', '\317' }, + { 'O', '\326' }, + { 'U', '\334' }, + { 'a', '\344' }, + { 'e', '\353' }, + { 'i', '\357' }, + { 'o', '\366' }, + { 'u', '\374' }, +}; + +static char *pad_chars = "0123456789+-*/\015,."; + +static char *pad_seq[] = { + "\033[2~", /* INS */ + "\033[4~", /* END */ + "\033[B" , /* DOWN */ + "\033[6~", /* PGDN */ + "\033[D" , /* LEFT */ + "\033[G" , /* MID */ + "\033[C" , /* RIGHT */ + "\033[1~", /* HOME */ + "\033[A" , /* UP */ + "\033[5~", /* PGUP */ + "+", /* PLUS */ + "-", /* MINUS */ + "*", /* ASTERISK */ + "/", /* SLASH */ + "'\n'", /* ENTER */ + ",", /* COMMA */ + "\033[3~", /* DEL */ +}; + +static char *fn_seq[] = { + "\033[[A", /* F1 */ + "\033[[B", /* F2 */ + "\033[[C", /* F3 */ + "\033[[D", /* F4 */ + "\033[[E", /* F5 */ + "\033[17~", /* F6 */ + "\033[18~", /* F7 */ + "\033[19~", /* F8 */ + "\033[20~", /* F9 */ + "\033[21~", /* F10 */ + "\033[23~", /* F11, SF1 */ + "\033[24~", /* F12, SF2 */ + "\033[25~", /* SF3 */ + "\033[26~", /* SF4 */ + "\033[28~", /* SF5 */ + "\033[29~", /* SF6 */ + "\033[31~", /* SF7 */ + "\033[32~", /* SF8 */ + "\033[33~", /* SF9 */ + "\033[34~", /* SF10 */ +}; + +static void keyboard_delay(void) +{ + int n; + + for(n = 0; n < 1000; n++) { + NOP(); + } +} + +/* wait controller input buffer to be clear (ready to write) */ +static int keyboard_wait_input(void) +{ + int n; + + for(n = 0; n < 500000; n++) { + if(!(inport_b(KBC_STATUS) & KB_STR_INBUSY)) { + return 0; + } + } + return 1; +} + +static int keyboard_write(const unsigned char port, const unsigned char byte) +{ + if(!keyboard_wait_input()) { + outport_b(port, byte); + if(!keyboard_wait_input()) { + return 0; + } + } + + return 1; +} + +/* wait controller output buffer to be full (ready to read) */ +static int keyboard_wait_output(void) +{ + int n, value; + + for(n = 0; n < 500000; n++) { + if((value = inport_b(KBC_STATUS)) & KB_STR_OUTBUSY) { + if(value & (KB_STR_COMMERR | KB_STR_PARERR)) { + continue; + } + return 0; + } + } + return 1; +} + +static int keyboard_wait_ack(void) +{ + int n; + + keyboard_wait_output(); + for(n = 0; n < 1000; n++) { + if(inport_b(KB_DATA) == KB_ACK) { + return 0; + } + keyboard_delay(); + } + return 1; +} + +static void keyboard_identify(void) +{ + /* disable */ + keyboard_write(KB_DATA, KB_CMD_DISABLE); + if(keyboard_wait_ack()) { + printk("WARNING: %s(): ACK not received on disable command!\n", __FUNCTION__); + } + + /* identify */ + keyboard_write(KB_DATA, KB_CMD_IDENTIFY); + if(keyboard_wait_ack()) { + printk("WARNING: %s(): ACK not received on identify command!\n", __FUNCTION__); + } + if(!keyboard_wait_output()) { + kb_identify[0] = inport_b(KB_DATA); + } + if(!keyboard_wait_output()) { + kb_identify[1] = inport_b(KB_DATA); + } + + /* enable */ + keyboard_write(KB_DATA, KB_CMD_ENABLE); + if(keyboard_wait_ack()) { + printk("WARNING: %s(): ACK not received on enable command!\n", __FUNCTION__); + } + keyboard_wait_output(); + inport_b(KB_DATA); + + /* get the interface type */ + keyboard_write(KBC_COMMAND, KBC_CMD_GET_IFACE); + keyboard_wait_output(); + ps2_iface = inport_b(KB_DATA); +} + +static void keyboard_reset(void) +{ + int errno; + unsigned char config; + + /* disable device(s) */ + keyboard_write(KBC_COMMAND, KBC_CMD_DISABLE_PS2_1); + keyboard_write(KBC_COMMAND, KBC_CMD_DISABLE_PS2_2); + + /* flush buffers */ + while(!keyboard_wait_output()) { + inport_b(KB_DATA); + } + + /* get controller configuration */ + keyboard_write(KBC_COMMAND, KBC_CMD_RECV_CONFIG); + keyboard_wait_output(); + config = inport_b(KB_DATA); + ps2_active_ports = config & 0x01 ? 1 : 0; + ps2_active_ports += config & 0x02 ? 1 : 0; + ps2_supp_ports = 1 + (config & 0x20 ? 1 : 0); + + /* set controller configuration (disabling IRQs) */ + /* + keyboard_write(KBC_COMMAND, KBC_CMD_SEND_CONFIG); + keyboard_write(KB_DATA, config & ~(0x01 | 0x02 | 0x40)); + */ + + /* PS/2 controller self-test */ + keyboard_write(KBC_COMMAND, KBC_CMD_SELF_TEST); + keyboard_wait_output(); + if((errno = inport_b(KB_DATA)) != 0x55) { + printk("WARNING: %s(): keyboard returned 0x%x in self-test.\n", __FUNCTION__, errno); + } + + /* + * This sets again the controller configuration since the previous + * step may also reset the PS/2 controller to its power-on defaults. + */ + keyboard_write(KBC_COMMAND, KBC_CMD_SEND_CONFIG); + keyboard_write(KB_DATA, config); + + /* first PS/2 interface test */ + keyboard_write(KBC_COMMAND, KBC_CMD_PS2_1_TEST); + keyboard_wait_output(); + if((errno = inport_b(KB_DATA)) != 0) { + printk("WARNING: %s(): keyboard returned 0x%x in first PS/2 interface test.\n", __FUNCTION__, errno); + } + + if(ps2_supp_ports > 1) { + /* second PS/2 interface test */ + keyboard_write(KBC_COMMAND, KBC_CMD_PS2_2_TEST); + keyboard_wait_output(); + if((errno = inport_b(KB_DATA)) != 0) { + printk("WARNING: %s(): keyboard returned 0x%x in second PS/2 interface test.\n", __FUNCTION__, errno); + } + } + + /* enable device(s) */ + keyboard_write(KBC_COMMAND, KBC_CMD_ENABLE_PS2_1); + keyboard_write(KBC_COMMAND, KBC_CMD_ENABLE_PS2_2); + + /* reset device(s) */ + keyboard_write(KB_DATA, KB_CMD_RESET); + if(keyboard_wait_ack()) { + printk("WARNING: %s(): ACK not received on reset command!\n", __FUNCTION__); + } + if(!keyboard_wait_output()) { + if((errno = inport_b(KB_DATA)) != KB_RESET_OK) { + printk("WARNING: %s(): keyboard returned 0x%x in reset.\n", __FUNCTION__, errno); + } + } + + return; +} + +static void putc(struct tty *tty, unsigned char ch) +{ + if(tty_queue_putchar(tty, &tty->read_q, ch) < 0) { + if(tty->termios.c_iflag & IMAXBEL) { + vconsole_beep(); + } + } +} + +static void puts(struct tty *tty, char *seq) +{ + char ch; + + while((ch = *(seq++))) { + putc(tty, ch); + } +} + +void reboot(void) +{ + CLI(); + keyboard_write(PS2_SYSCTRL_A, 0x01); /* Fast Hot Reset */ + keyboard_write(KBC_COMMAND, KBC_CMD_HOTRESET); /* Hot Reset */ + HLT(); +} + +void set_leds(unsigned char leds) +{ + keyboard_write(KB_DATA, KB_SETLED); + keyboard_wait_ack(); + + keyboard_write(KB_DATA, leds); + keyboard_wait_ack(); +} + +void irq_keyboard(void) +{ + __key_t key, type; + unsigned char scode, mod; + struct tty *tty; + struct vconsole *vc; + unsigned char c; + int n; + + tty = get_tty(MKDEV(VCONSOLES_MAJOR, current_cons)); + vc = (struct vconsole *)tty->driver_data; + + scode = inport_b(KB_DATA); + + screen_on(); + add_bh(keyboard_bh); + + if(scode == KB_ACK) { + return; + } + + if(scode == EXTKEY) { + extkey = 1; + return; + } + + key = keymap[NR_MODIFIERS * (scode & 0x7F)]; + + /* a key has been released */ + if(scode & NR_SCODES) { + switch(key) { + case CTRL: + ctrl = 0; + break; + case ALT: + if(!extkey) { + alt = 0; + } else { + altgr = 0; + } + break; + case SHIFT: + if(!extkey) { + shift = 0; + } + break; + case CAPS: + case NUMS: + case SCRL: + leds = 0; + break; + } + extkey = 0; + return; + } + + switch(key) { + case CAPS: + if(!leds) { + vc->led_status ^= CAPSBIT; + vc->capslock = !vc->capslock; + set_leds(vc->led_status); + } + leds = 1; + return; + case NUMS: + if(!leds) { + vc->led_status ^= NUMSBIT; + vc->numlock = !vc->numlock; + set_leds(vc->led_status); + } + leds = 1; + return; + case SCRL: + if(!leds) { + if(vc->scrlock) { + tty->start(tty); + } else { + tty->stop(tty); + } + } + leds = 1; + return; + case CTRL: + ctrl = 1; + return; + case ALT: + if(!extkey) { + alt = 1; + } else { + altgr = 1; + } + return; + case SHIFT: + shift = 1; + extkey = 0; + return; + } + + if(ctrl && alt && key == DEL) { + if(ctrl_alt_del) { + reboot(); + } else { + send_sig(&proc_table[INIT], SIGINT); + } + return; + } + + keymap_line = &keymap[(scode & 0x7F) * NR_MODIFIERS]; + mod = 0; + + if(vc->capslock && (keymap_line[MOD_BASE] & LETTER_KEYS)) { + mod = !vc->capslock ? shift : vc->capslock - shift; + } else { + if(shift && !extkey) { + mod = 1; + } + } + if(altgr) { + mod = 2; + } + if(ctrl) { + mod = 4; + } + if(alt) { + mod = 8; + } + + key = keymap_line[mod]; + + if(key >= AF1 && key <= AF12) { + do_switch_console = key - CONS_KEYS; + return; + } + + if(shift && (key == PGUP)) { + do_scrl_buf = VC_BUF_UP; + return; + } + + if(shift && (key == PGDN)) { + do_scrl_buf = VC_BUF_DOWN; + return; + } + + if(extkey && (scode == SLASH_NPAD)) { + key = SLASH; + } + + if(any_key_to_reboot) { + reboot(); + } + + if(tty->count) { + type = key & 0xFF00; + c = key & 0xFF; + + switch(type) { + case FN_KEYS: + puts(tty, fn_seq[c]); + break; + + case SPEC_KEYS: + switch(key) { + case CR: + putc(tty, C('M')); + break; + } + break; + + case PAD_KEYS: + if(!vc->numlock) { + puts(tty, pad_seq[c]); + } else { + putc(tty, pad_chars[c]); + } + break; + + case DEAD_KEYS: + if(!deadkey) { + switch(c) { + case GRAVE ^ DEAD_KEYS: + deadkey = 1; + diacr = grave_table; + break; + case ACUTE ^ DEAD_KEYS: + deadkey = 2; + diacr = acute_table; + break; + case CIRCM ^ DEAD_KEYS: + deadkey = 3; + diacr = circm_table; + break; + case DIERE ^ DEAD_KEYS: + deadkey = 5; + diacr = diere_table; + break; + } + return; + } + c = diacr_chars[c]; + deadkey = 0; + putc(tty, c); + + break; + + case META_KEYS: + putc(tty, '\033'); + putc(tty, c); + break; + + case LETTER_KEYS: + if(deadkey) { + for(n = 0; n < NR_DIACR; n++) { + if(diacr[n].letter == c) { + c = diacr[n].code; + } + } + } + putc(tty, c); + break; + + default: + if(deadkey && c == ' ') { + c = diacr_chars[deadkey - 1]; + } + putc(tty, c); + break; + } + } + deadkey = 0; + return; +} + +void keyboard_bh(void) +{ + int n; + struct tty *tty; + + if(do_switch_console >= 0) { + vconsole_select(do_switch_console); + do_switch_console = -1; + } + + if(do_scrl_buf) { + vconsole_buffer_scrl(do_scrl_buf); + do_scrl_buf = 0; + } + + tty = &tty_table[0]; + for(n = 0; n < NR_TTYS; n++, tty++) { + if(!tty->read_q.count) { + continue; + } + if(lock_area(AREA_TTY_READ)) { + continue; + } + tty->input(tty); + unlock_area(AREA_TTY_READ); + } +} + +void keyboard_init(void) +{ + keyboard_reset(); + + /* flush buffers */ + while(!keyboard_wait_output()) { + inport_b(KB_DATA); + } + + keyboard_identify(); + + keyboard_write(KB_DATA, KB_RATE); + keyboard_wait_ack(); + keyboard_write(KB_DATA, DELAY_250 | RATE_30); + keyboard_wait_ack(); + + printk("keyboard 0x%04X-0x%04X %d type=%s %s PS/2 devices=%d/%d\n", 0x60, 0x64, KEYBOARD_IRQ, kb_identify[0] == 0xAB ? "MF2" : "unknown", ps2_iface & 0x1 ? "MCA" : "AT", ps2_active_ports, ps2_supp_ports); + + if(!register_irq(KEYBOARD_IRQ, "keyboard", irq_keyboard)) { + enable_irq(KEYBOARD_IRQ); + } +} diff --git a/drivers/char/lp.c b/drivers/char/lp.c new file mode 100644 index 00000000..a6c096a0 --- /dev/null +++ b/drivers/char/lp.c @@ -0,0 +1,216 @@ +/* + * fiwix/drivers/char/lp.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct lp lp_table[LP_MINORS]; + +static struct fs_operations lp_driver_fsop = { + 0, + 0, + + lp_open, + lp_close, + NULL, /* read */ + lp_write, + NULL, /* ioctl */ + NULL, /* lseek */ + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lockup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +static struct device lp_device = { + "lp", + -1, + LP_MAJOR, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + 0, + NULL, + &lp_driver_fsop, +}; + +struct lp lp_table[LP_MINORS] = { + { LP0_ADDR, LP0_ADDR + 1, LP0_ADDR + 2, 0 } +}; + +static void lp_delay(void) +{ + int n; + + for(n = 0; n < 10000; n++) { + NOP(); + } +} + +static int lp_ready(int minor) +{ + int n; + + for(n = 0; n < LP_RDY_RETR; n++) { + if(inport_b(lp_table[minor].stat) & LP_STAT_BUS) { + break; + } + lp_delay(); + } + if(n == LP_RDY_RETR) { + return 0; + } + return 1; +} + +static int lp_probe(int minor) +{ + /* first check */ + outport_b(lp_table[minor].data, 0x55); + lp_delay(); + if(inport_b(lp_table[minor].data) != 0x55) { + return 1; /* did not retain data */ + } + + /* second check */ + outport_b(lp_table[minor].data, 0xAA); + lp_delay(); + if(inport_b(lp_table[minor].data) != 0xAA) { + return 1; /* did not retain data */ + } + return 0; +} + +static int lp_write_data(int minor, unsigned char c) +{ + unsigned char ctrl; + + if(!lp_ready(minor)) { + return -EBUSY; + } + outport_b(lp_table[minor].data, c); + ctrl = inport_b(lp_table[minor].ctrl); + outport_b(lp_table[minor].ctrl, ctrl | LP_CTRL_STR); + lp_delay(); + outport_b(lp_table[minor].ctrl, ctrl); + if(!lp_ready(minor)) { + return -EBUSY; + } + return 1; +} + +int lp_open(struct inode *i, struct fd *fd_table) +{ + int minor; + + minor = MINOR(i->rdev); + if(!TEST_MINOR(lp_device.minors, minor)) { + return -ENXIO; + } + if(!(lp_table[minor].flags & LP_CTRL_SEL)) { + return -ENXIO; + } + if(lp_table[minor].flags & LP_STAT_BUS) { + return -EBUSY; + } + lp_table[minor].flags |= LP_STAT_BUS; + return 0; +} + +int lp_close(struct inode *i, struct fd *fd_table) +{ + int minor; + + minor = MINOR(i->rdev); + if(!TEST_MINOR(lp_device.minors, minor)) { + return -ENXIO; + } + lp_table[minor].flags &= ~LP_STAT_BUS; + return 0; +} + +int lp_write(struct inode *i, struct fd *fd_table, const char *buffer, __size_t count) +{ + unsigned int n; + int bytes_written, total_written; + int minor; + + minor = MINOR(i->rdev); + if(!TEST_MINOR(lp_device.minors, minor)) { + return -ENXIO; + } + + total_written = 0; + for(n = 0; n < count; n++) { + bytes_written = lp_write_data(minor, buffer[n]); + if(bytes_written != 1) { + break; + } + total_written += bytes_written; + } + + return total_written; +} + +void lp_init(void) +{ + int n; + unsigned char ctrl; + + for(n = 0; n < LP_MINORS; n++) { + if(!lp_probe(n)) { + ctrl = inport_b(lp_table[n].ctrl); + ctrl &= ~LP_CTRL_AUT; /* disable auto LF */ + ctrl |= LP_CTRL_INI; /* initialize */ + ctrl |= LP_CTRL_SEL; /* select in */ + ctrl &= ~LP_CTRL_IRQ; /* disable IRQ */ + ctrl &= ~LP_CTRL_BID; /* disable bidirectional mode */ + outport_b(lp_table[n].ctrl, ctrl); + lp_table[n].flags |= LP_CTRL_SEL; + printk("lp%d 0x%04X-0x%04X - \n", n, lp_table[n].data, lp_table[n].data + 2); + SET_MINOR(lp_device.minors, n); + } + } + + for(n = 0; n < LP_MINORS; n++) { + if(lp_table[n].flags & LP_CTRL_SEL) { + if(register_device(CHR_DEV, &lp_device)) { + printk("WARNING: %s(): unable to register lp device.\n", __FUNCTION__); + } + break; + } + } +} diff --git a/drivers/char/memdev.c b/drivers/char/memdev.c new file mode 100644 index 00000000..c9b5ec36 --- /dev/null +++ b/drivers/char/memdev.c @@ -0,0 +1,427 @@ +/* + * fiwix/drivers/char/memdev.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct fs_operations mem_driver_fsop = { + 0, + 0, + + mem_open, + mem_close, + mem_read, + mem_write, + NULL, /* ioctl */ + mem_lseek, + NULL, /* readdir */ + mem_mmap, + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lockup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +static struct fs_operations kmem_driver_fsop = { + 0, + 0, + + kmem_open, + kmem_close, + kmem_read, + kmem_write, + NULL, /* ioctl */ + kmem_lseek, + NULL, /* readdir */ + mem_mmap, + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lockup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +static struct fs_operations null_driver_fsop = { + 0, + 0, + + null_open, + null_close, + null_read, + null_write, + NULL, /* ioctl */ + null_lseek, + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lockup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +static struct fs_operations zero_driver_fsop = { + 0, + 0, + + zero_open, + zero_close, + zero_read, + zero_write, + NULL, /* ioctl */ + zero_lseek, + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lockup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +static struct fs_operations memdev_driver_fsop = { + 0, + 0, + + memdev_open, + NULL, /* close */ + NULL, /* read */ + NULL, /* write */ + NULL, /* ioctl */ + NULL, /* lseek */ + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lockup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +static struct device memdev_device = { + "mem", + -1, + MEMDEV_MAJOR, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + 0, + NULL, + &memdev_driver_fsop, +}; + +int mem_open(struct inode *i, struct fd *fd_table) +{ + return 0; +} + +int mem_close(struct inode *i, struct fd *fd_table) +{ + return 0; +} + +int mem_read(struct inode *i, struct fd *fd_table, char *buffer, __size_t count) +{ + unsigned int physical_memory; + + physical_memory = (kstat.physical_pages << PAGE_SHIFT); + if(fd_table->offset >= physical_memory) { + return 0; + } + count = MIN(count, physical_memory - fd_table->offset); + memcpy_b(buffer, (void *)P2V(fd_table->offset), count); + fd_table->offset += count; + return count; +} + +int mem_write(struct inode *i, struct fd *fd_table, const char *buffer, __size_t count) +{ + unsigned int physical_memory; + + physical_memory = (kstat.physical_pages << PAGE_SHIFT); + if(fd_table->offset >= physical_memory) { + return 0; + } + count = MIN(count, physical_memory - fd_table->offset); + memcpy_b((void *)P2V(fd_table->offset), buffer, count); + fd_table->offset += count; + return count; +} + +int mem_lseek(struct inode *i, __off_t offset) +{ + return offset; +} + +int kmem_open(struct inode *i, struct fd *fd_table) +{ + return 0; +} + +int kmem_close(struct inode *i, struct fd *fd_table) +{ + return 0; +} + +int kmem_read(struct inode *i, struct fd *fd_table, char *buffer, __size_t count) +{ + memcpy_b(buffer, (void *)P2V(fd_table->offset), count); + fd_table->offset += count; + return count; +} + +int kmem_write(struct inode *i, struct fd *fd_table, const char *buffer, __size_t count) +{ + memcpy_b((void *)P2V(fd_table->offset), buffer, count); + fd_table->offset += count; + return count; +} + +int kmem_lseek(struct inode *i, __off_t offset) +{ + return offset; +} + +int null_open(struct inode *i, struct fd *fd_table) +{ + return 0; +} + +int null_close(struct inode *i, struct fd *fd_table) +{ + return 0; +} + +int null_read(struct inode *i, struct fd *fd_table, char *buffer, __size_t count) +{ + return 0; +} + +int null_write(struct inode *i, struct fd *fd_table, const char *buffer, __size_t count) +{ + return count; +} + +int null_lseek(struct inode *i, __off_t offset) +{ + return offset; +} + +int zero_open(struct inode *i, struct fd *fd_table) +{ + return 0; +} + +int zero_close(struct inode *i, struct fd *fd_table) +{ + return 0; +} + +int zero_read(struct inode *i, struct fd *fd_table, char *buffer, __size_t count) +{ + memset_b(buffer, NULL, count); + return count; +} + +int zero_write(struct inode *i, struct fd *fd_table, const char *buffer, __size_t count) +{ + return count; +} + +int zero_lseek(struct inode *i, __off_t offset) +{ + return offset; +} + +int memdev_open(struct inode *i, struct fd *fd_table) +{ + unsigned char minor; + + minor = MINOR(i->rdev); + switch(minor) { + case MEMDEV_MEM: + i->fsop = &mem_driver_fsop; + break; + case MEMDEV_KMEM: + i->fsop = &kmem_driver_fsop; + break; + case MEMDEV_NULL: + i->fsop = &null_driver_fsop; + break; + case MEMDEV_ZERO: + i->fsop = &zero_driver_fsop; + break; + default: + return -ENXIO; + } + if(i->fsop->open) { + return i->fsop->open(i, fd_table); + } + return 0; +} + +/* + * This function maps a range of physical addresses marked as not available for + * use in the BIOS memory map, like the video RAM. + */ +int mem_mmap(struct inode *i, struct vma *vma) +{ + unsigned int addr, length; + + length = (vma->end - vma->start) & PAGE_MASK; + + /* this breaks down the range in 4KB chunks */ + for(addr = 0; addr < length; addr += PAGE_SIZE) { + /* map the page only if is NOT available in the BIOS map */ + if(!addr_in_bios_map(vma->offset + addr)) { + if(!map_page(current, (vma->start + addr) & PAGE_MASK, (vma->offset + addr) & PAGE_MASK, PROT_READ | PROT_WRITE)) { + return -ENOMEM; + } + } else { + printk("ERROR: %s(): mapping AVAILABLE pages in BIOS memory map isn't supported.\n", __FUNCTION__); + printk("\tinvalid mapping: 0x%08x -> 0x%08x\n", (vma->start + addr) & PAGE_MASK, (vma->offset + addr) & PAGE_MASK); + return -EAGAIN; + } + } + invalidate_tlb(); + return 0; +} + +void memdev_init(void) +{ + if(register_device(CHR_DEV, &memdev_device)) { + printk("ERROR: %s(): unable to register memory devices.\n", __FUNCTION__); + return; + } + SET_MINOR(memdev_device.minors, MEMDEV_MEM); + SET_MINOR(memdev_device.minors, MEMDEV_KMEM); + SET_MINOR(memdev_device.minors, MEMDEV_NULL); + SET_MINOR(memdev_device.minors, MEMDEV_ZERO); +} diff --git a/drivers/char/tty.c b/drivers/char/tty.c new file mode 100644 index 00000000..2e8d80a5 --- /dev/null +++ b/drivers/char/tty.c @@ -0,0 +1,863 @@ +/* + * fiwix/drivers/char/tty.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct tty tty_table[NR_TTYS]; +extern short int current_cons; + +static void wait_vtime_off(unsigned int arg) +{ + unsigned int *fn = (unsigned int *)arg; + + wakeup(fn); +} + +static void get_termio(struct tty *tty, struct termio *termio) +{ + int n; + + termio->c_iflag = tty->termios.c_iflag; + termio->c_oflag = tty->termios.c_oflag; + termio->c_cflag = tty->termios.c_cflag; + termio->c_lflag = tty->termios.c_lflag; + termio->c_line = tty->termios.c_line; + for(n = 0; n < NCC; n++) { + termio->c_cc[n] = tty->termios.c_cc[n]; + } +} + +static void set_termio(struct tty *tty, struct termio *termio) +{ + int n; + + tty->termios.c_iflag = termio->c_iflag; + tty->termios.c_oflag = termio->c_oflag; + tty->termios.c_cflag = termio->c_cflag; + tty->termios.c_lflag = termio->c_lflag; + tty->termios.c_line = termio->c_line; + for(n = 0; n < NCC; n++) { + tty->termios.c_cc[n] = termio->c_cc[n]; + } +} + +static void out_char(struct tty *tty, unsigned char ch) +{ + if(ISCNTRL(ch) && !ISSPACE(ch) && (tty->termios.c_lflag & ECHOCTL)) { + if(tty->lnext || (!tty->lnext && ch != tty->termios.c_cc[VEOF])) { + tty_queue_putchar(tty, &tty->write_q, '^'); + tty_queue_putchar(tty, &tty->write_q, ch + 64); + } + } else { + tty_queue_putchar(tty, &tty->write_q, ch); + } +} + +static void erase_char(struct tty *tty, unsigned char erasechar) +{ + unsigned char ch; + + if(erasechar == tty->termios.c_cc[VERASE]) { + if((ch = tty_queue_unputchar(&tty->cooked_q))) { + if(tty->termios.c_lflag & ECHO) { + tty_queue_putchar(tty, &tty->write_q, '\b'); + tty_queue_putchar(tty, &tty->write_q, ' '); + tty_queue_putchar(tty, &tty->write_q, '\b'); + if(ch == '\t') { + tty->deltab(tty); + } + if(ISCNTRL(ch) && !ISSPACE(ch) && tty->termios.c_lflag & ECHOCTL) { + tty_queue_putchar(tty, &tty->write_q, '\b'); + tty_queue_putchar(tty, &tty->write_q, ' '); + tty_queue_putchar(tty, &tty->write_q, '\b'); + } + } + } + } + if(erasechar == tty->termios.c_cc[VWERASE]) { + unsigned char word_seen = 0; + + while(tty->cooked_q.count > 0) { + ch = LAST_CHAR(&tty->cooked_q); + if((ch == ' ' || ch == '\t') && word_seen) { + break; + } + if(ch != ' ' && ch != '\t') { + word_seen = 1; + } + erase_char(tty, tty->termios.c_cc[VERASE]); + } + } + if(erasechar == tty->termios.c_cc[VKILL]) { + while(tty->cooked_q.count > 0) { + erase_char(tty, tty->termios.c_cc[VERASE]); + } + if(tty->termios.c_lflag & ECHOK && !(tty->termios.c_lflag & ECHOE)) { + tty_queue_putchar(tty, &tty->write_q, '\n'); + } + } +} + +int register_tty(__dev_t dev) +{ + int n; + + for(n = 0; n < NR_TTYS; n++) { + if(tty_table[n].dev == dev) { + printk("ERROR: %s(): tty device %d,%d already registered!\n", __FUNCTION__, MAJOR(dev), MINOR(dev)); + return 1; + } + if(!tty_table[n].dev) { + tty_table[n].dev = dev; + tty_table[n].count = 0; + return 0; + } + } + printk("ERROR: %s(): tty table is full!\n", __FUNCTION__); + return 1; +} + +struct tty * get_tty(__dev_t dev) +{ + int n; + + if(!dev) { + return NULL; + } + + /* /dev/console = system console */ + if(dev == MKDEV(SYSCON_MAJOR, 1)) { + dev = (__dev_t)_syscondev; + } + + /* /dev/tty0 = current virtual console */ + if(dev == MKDEV(VCONSOLES_MAJOR, 0)) { + dev = MKDEV(VCONSOLES_MAJOR, current_cons); + } + + /* /dev/tty = controlling TTY device */ + if(dev == MKDEV(SYSCON_MAJOR, 0)) { + if(!current->ctty) { + return NULL; + } + dev = current->ctty->dev; + } + + for(n = 0; n < NR_TTYS; n++) { + if(tty_table[n].dev != dev) { + continue; + } + return &tty_table[n]; + } + return NULL; +} + +void disassociate_ctty(struct tty *tty) +{ + struct proc *p; + + if(!tty) { + return; + } + + /* this tty is no longer the controlling tty of any session */ + tty->pgid = tty->sid = 0; + + /* clear the controlling tty for all processes in the same SID */ + FOR_EACH_PROCESS(p) { + if((p->state != PROC_UNUSED) && (p->sid == current->sid)) { + p->ctty = NULL; + } + } + kill_pgrp(current->pgid, SIGHUP); + kill_pgrp(current->pgid, SIGCONT); +} + +void termios_reset(struct tty *tty) +{ + tty->termios.c_iflag = ICRNL | IXON | IXOFF; + tty->termios.c_oflag = OPOST | ONLCR; + tty->termios.c_cflag = B38400 | CS8 | HUPCL | CREAD | CLOCAL; + tty->termios.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN; + tty->termios.c_line = 0; + tty->termios.c_cc[VINTR] = 3; /* ^C */ + tty->termios.c_cc[VQUIT] = 28; /* ^\ */ + tty->termios.c_cc[VERASE] = BS; /* ^? (127) not '\b' (^H) */ + tty->termios.c_cc[VKILL] = 21; /* ^U */ + tty->termios.c_cc[VEOF] = 4; /* ^D */ + tty->termios.c_cc[VTIME] = 0; + tty->termios.c_cc[VMIN] = 1; + tty->termios.c_cc[VSWTC] = 0; + tty->termios.c_cc[VSTART] = 17; /* ^Q */ + tty->termios.c_cc[VSTOP] = 19; /* ^S */ + tty->termios.c_cc[VSUSP] = 26; /* ^Z */ + tty->termios.c_cc[VEOL] = '\n'; /* ^J */ + tty->termios.c_cc[VREPRINT] = 18; /* ^R */ + tty->termios.c_cc[VDISCARD] = 15; /* ^O */ + tty->termios.c_cc[VWERASE] = 23; /* ^W */ + tty->termios.c_cc[VLNEXT] = 22; /* ^V */ + tty->termios.c_cc[VEOL2] = 0; +} + +void do_cook(struct tty *tty) +{ + int n; + unsigned char ch; + struct cblock *cb; + + while(tty->read_q.count > 0) { + ch = tty_queue_getchar(&tty->read_q); + + if((tty->termios.c_lflag & ISIG) && !tty->lnext) { + if(ch == tty->termios.c_cc[VINTR]) { + if(!(tty->termios.c_lflag & NOFLSH)) { + tty_queue_flush(&tty->read_q); + tty_queue_flush(&tty->cooked_q); + } + if(tty->pgid > 0) { + kill_pgrp(tty->pgid, SIGINT); + } + break; + } + if(ch == tty->termios.c_cc[VQUIT]) { + if(tty->pgid > 0) { + kill_pgrp(tty->pgid, SIGQUIT); + } + break; + } + if(ch == tty->termios.c_cc[VSUSP]) { + if(tty->pgid > 0) { + kill_pgrp(tty->pgid, SIGTSTP); + } + break; + } + } + + if(tty->termios.c_iflag & ISTRIP) { + ch = TOASCII(ch); + } + if(tty->termios.c_iflag & IUCLC) { + if(ISUPPER(ch)) { + ch = TOLOWER(ch); + } + } + + if(!tty->lnext) { + if(ch == '\r') { + if(tty->termios.c_iflag & IGNCR) { + continue; + } + if(tty->termios.c_iflag & ICRNL) { + ch = '\n'; + } + } else { + if(ch == '\n') { + if(tty->termios.c_iflag & INLCR) { + ch = '\r'; + } + } + } + } + + if(tty->termios.c_lflag & ICANON && !tty->lnext) { + if(ch == tty->termios.c_cc[VERASE] || ch == tty->termios.c_cc[VWERASE] || ch == tty->termios.c_cc[VKILL]) { + erase_char(tty, ch); + continue; + } + + if(ch == tty->termios.c_cc[VREPRINT]) { + out_char(tty, ch); + tty_queue_putchar(tty, &tty->write_q, '\n'); + cb = tty->cooked_q.head; + while(cb) { + for(n = 0; n < cb->end_off; n++) { + if(n >= cb->start_off) { + out_char(tty, cb->data[n]); + } + } + cb = cb->next; + } + continue; + } + + if(ch == tty->termios.c_cc[VLNEXT] && tty->termios.c_lflag & IEXTEN) { + tty->lnext = 1; + if(tty->termios.c_lflag & ECHOCTL) { + tty_queue_putchar(tty, &tty->write_q, '^'); + tty_queue_putchar(tty, &tty->write_q, '\b'); + } + break; + } + + if(tty->termios.c_iflag & IXON) { + if(ch == tty->termios.c_cc[VSTART]) { + tty->start(tty); + continue; + } + if(ch == tty->termios.c_cc[VSTOP]) { + tty->stop(tty); + continue; + } + if(tty->termios.c_iflag & IXANY) { + tty->start(tty); + } + } + } + + /* using ISSPACE here makes LNEXT working incorrectly, FIXME */ + if(tty->termios.c_lflag & ICANON) { + if(ISCNTRL(ch) && !ISSPACE(ch) && (tty->termios.c_lflag & ECHOCTL)) { + out_char(tty, ch); + tty_queue_putchar(tty, &tty->cooked_q, ch); + tty->lnext = 0; + continue; + } + if(ch == '\n') { + tty->canon_data = 1; + } + } + + if(tty->termios.c_lflag & ECHO) { + out_char(tty, ch); + } else { + if((tty->termios.c_lflag & ECHONL) && (ch == '\n')) { + out_char(tty, ch); + } + } + tty_queue_putchar(tty, &tty->cooked_q, ch); + tty->lnext = 0; + } + tty->output(tty); + wakeup(&tty->cooked_q); + if(!(tty->termios.c_lflag & ICANON) || ((tty->termios.c_lflag & ICANON) && tty->canon_data)) { + wakeup(&do_select); + } +} + +int tty_open(struct inode *i, struct fd *fd_table) +{ + int noctty_flag; + __dev_t dev; + struct tty *tty; + + noctty_flag = fd_table->flags & O_NOCTTY; + + dev = i->rdev; + + if(MAJOR(i->rdev) == SYSCON_MAJOR && MINOR(i->rdev) == 0) { + if(!current->ctty) { + return -ENXIO; + } + dev = i->rdev; + } + + if(MAJOR(dev) == VCONSOLES_MAJOR && MINOR(dev) == 0) { + noctty_flag = 1; + } + + if(!(tty = get_tty(dev))) { + printk("%s(): oops! (%x)\n", __FUNCTION__, dev); + printk("_syscondev = %x\n", _syscondev); + return -ENXIO; + } + tty->count++; + + if(SESS_LEADER(current) && !current->ctty && !noctty_flag && !tty->sid) { + current->ctty = tty; + tty->sid = current->sid; + tty->pgid = current->pgid; + } + return 0; +} + +int tty_close(struct inode *i, struct fd *fd_table) +{ + struct proc *p; + struct tty *tty; + + if(!(tty = get_tty(i->rdev))) { + printk("%s(): oops! (%x)\n", __FUNCTION__, i->rdev); + return -ENXIO; + } + + tty->count = tty->count ? tty->count - 1 : 0; + if(!tty->count) { + tty->reset(tty); + termios_reset(tty); + tty->pgid = tty->sid = 0; + + /* this tty is no longer the controlling tty of any process */ + FOR_EACH_PROCESS(p) { + if((p->state != PROC_UNUSED) && (p->ctty == tty)) { + p->ctty = NULL; + } + } + } + return 0; +} + +int tty_read(struct inode *i, struct fd *fd_table, char *buffer, __size_t count) +{ + unsigned int n, min; + unsigned char ch; + struct tty *tty; + struct callout_req creq; + + if(!(tty = get_tty(i->rdev))) { + printk("%s(): oops! (%x)\n", __FUNCTION__, i->rdev); + return -ENXIO; + } + + /* only the foreground process group is allowed to read from the tty */ + if(i->rdev != MKDEV(VCONSOLES_MAJOR, 0)) { /* /dev/tty0 */ + if(current->pgid != tty->pgid) { + if(current->sigaction[SIGTTIN - 1].sa_handler == SIG_IGN || current->sigblocked & (1 << (SIGTTIN - 1)) || is_orphaned_pgrp(current->pgid)) { + return -EIO; + } + kill_pgrp(current->pgid, SIGTTIN); + return -ERESTART; + } + } + + n = min = 0; + while(count > 0) { + if(tty->termios.c_lflag & ICANON) { + if((ch = LAST_CHAR(&tty->cooked_q))) { + if(ch == '\n' || ch == tty->termios.c_cc[VEOL] || ch == tty->termios.c_cc[VEOF] || (tty->termios.c_lflag & IEXTEN && ch == tty->termios.c_cc[VEOL2] && tty->termios.c_cc[VEOL2] != 0)) { + + tty->canon_data = 0; + /* EOF is not passed to the reading process */ + if(ch == tty->termios.c_cc[VEOF]) { + tty_queue_unputchar(&tty->cooked_q); + } + + while(n < count) { + if((ch = tty_queue_getchar(&tty->cooked_q))) { + buffer[n++] = ch; + } else { + break; + } + } + break; + } + } + } else { + if(tty->termios.c_cc[VTIME] > 0) { + unsigned int ini_ticks = kstat.ticks; + unsigned int timeout; + + if(!tty->termios.c_cc[VMIN]) { + /* VTIME is measured in tenths of second */ + timeout = tty->termios.c_cc[VTIME] * (HZ / 10); + + while(kstat.ticks - ini_ticks < timeout && !tty->cooked_q.count) { + creq.fn = wait_vtime_off; + creq.arg = (unsigned int)&tty->cooked_q; + add_callout(&creq, timeout); + if(fd_table->flags & O_NONBLOCK) { + return -EAGAIN; + } + if(sleep(&tty->cooked_q, PROC_INTERRUPTIBLE)) { + return -EINTR; + } + } + while(n < count) { + if((ch = tty_queue_getchar(&tty->cooked_q))) { + buffer[n++] = ch; + } else { + break; + } + } + break; + } else { + if(tty->cooked_q.count > 0) { + if(n < MIN(tty->termios.c_cc[VMIN], count)) { + ch = tty_queue_getchar(&tty->cooked_q); + buffer[n++] = ch; + } + if(n >= MIN(tty->termios.c_cc[VMIN], count)) { + del_callout(&creq); + break; + } + timeout = tty->termios.c_cc[VTIME] * (HZ / 10); + creq.fn = wait_vtime_off; + creq.arg = (unsigned int)&tty->cooked_q; + add_callout(&creq, timeout); + if(fd_table->flags & O_NONBLOCK) { + return -EAGAIN; + } + if(sleep(&tty->cooked_q, PROC_INTERRUPTIBLE)) { + return -EINTR; + } + if(!tty->cooked_q.count) { + break; + } + continue; + } + } + } else { + if(tty->cooked_q.count > 0) { + if(min < tty->termios.c_cc[VMIN] || !tty->termios.c_cc[VMIN]) { + if(n < count) { + ch = tty_queue_getchar(&tty->cooked_q); + buffer[n++] = ch; + } + min++; + } + } + if(min >= tty->termios.c_cc[VMIN]) { + break; + } + } + } + if(fd_table->flags & O_NONBLOCK) { + return -EAGAIN; + } + if(sleep(&tty->cooked_q, PROC_INTERRUPTIBLE)) { + return -EINTR; + } + } + return n; +} + +int tty_write(struct inode *i, struct fd *fd_table, const char *buffer, __size_t count) +{ + unsigned int n; + unsigned char ch; + struct tty *tty; + + if(!(tty = get_tty(i->rdev))) { + printk("%s(): oops! (%x)\n", __FUNCTION__, i->rdev); + return -ENXIO; + } + + /* only the foreground process group is allowed to write to the tty */ + if(i->rdev != MKDEV(VCONSOLES_MAJOR, 0)) { /* /dev/tty0 */ + if(current->pgid != tty->pgid && tty->termios.c_lflag & TOSTOP) { + if(current->sigaction[SIGTTIN - 1].sa_handler != SIG_IGN && !(current->sigblocked & (1 << (SIGTTIN - 1)))) { + if(is_orphaned_pgrp(current->pgid)) { + return -EIO; + } + kill_pgrp(current->pgid, SIGTTOU); + return -ERESTART; + } + } + } + + n = 0; + for(;;) { + if(current->sigpending & ~current->sigblocked) { + return -ERESTART; + } + while(count && n < count) { + ch = *(buffer + n); + /* FIXME: check if *(buffer + n) address is valid */ + if(tty_queue_putchar(tty, &tty->write_q, ch) < 0) { + break; + } + n++; + } + tty->output(tty); + if(n == count) { + break; + } + if(tty->write_q.count > 0) { + if(sleep(&tty->write_q, PROC_INTERRUPTIBLE)) { + return -EINTR; + } + } + do_sched(); + } + return n; +} + +/* FIXME: http://www.lafn.org/~dave/linux/termios.txt (doc/termios.txt) */ +int tty_ioctl(struct inode *i, int cmd, unsigned long int arg) +{ + struct proc *p; + struct tty *tty; + int errno; + + if(!(tty = get_tty(i->rdev))) { + printk("%s(): oops! (%x)\n", __FUNCTION__, i->rdev); + return -ENXIO; + } + + switch(cmd) { + /* + * Fetch and store the current terminal parameters to a termios + * structure pointed to by the argument. + */ + case TCGETS: + if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(struct termios)))) { + return errno; + } + memcpy_b((void *)arg, &tty->termios, sizeof(struct termios)); + break; + + /* + * Set the current terminal parameters according to the + * values in the termios structure pointed to by the argument. + */ + case TCSETS: + if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct termios)))) { + return errno; + } + memcpy_b(&tty->termios, (void *)arg, sizeof(struct termios)); + break; + + /* + * Same as TCSETS except it doesn't take effect until all + * the characters queued for output have been transmitted. + */ + case TCSETSW: + if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct termios)))) { + return errno; + } + memcpy_b(&tty->termios, (void *)arg, sizeof(struct termios)); + break; + + /* + * Same as TCSETSW except that all characters queued for + * input are discarded. + */ + case TCSETSF: + if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct termios)))) { + return errno; + } + memcpy_b(&tty->termios, (void *)arg, sizeof(struct termios)); + tty_queue_flush(&tty->read_q); + break; + + /* + * Fetches and stores the current terminal parameters to a + * termio structure pointed to by the argument. + */ + case TCGETA: + if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(struct termio)))) { + return errno; + } + get_termio(tty, (struct termio *)arg); + break; + + /* + * Set the current terminal parameters according to the + * values in the termio structure pointed to by the argument. + */ + case TCSETA: + if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct termio)))) { + return errno; + } + set_termio(tty, (struct termio *)arg); + break; + + /* + * Same a TCSET except it doesn't take effect until all + * the characters queued for output have been transmitted. + */ + case TCSETAW: + if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct termio)))) { + return errno; + } + set_termio(tty, (struct termio *)arg); + break; + + /* + * Same as TCSETAW except that all characters queued for + * input are discarded. + */ + case TCSETAF: + if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct termio)))) { + return errno; + } + set_termio(tty, (struct termio *)arg); + break; + + case TCXONC: + switch(arg) { + case TCOOFF: + tty->stop(tty); + break; + case TCOON: + tty->start(tty); + break; + default: + return -EINVAL; + } + break; + case TCFLSH: + switch(arg) { + case TCIFLUSH: + tty_queue_flush(&tty->read_q); + tty_queue_flush(&tty->cooked_q); + break; + case TCOFLUSH: + tty_queue_flush(&tty->write_q); + break; + case TCIOFLUSH: + tty_queue_flush(&tty->read_q); + tty_queue_flush(&tty->cooked_q); + tty_queue_flush(&tty->write_q); + break; + default: + return -EINVAL; + } + break; + case TIOCSCTTY: + if(SESS_LEADER(current) && (current->sid == tty->sid)) { + return 0; + } + if(!SESS_LEADER(current) || current->ctty) { + return -EPERM; + } + if(tty->sid) { + if((arg == 1) && IS_SUPERUSER) { + FOR_EACH_PROCESS(p) { + if((p->state != PROC_UNUSED) && (p->ctty == tty)) { + p->ctty = NULL; + } + } + } else { + return -EPERM; + } + } + current->ctty = tty; + tty->sid = current->sid; + tty->pgid = current->pgid; + break; + case TIOCGPGRP: + if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(__pid_t)))) { + return errno; + } + memcpy_b((void *)arg, &tty->pgid, sizeof(__pid_t)); + break; + case TIOCSPGRP: + if(arg < 1) { + return -EINVAL; + } + if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(__pid_t)))) { + return errno; + } + memcpy_b(&tty->pgid, (void *)arg, sizeof(__pid_t)); + break; + case TIOCGWINSZ: + if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(struct winsize)))) { + return errno; + } + memcpy_b((void *)arg, &tty->winsize, sizeof(struct winsize)); + break; + case TIOCSWINSZ: + { + struct winsize *ws = (struct winsize *)arg; + short int changed; + + if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct winsize)))) { + return errno; + } + changed = 0; + if(tty->winsize.ws_row != ws->ws_row) { + changed = 1; + } + if(tty->winsize.ws_col != ws->ws_col) { + changed = 1; + } + if(tty->winsize.ws_xpixel != ws->ws_xpixel) { + changed = 1; + } + if(tty->winsize.ws_ypixel != ws->ws_ypixel) { + changed = 1; + } + tty->winsize.ws_row = ws->ws_row; + tty->winsize.ws_col = ws->ws_col; + tty->winsize.ws_xpixel = ws->ws_xpixel; + tty->winsize.ws_ypixel = ws->ws_ypixel; + if(changed) { + kill_pgrp(tty->pgid, SIGWINCH); + } + } + break; + case TIOCNOTTY: + if(current->ctty != tty) { + return -ENOTTY; + } + if(SESS_LEADER(current)) { + disassociate_ctty(tty); + } + break; + case TIOCLINUX: + { + int val = *(unsigned char *)arg; + if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(unsigned char)))) { + return errno; + } + switch(val) { + case 12: /* get current console */ + return current_cons; + break; + default: + return -EINVAL; + break; + } + break; + } + + default: + return vt_ioctl(tty, cmd, arg); + } + return 0; +} + +int tty_lseek(struct inode *i, __off_t offset) +{ + return -ESPIPE; +} + +int tty_select(struct inode *i, int flag) +{ + struct tty *tty; + + if(!(tty = get_tty(i->rdev))) { + printk("%s(): oops! (%x)\n", __FUNCTION__, i->rdev); + return 0; + } + + switch(flag) { + case SEL_R: + if(tty->cooked_q.count > 0) { + if(!(tty->termios.c_lflag & ICANON) || ((tty->termios.c_lflag & ICANON) && tty->canon_data)) { + return 1; + } + } + break; + case SEL_W: + if(!tty->write_q.count) { + return 1; + } + break; + } + return 0; +} + +void tty_init(void) +{ + memset_b(tty_table, NULL, sizeof(tty_table)); +} diff --git a/drivers/char/tty_queue.c b/drivers/char/tty_queue.c new file mode 100644 index 00000000..b46820d5 --- /dev/null +++ b/drivers/char/tty_queue.c @@ -0,0 +1,259 @@ +/* + * fiwix/drivers/char/tty_queue.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +/* + * tty_queue.c implements a queue using a static-sized doubly linked list of a + * central pool of buffers which covers all ttys. + * + * head tail + * +--------------+ +--------------+ ... +--------------+ + * |prev|data|next| |prev|data|next| ... |prev|data|next| + * | / | | --> <-- | | --> ... <-- | | / | + * +--------------+ +--------------+ ... +--------------+ + * (cblock) (cblock) (cblock) + */ + +struct cblock cblock_pool[CB_POOL_SIZE]; +struct cblock *cblock_pool_head; + +static struct cblock *get_free_cblock(void) +{ + struct cblock *new = NULL; + + if(cblock_pool_head) { + new = cblock_pool_head; + cblock_pool_head = cblock_pool_head->next; + new->prev = new->next = NULL; + } + return new; +} + +static void put_free_cblock(struct cblock *old) +{ + old->prev = NULL; + old->next = cblock_pool_head; + cblock_pool_head = old; +} + +static struct cblock *insert_cblock_in_head(struct clist *q) +{ + struct cblock *cb; + + if(q->cb_num >= NR_CB_QUEUE) { + return NULL; + } + if(!(cb = get_free_cblock())) { + return NULL; + } + + /* initialize cblock */ + cb->start_off = cb->end_off = 0; + memset_b(cb->data, 0, CBSIZE); + cb->prev = cb->next = NULL; + q->cb_num++; + + if(!q->head) { + q->head = q->tail = cb; + } else { + cb->prev = NULL; + cb->next = q->head; + q->head->prev = cb; + q->head = cb; + } + return cb; +} + +static struct cblock *insert_cblock_in_tail(struct clist *q) +{ + struct cblock *cb; + + if(q->cb_num >= NR_CB_QUEUE) { + return NULL; + } + if(!(cb = get_free_cblock())) { + return NULL; + } + + /* initialize cblock */ + cb->start_off = cb->end_off = 0; + memset_b(cb->data, 0, CBSIZE); + cb->prev = cb->next = NULL; + q->cb_num++; + + if(!q->tail) { + q->head = q->tail = cb; + } else { + cb->prev = q->tail; + cb->next = NULL; + q->tail->next = cb; + q->tail = cb; + } + return cb; +} + +static void delete_cblock_from_head(struct clist *q) +{ + struct cblock *tmp; + + if(!q->head) { + return; + } + + tmp = q->head; + if(q->head == q->tail) { + q->head = q->tail = NULL; + } else { + q->head = q->head->next; + q->head->prev = NULL; + } + + q->count -= tmp->end_off - tmp->start_off; + q->cb_num--; + put_free_cblock(tmp); +} + +static void delete_cblock_from_tail(struct clist *q) +{ + struct cblock *tmp; + + if(!q->tail) { + return; + } + + tmp = q->tail; + if(q->head == q->tail) { + q->head = q->tail = NULL; + } else { + q->tail = q->tail->prev; + q->tail->next = NULL; + } + + q->count -= tmp->end_off - tmp->start_off; + q->cb_num--; + put_free_cblock(tmp); +} + +int tty_queue_putchar(struct tty *tty, struct clist *q, unsigned char ch) +{ + unsigned long int flags; + struct cblock *cb; + int errno; + + SAVE_FLAGS(flags); CLI(); + + cb = q->tail; + if(!cb) { + cb = insert_cblock_in_tail(q); + if(!cb) { + RESTORE_FLAGS(flags); + return -EAGAIN; + } + } + + if(cb->end_off < CBSIZE) { + cb->data[cb->end_off] = ch; + cb->end_off++; + q->count++; + errno = 0; + } else if(insert_cblock_in_tail(q)) { + tty_queue_putchar(tty, q, ch); + errno = 0; + } else { + errno = -EAGAIN; + } + + RESTORE_FLAGS(flags); + return errno; +} + +int tty_queue_unputchar(struct clist *q) +{ + unsigned long int flags; + struct cblock *cb; + unsigned char ch; + + SAVE_FLAGS(flags); CLI(); + + ch = 0; + cb = q->tail; + if(cb) { + if(cb->end_off > cb->start_off) { + ch = cb->data[cb->end_off - 1]; + cb->end_off--; + q->count--; + } + if(cb->end_off - cb->start_off == 0) { + delete_cblock_from_tail(q); + } + } + + RESTORE_FLAGS(flags); + return ch; +} + +unsigned char tty_queue_getchar(struct clist *q) +{ + unsigned long int flags; + struct cblock *cb; + unsigned char ch; + + SAVE_FLAGS(flags); CLI(); + + ch = 0; + cb = q->head; + if(cb) { + if(cb->start_off < cb->end_off) { + ch = cb->data[cb->start_off]; + cb->start_off++; + q->count--; + } + if(cb->end_off - cb->start_off == 0) { + delete_cblock_from_head(q); + } + } + + RESTORE_FLAGS(flags); + return ch; +} + +void tty_queue_flush(struct clist *q) +{ + unsigned long int flags; + + SAVE_FLAGS(flags); CLI(); + + while(q->head != NULL) { + delete_cblock_from_head(q); + } + + RESTORE_FLAGS(flags); +} + +void tty_queue_init(struct tty *tty) +{ + int n; + struct cblock *cb; + + memset_b(cblock_pool, NULL, sizeof(cblock_pool)); + + /* cblock free list initialization */ + cblock_pool_head = NULL; + n = CB_POOL_SIZE; + while(n--) { + cb = &cblock_pool[n]; + put_free_cblock(cb); + } + tty->read_q.head = tty->read_q.tail = NULL; + tty->cooked_q.head = tty->cooked_q.tail = NULL; + tty->write_q.head = tty->write_q.tail = NULL; +} diff --git a/drivers/char/vt.c b/drivers/char/vt.c new file mode 100644 index 00000000..ae94fbb2 --- /dev/null +++ b/drivers/char/vt.c @@ -0,0 +1,197 @@ +/* + * fiwix/drivers/char/vt.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int vt_ioctl(struct tty *tty, int cmd, unsigned long int arg) +{ + struct vconsole *vc; + int n, errno; + + /* only virtual consoles support the following ioctl commands */ + if(MAJOR(tty->dev) != VCONSOLES_MAJOR) { + return -ENXIO; + } + + vc = (struct vconsole *)tty->driver_data; + + switch(cmd) { + case KDGETLED: + if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned char)))) { + return errno; + } + memset_b((void *)arg, vc->led_status, sizeof(char)); + break; + + case KDSETLED: + if(arg > 7) { + return -EINVAL; + } + vc->led_status = arg; + set_leds(vc->led_status); + break; + + case KDGKBTYPE: + if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned char)))) { + return errno; + } + memset_b((void *)arg, KB_101, sizeof(char)); + break; + + case KDSETMODE: + if(arg != KD_TEXT && arg != KD_GRAPHICS) { + return -EINVAL; + } + if(vc->vc_mode != arg) { + vc->vc_mode = arg; + if(arg == KD_GRAPHICS) { + blank_screen(vc); + } else { + unblank_screen(vc); + } + } + break; + + case KDGETMODE: + if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned char)))) { + return errno; + } + memset_b((void *)arg, vc->vc_mode, sizeof(char)); + break; + + case KDSKBENT: + { + struct kbentry *k = (struct kbentry *)arg; + if((errno = check_user_area(VERIFY_WRITE, (void *)k, sizeof(struct kbentry)))) { + return errno; + } + if(k->kb_table < NR_MODIFIERS) { + if(k->kb_index < NR_SCODES) { + keymap[(k->kb_index * NR_MODIFIERS) + k->kb_table] = k->kb_value; + } else { + return -EINVAL; + } + } else { + printk("%s(): kb_table value '%d' not supported.\n", __FUNCTION__, k->kb_table); + return -EINVAL; + } + } + break; + + case VT_OPENQRY: + { + int *val = (int *)arg; + if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int)))) { + return errno; + } + for(n = 1; n < NR_VCONSOLES + 1; n++) { + tty = get_tty(MKDEV(VCONSOLES_MAJOR, n)); + if(!tty->count) { + break; + } + } + *val = (n < NR_VCONSOLES + 1 ? n : -1); + } + break; + + case VT_GETMODE: + { + struct vt_mode *vt_mode = (struct vt_mode *)arg; + if((errno = check_user_area(VERIFY_WRITE, (void *)vt_mode, sizeof(struct vt_mode)))) { + return errno; + } + memcpy_b(vt_mode, &vc->vt_mode, sizeof(struct vt_mode)); + } + break; + + case VT_SETMODE: + { + struct vt_mode *vt_mode = (struct vt_mode *)arg; + if((errno = check_user_area(VERIFY_READ, (void *)vt_mode, sizeof(struct vt_mode)))) { + return errno; + } + if(vt_mode->mode != VT_AUTO && vt_mode->mode != VT_PROCESS) { + return -EINVAL; + } + memcpy_b(&vc->vt_mode, vt_mode, sizeof(struct vt_mode)); + vc->vt_mode.frsig = 0; /* ignored */ + tty->pid = current->pid; + vc->switchto_tty = 0; + } + break; + + case VT_GETSTATE: + { + struct vt_stat *vt_stat = (struct vt_stat *)arg; + if((errno = check_user_area(VERIFY_WRITE, (void *)vt_stat, sizeof(struct vt_stat)))) { + return errno; + } + vt_stat->v_active = current_cons; + vt_stat->v_state = 1; /* /dev/tty0 is always opened */ + for(n = 1; n < NR_VCONSOLES + 1; n++) { + tty = get_tty(MKDEV(VCONSOLES_MAJOR, n)); + if(tty->count) { + vt_stat->v_state |= (1 << n); + } + } + } + break; + + case VT_RELDISP: + if(vc->vt_mode.mode != VT_PROCESS) { + return -EINVAL; + } + if(vc->switchto_tty < 0) { + if(arg != VT_ACKACQ) { + return -EINVAL; + } + } else { + if(arg) { + int switchto_tty; + switchto_tty = vc->switchto_tty; + vc->switchto_tty = -1; + vconsole_select_final(switchto_tty); + } else { + vc->switchto_tty = -1; + } + } + break; + + case VT_ACTIVATE: + if(current_cons == MINOR(tty->dev) || IS_SUPERUSER) { + if(!arg || arg > NR_VCONSOLES) { + return -ENXIO; + } + vconsole_select(--arg); + } else { + return -EPERM; + } + break; + + case VT_WAITACTIVE: + if(current_cons == MINOR(tty->dev)) { + break; + } + if(!arg || arg > NR_VCONSOLES) { + return -ENXIO; + } + printk("ACTIVATING another tty!! (cmd = 0x%x)\n", cmd); + break; + + default: + return -EINVAL; + } + return 0; +} diff --git a/fiwix.ld b/fiwix.ld new file mode 100644 index 00000000..3f0b4eb7 --- /dev/null +++ b/fiwix.ld @@ -0,0 +1,55 @@ +/* + * Linker script for the Fiwix kernel (3GB user / 1GB kernel). + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +OUTPUT_FORMAT("elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(start) /* entry point */ +vaddr = 0xC0000000; /* virtual base address at 3GB */ +paddr = 0x100000; /* physical address at 1MB */ + +/* define output sections */ +SECTIONS +{ + . = paddr; + + /* kernel setup code */ + .setup ALIGN(4096) : + { + *(.setup) + } + + . += vaddr; + + /* kernel code */ + .text : AT(ADDR(.text) - vaddr) + { + *(.text) + } + _etext = .; + + /* initialized data */ + .data ALIGN(4096) : AT(ADDR(.data) - vaddr) + { + *(.data) + *(.rodata*) + } + _edata = .; + + /* uninitialized data */ + .bss ALIGN(4096) : AT(ADDR(.bss) - vaddr) + { + *(COMMON*) + *(.bss*) + } + _end = .; + + /* remove information not needed */ + /DISCARD/ : + { + *(.eh_frame) + } +} diff --git a/fs/Makefile b/fs/Makefile new file mode 100644 index 00000000..6091bb56 --- /dev/null +++ b/fs/Makefile @@ -0,0 +1,25 @@ +# fiwix/fs/Makefile +# +# Copyright 2018, Jordi Sanfeliu. All rights reserved. +# Distributed under the terms of the Fiwix License. +# + +.S.o: + $(CC) -traditional -I$(INCLUDE) -c -o $@ $< +.c.o: + $(CC) $(CFLAGS) -c -o $@ $< + +FSDIRS = minix ext2 pipefs iso9660 procfs +FILESYSTEMS = minix/minix.o ext2/ext2.o pipefs/pipefs.o iso9660/iso9660.o \ + procfs/procfs.o +OBJS = filesystems.o devices.o buffer.o fd.o locks.o super.o inode.o \ + namei.o elf.o + +fs: $(OBJS) + @for n in $(FSDIRS) ; do (cd $$n ; $(MAKE)) ; done + $(LD) $(LDFLAGS) -r $(FILESYSTEMS) $(OBJS) -o fs.o + +clean: + @for n in $(FSDIRS) ; do (cd $$n ; $(MAKE) clean) ; done + rm -f *.o + diff --git a/fs/buffer.c b/fs/buffer.c new file mode 100644 index 00000000..73e3ef03 --- /dev/null +++ b/fs/buffer.c @@ -0,0 +1,443 @@ +/* + * fiwix/fs/buffer.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +/* + * buffer.c implements a cache with a free list as a doubly circular linked + * list and a chained hash table with doubly linked lists. + * + * hash table + * +--------+ +--------------+ +--------------+ +--------------+ + * | index | |prev|data|next| |prev|data|next| |prev|data|next| + * | 0 --> | / | | ---> <--- | | ---> <--- | | / | + * +--------+ +--------------+ +--------------+ +--------------+ + * +--------+ +--------------+ +--------------+ +--------------+ + * | index | |prev|data|next| |prev|data|next| |prev|data|next| + * | 1 --> | / | | ---> <--- | | ---> <--- | | / | + * +--------+ +--------------+ +--------------+ +--------------+ + * (buffer) (buffer) (buffer) + * ... + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFFER_HASH(dev, block) (((__dev_t)(dev) ^ (__blk_t)(block)) % (NR_BUF_HASH)) +#define NR_BUFFERS buffer_table_size / sizeof(struct buffer) +#define NR_BUF_HASH buffer_hash_table_size / sizeof(unsigned int) + +struct buffer *buffer_table; /* buffer pool */ +struct buffer *buffer_head; /* buffer pool head */ +struct buffer **buffer_hash_table; + +static struct resource sync_resource = { NULL, NULL }; + +static void insert_to_hash(struct buffer *buf) +{ + struct buffer **h; + int i; + + i = BUFFER_HASH(buf->dev, buf->block); + h = &buffer_hash_table[i]; + + if(!*h) { + *h = buf; + (*h)->prev_hash = (*h)->next_hash = NULL; + } else { + buf->prev_hash = NULL; + buf->next_hash = *h; + (*h)->prev_hash = buf; + *h = buf; + } +} + +static void remove_from_hash(struct buffer *buf) +{ + struct buffer **h; + int i; + + i = BUFFER_HASH(buf->dev, buf->block); + h = &buffer_hash_table[i]; + + while(*h) { + if(*h == buf) { + if((*h)->next_hash) { + (*h)->next_hash->prev_hash = (*h)->prev_hash; + } + if((*h)->prev_hash) { + (*h)->prev_hash->next_hash = (*h)->next_hash; + } + if(h == &buffer_hash_table[i]) { + *h = (*h)->next_hash; + } + break; + } + h = &(*h)->next_hash; + } +} + +static void remove_from_free_list(struct buffer *buf) +{ + buf->prev_free->next_free = buf->next_free; + buf->next_free->prev_free = buf->prev_free; + if(buf == buffer_head) { + buffer_head = buf->next_free; + } +} + +static void buffer_wait(struct buffer *buf) +{ + unsigned long int flags; + + for(;;) { + SAVE_FLAGS(flags); CLI(); + if(buf->locked) { + RESTORE_FLAGS(flags); + sleep(&buffer_wait, PROC_UNINTERRUPTIBLE); + } else { + break; + } + } + buf->locked = 1; + RESTORE_FLAGS(flags); +} + +static struct buffer * get_free_buffer(void) +{ + unsigned long int flags; + struct buffer *buf; + + /* no more buffers on free list */ + if(buffer_head == buffer_head->next_free) { + return NULL; + } + + for(;;) { + SAVE_FLAGS(flags); CLI(); + buf = buffer_head; + if(buf->locked) { + RESTORE_FLAGS(flags); + sleep(&buffer_wait, PROC_UNINTERRUPTIBLE); + } else { + break; + } + } + + buf = buffer_head; + remove_from_free_list(buf); + buf->locked = 1; + + RESTORE_FLAGS(flags); + return buf; +} + +static void sync_one_buffer(struct buffer *buf) +{ + struct device *d; + int errno; + + if(!(d = get_device(BLK_DEV, MAJOR(buf->dev)))) { + printk("WARNING: %s(): block device %d,%d not registered!\n", __FUNCTION__, MAJOR(buf->dev), MINOR(buf->dev)); + return; + } + + if(d->fsop && d->fsop->write_block) { + errno = d->fsop->write_block(buf->dev, buf->block, buf->data, buf->size); + if(errno < 0) { + if(errno == -EROFS) { + printk("WARNING: %s(): write protection on device %d,%d.\n", __FUNCTION__, MAJOR(buf->dev), MINOR(buf->dev), buf->block); + } else { + printk("WARNING: %s(): I/O error on device %d,%d.\n", __FUNCTION__, MAJOR(buf->dev), MINOR(buf->dev), buf->block); + } + return; + } + buf->dirty = 0; + } else { + printk("WARNING: %s(): device %d,%d does not have the write_block() method!\n", __FUNCTION__, MAJOR(buf->dev), MINOR(buf->dev)); + } +} + +static struct buffer * search_buffer_hash(__dev_t dev, __blk_t block, int size) +{ + struct buffer *buf; + int i; + + i = BUFFER_HASH(dev, block); + buf = buffer_hash_table[i]; + + while(buf) { + if(buf->dev == dev && buf->block == block && buf->size == size) { + return buf; + } + buf = buf->next_hash; + } + + return NULL; +} + +static struct buffer * getblk(__dev_t dev, __blk_t block, int size) +{ + unsigned long int flags; + struct buffer *buf; + + for(;;) { + if((buf = search_buffer_hash(dev, block, size))) { + SAVE_FLAGS(flags); CLI(); + if(buf->locked) { + RESTORE_FLAGS(flags); + sleep(&buffer_wait, PROC_UNINTERRUPTIBLE); + continue; + } + buf->locked = 1; + remove_from_free_list(buf); + RESTORE_FLAGS(flags); + return buf; + } + + if(!(buf = get_free_buffer())) { + printk("WARNING: %s(): no more buffers on free list!\n", __FUNCTION__); + sleep(&get_free_buffer, PROC_UNINTERRUPTIBLE); + continue; + } + + if(buf->dirty) { + sync_one_buffer(buf); + } else { + if(!buf->data) { + if(!(buf->data = (char *)kmalloc())) { + brelse(buf); + printk("%s(): returning NULL\n", __FUNCTION__); + return NULL; + } + kstat.buffers += (PAGE_SIZE / 1024); + } + } + + SAVE_FLAGS(flags); CLI(); + remove_from_hash(buf); /* remove it from old hash */ + buf->dev = dev; + buf->block = block; + buf->size = size; + insert_to_hash(buf); + buf->valid = 0; + RESTORE_FLAGS(flags); + return buf; + } +} + +struct buffer * get_dirty_buffer(__dev_t dev, __blk_t block, int size) +{ + unsigned long int flags; + struct buffer *buf; + + for(;;) { + if((buf = search_buffer_hash(dev, block, size))) { + if(buf->dirty) { + SAVE_FLAGS(flags); CLI(); + if(buf->locked) { + RESTORE_FLAGS(flags); + sleep(&buffer_wait, PROC_UNINTERRUPTIBLE); + continue; + } + buf->locked = 1; + remove_from_free_list(buf); + RESTORE_FLAGS(flags); + break; + } + } + buf = NULL; + break; + } + + return buf; +} + +struct buffer * bread(__dev_t dev, __blk_t block, int size) +{ + struct buffer *buf; + struct device *d; + + if(!(d = get_device(BLK_DEV, MAJOR(dev)))) { + printk("WARNING: %s(): device major %d not found!\n", __FUNCTION__, MAJOR(dev)); + return NULL; + } + + if((buf = getblk(dev, block, size))) { + if(!buf->valid) { + if(d->fsop && d->fsop->read_block) { + if(d->fsop->read_block(dev, block, buf->data, size) >= 0) { + buf->valid = 1; + } + } + } + if(buf->valid) { + return buf; + } + brelse(buf); + } + + printk("WARNING: %s(): returning NULL!\n", __FUNCTION__); + return NULL; +} + +void bwrite(struct buffer *buf) +{ + buf->dirty = 1; + buf->valid = 1; + brelse(buf); +} + +void brelse(struct buffer *buf) +{ + unsigned long int flags; + + SAVE_FLAGS(flags); CLI(); + + if(!buffer_head) { + buf->prev_free = buf->next_free = buf; + buffer_head = buf; + } else { + buf->next_free = buffer_head; + buf->prev_free = buffer_head->prev_free; + buffer_head->prev_free->next_free = buf; + buffer_head->prev_free = buf; + + /* if not valid place the buffer at the head of the free list */ + if(!buf->valid) { + buffer_head = buf; + } + } + buf->locked = 0; + + RESTORE_FLAGS(flags); + + wakeup(&get_free_buffer); + wakeup(&buffer_wait); +} + +void sync_buffers(__dev_t dev) +{ + struct buffer *buf; + int n; + + buf = &buffer_table[0]; + + lock_resource(&sync_resource); + for(n = 0; n < NR_BUFFERS; n++) { + if(buf->dirty) { + if(!dev || buf->dev == dev) { + buffer_wait(buf); + sync_one_buffer(buf); + buf->locked = 0; + wakeup(&buffer_wait); + } + } + buf++; + } + unlock_resource(&sync_resource); + return; +} + +void invalidate_buffers(__dev_t dev) +{ + unsigned long int flags; + unsigned int n; + struct buffer *buf; + + buf = &buffer_table[0]; + SAVE_FLAGS(flags); CLI(); + + for(n = 0; n < NR_BUFFERS; n++) { + if(!buf->locked && buf->dev == dev) { + buffer_wait(buf); + remove_from_hash(buf); + buf->valid = 0; + buf->locked = 0; + wakeup(&buffer_wait); + } + buf++; + } + + RESTORE_FLAGS(flags); + /* FIXME: invalidate_pages(dev); */ +} + +/* + * When kernel runs out of pages, kswapd is awaken and it calls this function + * which goes throught the buffer free list, freeing up to NR_BUF_RECLAIM + * buffers. + */ +int reclaim_buffers(void) +{ + struct buffer *buf, *first; + int reclaimed; + + reclaimed = 0; + first = NULL; + + for(;;) { + if(!(buf = get_free_buffer())) { + printk("WARNING: %s(): no more buffers on free list!\n", __FUNCTION__); + sleep(&get_free_buffer, PROC_UNINTERRUPTIBLE); + continue; + } + + remove_from_hash(buf); + if(buf->dirty) { + sync_one_buffer(buf); + } + + /* this ensures the buffer will go to the tail */ + buf->valid = 1; + + if(first) { + if(first == buf) { + brelse(buf); + break; + } + } else { + first = buf; + } + if(buf->data) { + kfree((unsigned int)buf->data); + buf->data = NULL; + kstat.buffers -= (PAGE_SIZE / 1024); + reclaimed++; + if(reclaimed == NR_BUF_RECLAIM) { + brelse(buf); + break; + } + } + brelse(buf); + do_sched(); + } + + wakeup(&buffer_wait); + return reclaimed; +} + +void buffer_init(void) +{ + struct buffer *buf; + unsigned int n; + + memset_b(buffer_table, NULL, buffer_table_size); + memset_b(buffer_hash_table, NULL, buffer_hash_table_size); + for(n = 0; n < NR_BUFFERS; n++) { + buf = &buffer_table[n]; + brelse(buf); + } +} diff --git a/fs/devices.c b/fs/devices.c new file mode 100644 index 00000000..1a667f8e --- /dev/null +++ b/fs/devices.c @@ -0,0 +1,348 @@ +/* + * fiwix/fs/devices.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct device chr_device_table[NR_CHRDEV]; +struct device blk_device_table[NR_BLKDEV]; + +struct fs_operations def_chr_fsop = { + 0, + 0, + + chr_dev_open, + NULL, /* close */ + NULL, /* read */ + NULL, /* write */ + NULL, /* ioctl */ + NULL, /* lseek */ + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lockup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* stats */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +struct fs_operations def_blk_fsop = { + 0, + 0, + + blk_dev_open, + blk_dev_close, + blk_dev_read, + blk_dev_write, + blk_dev_ioctl, + blk_dev_lseek, + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lockup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* stats */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +int register_device(int type, struct device *new_d) +{ + struct device *d; + + switch(type) { + case CHR_DEV: + if(new_d->major >= NR_CHRDEV) { + printk("%s(): character device major %d is greater than NR_CHRDEV (%d).\n", __FUNCTION__, new_d->major, NR_CHRDEV); + return 1; + } + d = chr_device_table; + break; + case BLK_DEV: + if(new_d->major >= NR_BLKDEV) { + printk("%s(): block device major %d is greater than NR_BLKDEV (%d).\n", __FUNCTION__, new_d->major, NR_BLKDEV); + return 1; + } + d = blk_device_table; + break; + default: + printk("WARNING: %s(): invalid device type %d.\n", __FUNCTION__, type); + return 1; + break; + } + + if(d[new_d->major].major) { + printk("%s(): device '%s' with major %d is already registered.\n", __FUNCTION__, new_d->name, new_d->major); + return 1; + } + memcpy_b(d + new_d->major, new_d, sizeof(struct device)); + return 0; +} + +struct device * get_device(int type, unsigned char major) +{ + char *name; + struct device *d; + + switch(type) { + case CHR_DEV: + if(major >= NR_CHRDEV) { + printk("%s(): character device major %d is greater than NR_CHRDEV (%d).\n", __FUNCTION__, major, NR_CHRDEV); + return NULL; + } + d = chr_device_table; + name = "character"; + break; + case BLK_DEV: + if(major >= NR_BLKDEV) { + printk("%s(): block device major %d is greater than NR_BLKDEV (%d).\n", __FUNCTION__, major, NR_BLKDEV); + return NULL; + } + d = blk_device_table; + name = "block"; + break; + default: + printk("WARNING: %s(): invalid device type %d.\n", __FUNCTION__, type); + return NULL; + } + + if(d[major].major) { + return &d[major]; + } + + printk("WARNING: %s(): no %s device found with major %d.\n", __FUNCTION__, name, major); + return NULL; +} + +int chr_dev_open(struct inode *i, struct fd *fd_table) +{ + struct device *d; + + if((d = get_device(CHR_DEV, MAJOR(i->rdev)))) { + i->fsop = d->fsop; + if(i->fsop && i->fsop->open) { + return i->fsop->open(i, fd_table); + } + } + + return -EINVAL; +} + +int blk_dev_open(struct inode *i, struct fd *fd_table) +{ + struct device *d; + + if((d = get_device(BLK_DEV, MAJOR(i->rdev)))) { + if(d->fsop && d->fsop->open) { + return d->fsop->open(i, fd_table); + } + } + + return -EINVAL; +} + +int blk_dev_close(struct inode *i, struct fd *fd_table) +{ + struct device *d; + + if((d = get_device(BLK_DEV, MAJOR(i->rdev)))) { + if(d->fsop && d->fsop->close) { + return d->fsop->close(i, fd_table); + } + } + + printk("WARNING: %s(): block device %d,%d does not have the close() method.\n", __FUNCTION__, MAJOR(i->rdev), MINOR(i->rdev)); + return -EINVAL; +} + +int blk_dev_read(struct inode *i, struct fd *fd_table, char *buffer, __size_t count) +{ + __blk_t block; + __off_t total_read, device_size; + int blksize; + unsigned int boffset, bytes; + struct buffer *buf; + struct device *d; + + if(!(d = get_device(BLK_DEV, MAJOR(i->rdev)))) { + return -EINVAL; + } + + blksize = d->blksize ? d->blksize : BLKSIZE_1K; + total_read = 0; + if(!d->device_data) { + printk("%s(): don't know the size of the block device %d,%d.\n", __FUNCTION__, MAJOR(i->rdev), MINOR(i->rdev)); + return -EIO; + } + + /* check if device size is greater than 4GB (in 32bit would overflow) */ + if(((__off_t *)d->device_data)[MINOR(i->rdev)] >= (4096 * 1024)) { + printk("WARNING: %s(): device size > 4GB (would overflow). Defaulting to 4GB.\n", __FUNCTION__); + device_size = (unsigned int)4096 * 1024 * 1023; + } else { + device_size = ((__off_t *)d->device_data)[MINOR(i->rdev)] * 1024; + } + + count = (fd_table->offset + count > device_size) ? device_size - fd_table->offset : count; + if(!count || fd_table->offset > device_size) { + return 0; + } + while(count) { + boffset = fd_table->offset % blksize; + block = (fd_table->offset / blksize); + if(!(buf = bread(i->rdev, block, blksize))) { + return -EIO; + } + bytes = blksize - boffset; + bytes = MIN(bytes, count); + memcpy_b(buffer + total_read, buf->data + boffset, bytes); + total_read += bytes; + count -= bytes; + boffset += bytes; + boffset %= blksize; + fd_table->offset += bytes; + brelse(buf); + } + return total_read; +} + +int blk_dev_write(struct inode *i, struct fd *fd_table, const char *buffer, __size_t count) +{ + __blk_t block; + __off_t total_written, device_size; + int blksize; + unsigned int boffset, bytes; + struct buffer *buf; + struct device *d; + + if(!(d = get_device(BLK_DEV, MAJOR(i->rdev)))) { + return -EINVAL; + } + + blksize = d->blksize ? d->blksize : BLKSIZE_1K; + total_written = 0; + if(!d->device_data) { + printk("%s(): don't know the size of the block device %d,%d.\n", __FUNCTION__, MAJOR(i->rdev), MINOR(i->rdev)); + return -EIO; + } + + /* check if device size is greater than 4GB (in 32bit would overflow) */ + if(((__off_t *)d->device_data)[MINOR(i->rdev)] >= (4096 * 1024)) { + printk("WARNING: %s(): device size > 4GB (would overflow). Defaulting to 4GB.\n", __FUNCTION__); + device_size = (unsigned int)4096 * 1024 * 1023; + } else { + device_size = ((__off_t *)d->device_data)[MINOR(i->rdev)] * 1024; + } + + count = (fd_table->offset + count > device_size) ? device_size - fd_table->offset : count; + if(!count || fd_table->offset > device_size) { + printk("%s(): I/O error on device %d,%d, offset %u.\n", __FUNCTION__, MAJOR(i->rdev), MINOR(i->rdev), fd_table->offset); + return -EIO; + } + while(count) { + boffset = fd_table->offset % blksize; + block = (fd_table->offset / blksize); + if(!(buf = bread(i->rdev, block, blksize))) { + return -EIO; + } + bytes = blksize - boffset; + bytes = MIN(bytes, count); + memcpy_b(buf->data + boffset, buffer + total_written, bytes); + total_written += bytes; + count -= bytes; + boffset += bytes; + boffset %= blksize; + fd_table->offset += bytes; + bwrite(buf); + } + return total_written; +} + +int blk_dev_ioctl(struct inode *i, int cmd, unsigned long int arg) +{ + struct device *d; + + if((d = get_device(BLK_DEV, MAJOR(i->rdev)))) { + if(d->fsop && d->fsop->ioctl) { + return d->fsop->ioctl(i, cmd, arg); + } + } + + printk("WARNING: %s(): block device %d,%d does not have the ioctl() method.\n", __FUNCTION__, MAJOR(i->rdev), MINOR(i->rdev)); + return -EINVAL; +} + +int blk_dev_lseek(struct inode *i, __off_t offset) +{ + struct device *d; + + if((d = get_device(BLK_DEV, MAJOR(i->rdev)))) { + if(d->fsop && d->fsop->lseek) { + return d->fsop->lseek(i, offset); + } + } + + return offset; +} + +void dev_init(void) +{ + memset_b(chr_device_table, NULL, sizeof(chr_device_table)); + memset_b(blk_device_table, NULL, sizeof(blk_device_table)); +} diff --git a/fs/elf.c b/fs/elf.c new file mode 100644 index 00000000..5c2757c1 --- /dev/null +++ b/fs/elf.c @@ -0,0 +1,719 @@ +/* + * fiwix/fs/elf.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AT_ITEMS 12 /* ELF Auxiliary Vectors */ + +static int check_elf(struct elf32_hdr *elf32_h) +{ + if(elf32_h->e_ident[EI_MAG0] != ELFMAG0 || + elf32_h->e_ident[EI_MAG1] != ELFMAG1 || + elf32_h->e_ident[EI_MAG2] != ELFMAG2 || + elf32_h->e_ident[EI_MAG3] != ELFMAG3 || + (elf32_h->e_type != ET_EXEC && elf32_h->e_type != ET_DYN) || + elf32_h->e_machine != EM_386) { + return -EINVAL; + } + return 0; +} + +static void free_barg_pages(struct binargs *barg) +{ + int n; + + for(n = 0; n < ARG_MAX; n++) { + if(barg->page[n]) { + kfree(barg->page[n]); + } + } +} + +static int copy_strings(struct binargs *barg, char *argv[], char *envp[]) +{ + int n, p, offset; + unsigned int ae_str_len; + char *page, *str; + + p = ARG_MAX - 1; + ae_str_len = barg->argv_len + barg->envp_len + 4; + p -= ae_str_len / PAGE_SIZE; + offset = PAGE_SIZE - (ae_str_len % PAGE_SIZE); + if(offset == PAGE_SIZE) { + offset = 0; + p++; + } + barg->offset = offset; + for(n = p; n < ARG_MAX; n++) { + if(!(barg->page[n] = kmalloc())) { + free_barg_pages(barg); + return -ENOMEM; + } + } + for(n = 0; n < barg->argc; n++) { + str = argv[n]; + page = (char *)barg->page[p]; + while(*str) { + *(page + offset) = *str; + offset++; + str++; + if(offset == PAGE_SIZE) { + p++; + offset = 0; + page = (char *)barg->page[p]; + } + } + *(page + offset++) = NULL; + if(offset == PAGE_SIZE) { + p++; + offset = 0; + } + } + for(n = 0; n < barg->envc; n++) { + str = envp[n]; + page = (char *)barg->page[p]; + while(*str) { + *(page + offset) = *str; + offset++; + str++; + if(offset == PAGE_SIZE) { + p++; + offset = 0; + page = (char *)barg->page[p]; + } + } + *(page + offset++) = NULL; + if(offset == PAGE_SIZE) { + p++; + offset = 0; + } + } + + return 0; +} + +/* + * Setup the initial process stack (System V ABI for i386) + * ---------------------------------------------------------------------------- + * 0xBFFFFFFF + * +---------------+ \ + * | envp[] str | | + * +---------------+ | + * | argv[] str | | + * +---------------+ | + * | NULL | | + * +---------------+ | + * | ELF Aux.Vect. | | + * +---------------+ | + * | NULL | | elf_create_stack() setups this section + * +---------------+ | + * | envp[] ptr | | + * +---------------+ | + * | NULL | | + * +---------------+ | + * | argv[] ptr | | + * +---------------+ | + * | argc | | + * +---------------+ / + * | stack pointer | grows toward lower addresses + * +---------------+ || + * |...............| \/ + * |...............| + * |...............| + * |...............| /\ + * +---------------+ || + * | brk (heap) | grows toward higher addresses + * +---------------+ + * | .bss section | + * +---------------+ + * | .data section | + * +---------------+ + * | .text section | + * +---------------+ + * 0x08048000 + */ +static void elf_create_stack(struct binargs *barg, unsigned int *sp, unsigned int str_ptr, int at_base, struct elf32_hdr *elf32_h, unsigned int phdr_addr) +{ + unsigned int n, addr; + char *str; + + /* copy strings */ + for(n = 0; n < ARG_MAX; n++) { + if(barg->page[n]) { + addr = KERNEL_BASE_ADDR - ((ARG_MAX - n) * PAGE_SIZE); + memcpy_b((void *)addr, (void *)barg->page[n], PAGE_SIZE); + } + } + +#ifdef __DEBUG__ + printk("sp = 0x%08x\n", sp); +#endif /*__DEBUG__ */ + + /* copy the value of 'argc' into the stack */ + memcpy_l((void *)sp, &barg->argc, 1); +#ifdef __DEBUG__ + printk("at 0x%08x -> argc\n", sp); +#endif /*__DEBUG__ */ + sp++; + + /* copy as many pointers to strings as 'argc' */ + current->argv = (char **)sp; + for(n = 0; n < barg->argc; n++) { + memcpy_l((void *)sp, &str_ptr, 1); + str = (char *)str_ptr; +#ifdef __DEBUG__ + printk("at 0x%08x -> str_ptr(%d) = 0x%08x (+ %d)\n", sp, n, str_ptr, strlen(str) + 1); +#endif /*__DEBUG__ */ + sp++; + str_ptr += strlen(str) + 1; + } + + /* the last element of 'argv[]' must be a NULL-pointer */ + memset_l((void *)sp, NULL, 1); +#ifdef __DEBUG__ + printk("at 0x%08x -> -------------- = 0x%08x\n", sp, 0); +#endif /*__DEBUG__ */ + sp++; + + /* copy as many pointers to strings as 'envc' */ + current->envp = (char **)sp; + for(n = 0; n < barg->envc; n++) { + memcpy_l((void *)sp, &str_ptr, 1); + str = (char *)str_ptr; +#ifdef __DEBUG__ + printk("at 0x%08x -> str_ptr(%d) = 0x%08x (+ %d)\n", sp, n, str_ptr, strlen(str) + 1); +#endif /*__DEBUG__ */ + sp++; + str_ptr += strlen(str) + 1; + } + + /* the last element of 'envp[]' must be a NULL-pointer */ + memset_l((void *)sp, NULL, 1); +#ifdef __DEBUG__ + printk("at 0x%08x -> -------------- = 0x%08x\n", sp, 0); +#endif /*__DEBUG__ */ + sp++; + + + /* copy the Auxiliar Table Items (dlinfo_items) */ + if(at_base) { + memset_l((void *)sp, AT_PHDR, 1); +#ifdef __DEBUG__ + printk("at 0x%08x -> AT_PHDR = %d", sp, *sp); +#endif /*__DEBUG__ */ + sp++; + + memcpy_l((void *)sp, &phdr_addr, 1); +#ifdef __DEBUG__ + printk("\t\tAT_PHDR = 0x%08x\n", *sp); +#endif /*__DEBUG__ */ + sp++; + + memset_l((void *)sp, AT_PHENT, 1); +#ifdef __DEBUG__ + printk("at 0x%08x -> AT_PHENT = %d", sp, *sp); +#endif /*__DEBUG__ */ + sp++; + + memset_l((void *)sp, sizeof(struct elf32_phdr), 1); +#ifdef __DEBUG__ + printk("\t\tAT_PHENT = %d\n", *sp); +#endif /*__DEBUG__ */ + sp++; + + memset_l((void *)sp, AT_PHNUM, 1); +#ifdef __DEBUG__ + printk("at 0x%08x -> AT_PHNUM = %d", sp, *sp); +#endif /*__DEBUG__ */ + sp++; + + memset_l((void *)sp, 0, 1); + memcpy_w((void *)sp, &elf32_h->e_phnum, 1); +#ifdef __DEBUG__ + printk("\t\tAT_PHNUM = %d\n", *sp); +#endif /*__DEBUG__ */ + sp++; + + memset_l((void *)sp, AT_PAGESZ, 1); +#ifdef __DEBUG__ + printk("at 0x%08x -> AT_PGSIZE = %d", sp, *sp); +#endif /*__DEBUG__ */ + sp++; + + memset_l((void *)sp, PAGE_SIZE, 1); +#ifdef __DEBUG__ + printk("\t\tAT_PGSIZE = %d\n", *sp); +#endif /*__DEBUG__ */ + sp++; + + memset_l((void *)sp, AT_BASE, 1); +#ifdef __DEBUG__ + printk("at 0x%08x -> AT_BASE = %d", sp, *sp); +#endif /*__DEBUG__ */ + sp++; + + memset_l((void *)sp, at_base, 1); +#ifdef __DEBUG__ + printk("\t\tAT_BASE = 0x%08x\n", sp); +#endif /*__DEBUG__ */ + sp++; + + memset_l((void *)sp, AT_FLAGS, 1); +#ifdef __DEBUG__ + printk("at 0x%08x -> AT_FLAGS = %d", sp, *sp); +#endif /*__DEBUG__ */ + sp++; + + memset_l((void *)sp, NULL, 1); +#ifdef __DEBUG__ + printk("\t\tAT_FLAGS = %d\n", *sp); +#endif /*__DEBUG__ */ + sp++; + + memset_l((void *)sp, AT_ENTRY, 1); +#ifdef __DEBUG__ + printk("at 0x%08x -> AT_ENTRY = %d ", sp, *sp); +#endif /*__DEBUG__ */ + sp++; + + memcpy_l((void *)sp, &elf32_h->e_entry, 1); +#ifdef __DEBUG__ + printk("\t\tAT_ENTRY = 0x%08x\n", *sp); +#endif /*__DEBUG__ */ + sp++; + + memset_l((void *)sp, AT_UID, 1); +#ifdef __DEBUG__ + printk("at 0x%08x -> AT_UID = %d", sp, *sp); +#endif /*__DEBUG__ */ + sp++; + + memcpy_l((void *)sp, ¤t->uid, 1); +#ifdef __DEBUG__ + printk("\t\tAT_UID = %d\n", *sp); +#endif /*__DEBUG__ */ + sp++; + + memset_l((void *)sp, AT_EUID, 1); +#ifdef __DEBUG__ + printk("at 0x%08x -> AT_EUID = %d", sp, *sp); +#endif /*__DEBUG__ */ + sp++; + + memcpy_l((void *)sp, ¤t->euid, 1); +#ifdef __DEBUG__ + printk("\t\tAT_EUID = %d\n", *sp); +#endif /*__DEBUG__ */ + sp++; + + memset_l((void *)sp, AT_GID, 1); +#ifdef __DEBUG__ + printk("at 0x%08x -> AT_GID = %d", sp, *sp); +#endif /*__DEBUG__ */ + sp++; + + memcpy_l((void *)sp, ¤t->gid, 1); +#ifdef __DEBUG__ + printk("\t\tAT_GID = %d\n", *sp); +#endif /*__DEBUG__ */ + sp++; + + memset_l((void *)sp, AT_EGID, 1); +#ifdef __DEBUG__ + printk("at 0x%08x -> AT_EGID = %d", sp, *sp); +#endif /*__DEBUG__ */ + sp++; + + memcpy_l((void *)sp, ¤t->egid, 1); +#ifdef __DEBUG__ + printk("\t\tAT_EGID = %d\n", *sp); +#endif /*__DEBUG__ */ + sp++; + } + + memset_l((void *)sp, AT_NULL, 1); +#ifdef __DEBUG__ + printk("at 0x%08x -> AT_NULL = %d", sp, *sp); +#endif /*__DEBUG__ */ + sp++; + + memset_l((void *)sp, NULL, 1); +#ifdef __DEBUG__ + printk("\t\tAT_NULL = %d\n", *sp); +#endif /*__DEBUG__ */ + sp++; + +#ifdef __DEBUG__ + for(n = 0; n < barg->argc; n++) { + printk("at 0x%08x -> argv[%d] = '%s'\n", current->argv[n], n, current->argv[n]); + } + for(n = 0; n < barg->envc; n++) { + printk("at 0x%08x -> envp[%d] = '%s'\n", current->envp[n], n, current->envp[n]); + } +#endif /*__DEBUG__ */ +} + +static int elf_load_interpreter(struct inode *ii) +{ + int n, errno; + struct buffer *buf; + struct elf32_hdr *elf32_h; + struct elf32_phdr *elf32_ph, *last_ptload; + __blk_t block; + unsigned int start, end, length; + unsigned int prot; + char *data; + char type; + + if((block = bmap(ii, 0, FOR_READING)) < 0) { + return block; + } + if(!(buf = bread(ii->dev, block, ii->sb->s_blocksize))) { + return -EIO; + } + + /* + * The contents of the buffer is copied and then freed immediately to + * make sure that it won't conflict while zeroing the BSS fractional + * page, in case that the same block is requested during the page fault. + */ + if(!(data = (void *)kmalloc())) { + brelse(buf); + return -ENOMEM; + } + memcpy_b(data, buf->data, ii->sb->s_blocksize); + brelse(buf); + + elf32_h = (struct elf32_hdr *)data; + if(check_elf(elf32_h)) { + kfree((unsigned int)data); + return -ELIBBAD; + } + + last_ptload = NULL; + for(n = 0; n < elf32_h->e_phnum; n++) { + elf32_ph = (struct elf32_phdr *)(data + elf32_h->e_phoff + (sizeof(struct elf32_phdr) * n)); + if(elf32_ph->p_type == PT_LOAD) { +#ifdef __DEBUG__ + printk("p_offset = 0x%08x\n", elf32_ph->p_offset); + printk("p_vaddr = 0x%08x\n", elf32_ph->p_vaddr); + printk("p_paddr = 0x%08x\n", elf32_ph->p_paddr); + printk("p_filesz = 0x%08x\n", elf32_ph->p_filesz); + printk("p_memsz = 0x%08x\n\n", elf32_ph->p_memsz); +#endif /*__DEBUG__ */ + start = (elf32_ph->p_vaddr & PAGE_MASK) + MMAP_START; + length = (elf32_ph->p_vaddr & ~PAGE_MASK) + elf32_ph->p_filesz; + type = P_DATA; + prot = 0; + if(elf32_ph->p_flags & PF_R) { + prot = PROT_READ; + } + if(elf32_ph->p_flags & PF_W) { + prot |= PROT_WRITE; + } + if(elf32_ph->p_flags & PF_X) { + prot |= PROT_EXEC; + type = P_TEXT; + } + errno = do_mmap(ii, start, length, prot, MAP_PRIVATE | MAP_FIXED, elf32_ph->p_offset & PAGE_MASK, type, O_RDONLY); + if(errno < 0 && errno > -PAGE_SIZE) { + kfree((unsigned int)data); + send_sig(current, SIGSEGV); + return -ENOEXEC; + } + last_ptload = elf32_ph; + } + } + + if(!last_ptload) { + printk("WARNING: 'last_ptload' is NULL!\n"); + } + elf32_ph = last_ptload; + + /* zero-fill the fractional page of the DATA section */ + end = PAGE_ALIGN(elf32_ph->p_vaddr + elf32_ph->p_filesz) + MMAP_START; + start = (elf32_ph->p_vaddr + elf32_ph->p_filesz) + MMAP_START; + length = end - start; + + /* this will generate a page fault which will load the page in */ + memset_b((void *)start, NULL, length); + + /* setup the BSS section */ + start = (elf32_ph->p_vaddr + elf32_ph->p_filesz) + MMAP_START; + start = PAGE_ALIGN(start); + end = (elf32_ph->p_vaddr + elf32_ph->p_memsz) + MMAP_START; + end = PAGE_ALIGN(end); + length = end - start; + errno = do_mmap(NULL, start, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, 0, P_BSS, 0); + if(errno < 0 && errno > -PAGE_SIZE) { + kfree((unsigned int)data); + send_sig(current, SIGSEGV); + return -ENOEXEC; + } + kfree((unsigned int)data); + return elf32_h->e_entry + MMAP_START; +} + +int elf_load(struct inode *i, char *argv[], char *envp[], struct sigcontext *sc) +{ + int n, errno; + struct buffer *buf; + struct binargs barg; + struct elf32_hdr *elf32_h; + struct elf32_phdr *elf32_ph, *last_ptload; + struct inode *ii; + __blk_t block; + unsigned int start, end, length; + unsigned int prot; + char *interpreter; + char *data; + int at_base, phdr_addr; + char type; + unsigned int ae_ptr_len, ae_str_len; + unsigned int sp, str; + + if((block = bmap(i, 0, FOR_READING)) < 0) { + return block; + } + if(!(buf = bread(i->dev, block, i->sb->s_blocksize))) { + return -EIO; + } + + /* + * The contents of the buffer is copied and then freed immediately to + * make sure that it won't conflict while zeroing the BSS fractional + * page, in case that the same block is requested during the page fault. + */ + if(!(data = (void *)kmalloc())) { + brelse(buf); + return -ENOMEM; + } + memcpy_b(data, buf->data, i->sb->s_blocksize); + brelse(buf); + + elf32_h = (struct elf32_hdr *)data; + if(check_elf(elf32_h)) { + kfree((unsigned int)data); + if(current->pid == INIT) { + PANIC("%s has an unrecognized binary format.\n", INIT_PROGRAM); + } + return -ENOEXEC; + } + + /* check if an interpreter is required */ + interpreter = NULL; + ii = NULL; + phdr_addr = at_base = 0; + for(n = 0; n < elf32_h->e_phnum; n++) { + elf32_ph = (struct elf32_phdr *)(data + elf32_h->e_phoff + (sizeof(struct elf32_phdr) * n)); + if(elf32_ph->p_type == PT_INTERP) { + at_base = MMAP_START; + interpreter = data + elf32_ph->p_offset; + if(namei(interpreter, &ii, NULL, FOLLOW_LINKS)) { + printk("%s(): can't find interpreter '%s'.\n", __FUNCTION__, interpreter); + kfree((unsigned int)data); + send_sig(current, SIGSEGV); + return -ELIBACC; + } +#ifdef __DEBUG__ + printk("p_offset = 0x%08x\n", elf32_ph->p_offset); + printk("p_vaddr = 0x%08x\n", elf32_ph->p_vaddr); + printk("p_paddr = 0x%08x\n", elf32_ph->p_paddr); + printk("p_filesz = 0x%08x\n", elf32_ph->p_filesz); + printk("p_memsz = 0x%08x\n", elf32_ph->p_memsz); + printk("using interpreter '%s'\n", interpreter); +#endif /*__DEBUG__ */ + } + } + + for(n = 0; n < ARG_MAX; n++) { + barg.page[n] = 0; + } + barg.argv_len = barg.envp_len = 0; + + for(n = 0; argv[n]; n++) { + if((errno = check_user_area(VERIFY_READ, argv[n], sizeof(char *)))) { + kfree((unsigned int)data); + return errno; + } + barg.argv_len += strlen(argv[n]) + 1; + } + barg.argc = n; + + for(n = 0; envp[n]; n++) { + if((errno = check_user_area(VERIFY_READ, envp[n], sizeof(char *)))) { + kfree((unsigned int)data); + return errno; + } + barg.envp_len += strlen(envp[n]) + 1; + } + barg.envc = n; + + strncpy(current->argv0, argv[0], NAME_MAX); + + /* + * calculate the final size of 'ae_ptr_len' based on: + * - argc = 4 bytes (unsigned int) + * - barg.argc = (num. of pointers to strings + 1 NULL) x 4 bytes (unsigned int) + * - barg.envc = (num. of pointers to strings + 1 NULL) x 4 bytes (unsigned int) + */ + ae_ptr_len = (1 + (barg.argc + 1) + (barg.envc + 1)) * sizeof(unsigned int); + ae_str_len = barg.argv_len + barg.envp_len; + if(ae_ptr_len + ae_str_len > (ARG_MAX * PAGE_SIZE)) { + printk("WARNING: %s(): argument list (%d) exceeds ARG_MAX (%d)!\n", __FUNCTION__, ae_ptr_len + ae_str_len, ARG_MAX * PAGE_SIZE); + kfree((unsigned int)data); + return -E2BIG; + } + +#ifdef __DEBUG__ + printk("argc=%d (argv_len=%d) envc=%d (envp_len=%d) ae_ptr_len=%d ae_str_len=%d\n", barg.argc, barg.argv_len, barg.envc, barg.envp_len, ae_ptr_len, ae_str_len); +#endif /*__DEBUG__ */ + + /* save 'argv' and 'envp' into the kernel space */ + if((errno = copy_strings(&barg, argv, envp))) { + kfree((unsigned int)data); + return errno; + } + + + /* point of no return */ + + release_binary(); + current->rss = 0; + + current->entry_address = elf32_h->e_entry; + if(interpreter) { + errno = elf_load_interpreter(ii); + if(errno < 0) { + printk("%s(): unable to load the interpreter '%s'.\n", __FUNCTION__, interpreter); + kfree((unsigned int)data); + free_barg_pages(&barg); + iput(ii); + send_sig(current, SIGKILL); + return errno; + } + current->entry_address = errno; + iput(ii); + } + + elf32_ph = last_ptload = NULL; + for(n = 0; n < elf32_h->e_phnum; n++) { + elf32_ph = (struct elf32_phdr *)(data + elf32_h->e_phoff + (sizeof(struct elf32_phdr) * n)); + if(elf32_ph->p_type == PT_PHDR) { + phdr_addr = elf32_ph->p_vaddr; + } + if(elf32_ph->p_type == PT_LOAD) { + start = elf32_ph->p_vaddr & PAGE_MASK; + length = (elf32_ph->p_vaddr & ~PAGE_MASK) + elf32_ph->p_filesz; + type = P_DATA; + prot = 0; + if(elf32_ph->p_flags & PF_R) { + prot = PROT_READ; + } + if(elf32_ph->p_flags & PF_W) { + prot |= PROT_WRITE; + } + if(elf32_ph->p_flags & PF_X) { + prot |= PROT_EXEC; + type = P_TEXT; + } + errno = do_mmap(i, start, length, prot, MAP_PRIVATE | MAP_FIXED, elf32_ph->p_offset & PAGE_MASK, type, O_RDONLY); + if(errno < 0 && errno > -PAGE_SIZE) { + kfree((unsigned int)data); + free_barg_pages(&barg); + send_sig(current, SIGSEGV); + return -ENOEXEC; + } + last_ptload = elf32_ph; + } + } + + elf32_ph = last_ptload; + + /* zero-fill the fractional page of the DATA section */ + end = PAGE_ALIGN(elf32_ph->p_vaddr + elf32_ph->p_filesz); + start = elf32_ph->p_vaddr + elf32_ph->p_filesz; + length = end - start; + + /* this will generate a page fault which will load the page in */ + memset_b((void *)start, NULL, length); + + /* setup the BSS section */ + start = elf32_ph->p_vaddr + elf32_ph->p_filesz; + start = PAGE_ALIGN(start); + end = elf32_ph->p_vaddr + elf32_ph->p_memsz; + end = PAGE_ALIGN(end); + length = end - start; + errno = do_mmap(NULL, start, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, 0, P_BSS, 0); + if(errno < 0 && errno > -PAGE_SIZE) { + kfree((unsigned int)data); + free_barg_pages(&barg); + send_sig(current, SIGSEGV); + return -ENOEXEC; + } + current->brk_lower = start; + + /* setup the HEAP section */ + start = elf32_ph->p_vaddr + elf32_ph->p_memsz; + start = PAGE_ALIGN(start); + length = PAGE_SIZE; + errno = do_mmap(NULL, start, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, 0, P_HEAP, 0); + if(errno < 0 && errno > -PAGE_SIZE) { + kfree((unsigned int)data); + free_barg_pages(&barg); + send_sig(current, SIGSEGV); + return -ENOEXEC; + } + current->brk = start; + + /* setup the STACK section */ + sp = KERNEL_BASE_ADDR - 4; /* formerly 0xBFFFFFFC */ + sp -= ae_str_len; + str = sp; /* this is the address of the first string (argv[0]) */ + sp &= ~3; + sp -= at_base ? (AT_ITEMS * 2) * sizeof(unsigned int) : 2 * sizeof(unsigned int); + sp -= ae_ptr_len; + length = KERNEL_BASE_ADDR - (sp & PAGE_MASK); + errno = do_mmap(NULL, sp & PAGE_MASK, length, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_FIXED, 0, P_STACK, 0); + if(errno < 0 && errno > -PAGE_SIZE) { + kfree((unsigned int)data); + free_barg_pages(&barg); + send_sig(current, SIGSEGV); + return -ENOEXEC; + } + + elf_create_stack(&barg, (unsigned int *)sp, str, at_base, elf32_h, phdr_addr); + kfree((unsigned int)data); + free_barg_pages(&barg); + + /* set %esp to point to 'argc' */ + sc->oldesp = sp; + sc->eflags = 0x202; /* FIXME: linux 2.2 = 0x292 */ + sc->eip = current->entry_address; + sc->err = 0; + sc->eax = 0; + sc->ecx = 0; + sc->edx = 0; + sc->ebx = 0; + sc->ebp = 0; + sc->esi = 0; + sc->edi = 0; + return 0; +} diff --git a/fs/ext2/Makefile b/fs/ext2/Makefile new file mode 100644 index 00000000..5d490b9f --- /dev/null +++ b/fs/ext2/Makefile @@ -0,0 +1,19 @@ +# fiwix/fs/ext2/Makefile +# +# Copyright 2018, Jordi Sanfeliu. All rights reserved. +# Distributed under the terms of the Fiwix License. +# + +.S.o: + $(CC) -traditional -I$(INCLUDE) -c -o $@ $< +.c.o: + $(CC) $(CFLAGS) -c -o $@ $< + +OBJS = inode.o super.o namei.o symlink.o dir.o file.o + +ext2: $(OBJS) + $(LD) $(LDFLAGS) -r $(OBJS) -o ext2.o + +clean: + rm -f *.o + diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c new file mode 100644 index 00000000..9089d7c8 --- /dev/null +++ b/fs/ext2/dir.c @@ -0,0 +1,142 @@ +/* + * fiwix/fs/ext2/dir.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct fs_operations ext2_dir_fsop = { + 0, + 0, + + ext2_dir_open, + ext2_dir_close, + ext2_dir_read, + NULL, /* write */ + NULL, /* ioctl */ + NULL, /* lseek */ + ext2_dir_readdir, + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + ext2_bmap, + ext2_lookup, + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +int ext2_dir_open(struct inode *i, struct fd *fd_table) +{ + fd_table->offset = 0; + return 0; +} + +int ext2_dir_close(struct inode *i, struct fd *fd_table) +{ + return 0; +} + +int ext2_dir_read(struct inode *i, struct fd *fd_table, char *buffer, __size_t count) +{ + return -EISDIR; +} + +int ext2_dir_readdir(struct inode *i, struct fd *fd_table, struct dirent *dirent, unsigned int count) +{ + __blk_t block; + unsigned int doffset, offset; + unsigned int size, dirent_len; + struct ext2_dir_entry_2 *d; + int base_dirent_len; + int blksize; + struct buffer *buf; + + if(!(S_ISDIR(i->i_mode))) { + return -EBADF; + } + + blksize = i->sb->s_blocksize; + if(fd_table->offset > i->i_size) { + fd_table->offset = i->i_size; + } + + base_dirent_len = sizeof(dirent->d_ino) + sizeof(dirent->d_off) + sizeof(dirent->d_reclen); + doffset = offset = size = 0; + + while(doffset < count) { + if((block = bmap(i, fd_table->offset, FOR_READING)) < 0) { + return block; + } + if(block) { + if(!(buf = bread(i->dev, block, blksize))) { + return -EIO; + } + + doffset = fd_table->offset; + offset = fd_table->offset % blksize; + while(doffset < i->i_size && offset < blksize) { + d = (struct ext2_dir_entry_2 *)(buf->data + offset); + if(d->inode) { + dirent_len = (base_dirent_len + (d->name_len + 1)) + 3; + dirent_len &= ~3; /* round up */ + dirent->d_ino = d->inode; + if((size + dirent_len) < count) { + dirent->d_off = doffset; + dirent->d_reclen = dirent_len; + memcpy_b(dirent->d_name, d->name, d->name_len); + dirent->d_name[d->name_len] = NULL; + dirent = (struct dirent *)((char *)dirent + dirent_len); + size += dirent_len; + } else { + break; + } + } + doffset += d->rec_len; + offset += d->rec_len; + if(!d->rec_len) { + break; + } + } + brelse(buf); + } + fd_table->offset &= ~(blksize - 1); + doffset = fd_table->offset; + fd_table->offset += offset; + doffset += blksize; + } + + return size; +} diff --git a/fs/ext2/file.c b/fs/ext2/file.c new file mode 100644 index 00000000..b2200ace --- /dev/null +++ b/fs/ext2/file.c @@ -0,0 +1,78 @@ +/* + * fiwix/fs/ext2/file.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct fs_operations ext2_file_fsop = { + 0, + 0, + + ext2_file_open, + ext2_file_close, + file_read, + NULL, /* write */ + NULL, /* ioctl */ + ext2_file_lseek, + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + ext2_bmap, + NULL, /* lookup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +int ext2_file_open(struct inode *i, struct fd *fd_table) +{ + if(fd_table->flags & (O_WRONLY | O_RDWR | O_TRUNC | O_APPEND)) { + return -ENOENT; + } + fd_table->offset = 0; + return 0; +} + +int ext2_file_close(struct inode *i, struct fd *fd_table) +{ + return 0; +} + +int ext2_file_lseek(struct inode *i, __off_t offset) +{ + return offset; +} diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c new file mode 100644 index 00000000..0691bdea --- /dev/null +++ b/fs/ext2/inode.c @@ -0,0 +1,200 @@ +/* + * fiwix/fs/ext2/inode.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BLOCKS_PER_IND_BLOCK(sb) (EXT2_BLOCK_SIZE(sb) / sizeof(unsigned int)) +#define BLOCKS_PER_DIND_BLOCK(sb) (BLOCKS_PER_IND_BLOCK(sb) * BLOCKS_PER_IND_BLOCK(sb)) +#define BLOCKS_PER_TIND_BLOCK(sb) (BLOCKS_PER_IND_BLOCK(sb) * BLOCKS_PER_IND_BLOCK(sb) * BLOCKS_PER_IND_BLOCK(sb)) + +#define EXT2_INODES_PER_BLOCK(sb) (EXT2_BLOCK_SIZE(sb) / sizeof(struct ext2_inode)) + +static int get_group_desc(struct inode *i, struct superblock *sb, struct ext2_group_desc *gd) +{ + int block_group; + int desc_per_block, group_desc_block, group_desc; + struct buffer *buf; + + block_group = ((i->inode - 1) / sb->u.ext2.s_inodes_per_group); + desc_per_block = sb->s_blocksize / sizeof(struct ext2_group_desc); + group_desc_block = block_group / desc_per_block; + group_desc = block_group % desc_per_block; + if(!(buf = bread(i->dev, SUPERBLOCK + sb->u.ext2.s_first_data_block + group_desc_block, i->sb->s_blocksize))) { + return -EIO; + } + memcpy_b(gd, (void *)(buf->data + (group_desc * sizeof(struct ext2_group_desc))), sizeof(struct ext2_group_desc)); + brelse(buf); + return 0; +} + +int ext2_read_inode(struct inode *i) +{ + __ino_t block; + unsigned int offset; + struct superblock *sb; + struct ext2_inode *ii; + struct ext2_group_desc gd; + struct buffer *buf; + + if(!(sb = get_superblock(i->dev))) { + printk("WARNING: %s(): get_superblock() has returned NULL.\n"); + return -EINVAL; + } + if(get_group_desc(i, sb, &gd)) { + return -EIO; + } + block = (((i->inode - 1) % sb->u.ext2.s_inodes_per_group) / EXT2_INODES_PER_BLOCK(sb)); + + if(!(buf = bread(i->dev, gd.bg_inode_table + block, i->sb->s_blocksize))) { + return -EIO; + } + offset = ((((i->inode - 1) % sb->u.ext2.s_inodes_per_group) % EXT2_INODES_PER_BLOCK(sb)) * sizeof(struct ext2_inode)); + + ii = (struct ext2_inode *)(buf->data + offset); + memcpy_b(&i->u.ext2.i_block, ii->i_block, sizeof(ii->i_block)); + + i->i_mode = ii->i_mode; + i->i_uid = ii->i_uid; + i->i_size = ii->i_size; + i->i_atime = ii->i_atime; + i->i_ctime = ii->i_ctime; + i->i_mtime = ii->i_mtime; + i->i_gid = ii->i_gid; + i->i_nlink = ii->i_links_count; + i->i_blocks = ii->i_blocks; + i->i_flags = ii->i_flags; + i->count = 1; + switch(i->i_mode & S_IFMT) { + case S_IFCHR: + i->fsop = &def_chr_fsop; + i->rdev = ii->i_block[0]; + break; + case S_IFBLK: + i->fsop = &def_blk_fsop; + i->rdev = ii->i_block[0]; + break; + case S_IFIFO: + i->fsop = &pipefs_fsop; + /* it's a union so we need to clear pipefs_i */ + memset_b(&i->u.pipefs, NULL, sizeof(struct pipefs_inode)); + break; + case S_IFDIR: + i->fsop = &ext2_dir_fsop; + break; + case S_IFREG: + i->fsop = &ext2_file_fsop; + break; + case S_IFLNK: + i->fsop = &ext2_symlink_fsop; + break; + case S_IFSOCK: + i->fsop = NULL; + break; + default: + printk("WARNING: %s(): invalid inode (%d) mode %08o.\n", __FUNCTION__, i->inode, i->i_mode); + brelse(buf); + return -ENOENT; + } + brelse(buf); + return 0; +} + +int ext2_bmap(struct inode *i, __off_t offset, int mode) +{ + unsigned char level; + short int dind_block; + __blk_t *indblock; + __blk_t *dindblock; + __blk_t block; + struct buffer *buf; + + block = offset / i->sb->s_blocksize; + level = 0; + + if(block < EXT2_NDIR_BLOCKS) { + level = EXT2_NDIR_BLOCKS - 1; + } else { + if(block < (BLOCKS_PER_IND_BLOCK(i->sb) + EXT2_NDIR_BLOCKS)) { + level = EXT2_IND_BLOCK; + block -= EXT2_NDIR_BLOCKS; + } else { + if(block < BLOCKS_PER_DIND_BLOCK(i->sb)) { + level = EXT2_DIND_BLOCK; + block -= EXT2_NDIR_BLOCKS; + block -= BLOCKS_PER_IND_BLOCK(i->sb); + } else { + level = EXT2_TIND_BLOCK; + block = 0; + } + } + } + + if(level == EXT2_TIND_BLOCK) { + printk("(level = %d) (offset = %d) (block = %d)\n", level, offset, block); + printk("WARNING: triple-indirect blocks are not supported!\n"); + return -EINVAL; + } + + if(level < EXT2_NDIR_BLOCKS) { + return i->u.ext2.i_block[block]; + } + + if(!(indblock = (void *)kmalloc())) { + printk("%s(): returning -ENOMEM.\n", __FUNCTION__); + return -ENOMEM; + } + if(i->u.ext2.i_block[level] == 0) { + printk("WARNING: %s(): will return 0 as an indirect block request! (inode %d).\n", __FUNCTION__, i->inode); + kfree((unsigned int)indblock); + return 0; + } + if(!(buf = bread(i->dev, i->u.ext2.i_block[level], i->sb->s_blocksize))) { + kfree((unsigned int)indblock); + printk("%s(): returning -EIO.\n", __FUNCTION__); + return -EIO; + } + memcpy_l(indblock, buf->data, BLOCKS_PER_IND_BLOCK(i->sb)); + brelse(buf); + + if(level == EXT2_IND_BLOCK) { + kfree((unsigned int)indblock); + return indblock[block]; + } + + if(!(dindblock = (void *)kmalloc())) { + kfree((unsigned int)indblock); + printk("%s(): returning -ENOMEM.\n", __FUNCTION__); + return -ENOMEM; + } + dind_block = block / BLOCKS_PER_IND_BLOCK(i->sb); + if(!(buf = bread(i->dev, indblock[dind_block], i->sb->s_blocksize))) { + kfree((unsigned int)indblock); + kfree((unsigned int)dindblock); + printk("%s(): returning -EIO.\n", __FUNCTION__); + return -EIO; + } + memcpy_l(dindblock, buf->data, BLOCKS_PER_IND_BLOCK(i->sb)); + brelse(buf); + block = dindblock[block - (dind_block * BLOCKS_PER_IND_BLOCK(i->sb))]; + kfree((unsigned int)indblock); + kfree((unsigned int)dindblock); + return block; +} diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c new file mode 100644 index 00000000..33917b9a --- /dev/null +++ b/fs/ext2/namei.c @@ -0,0 +1,79 @@ +/* + * fiwix/fs/ext2/namei.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int ext2_lookup(const char *name, struct inode *dir, struct inode **i_res) +{ + __blk_t block; + unsigned int blksize; + unsigned int offset, doffset; + struct buffer *buf; + struct ext2_dir_entry_2 *d; + __ino_t inode; + + blksize = dir->sb->s_blocksize; + inode = offset = 0; + dir->count++; + + while(offset < dir->i_size && !inode) { + if((block = bmap(dir, offset, FOR_READING)) < 0) { + return block; + } + if(block) { + if(!(buf = bread(dir->dev, block, blksize))) { + return -EIO; + } + doffset = 0; + do { + d = (struct ext2_dir_entry_2 *)(buf->data + doffset); + if(d->inode) { + if(d->name_len == strlen(name)) { + if(strncmp(d->name, name, d->name_len) == 0) { + inode = d->inode; + } + } + doffset += d->rec_len; + } else { + doffset += sizeof(struct ext2_dir_entry_2); + } + } while((doffset < blksize) && (!inode)); + + brelse(buf); + offset += blksize; + if(inode) { + /* + * This prevents a deadlock in iget() when + * trying to lock '.' when 'dir' is the same + * directory (ls -lai ). + */ + if(inode == dir->inode) { + *i_res = dir; + return 0; + } + + if(!(*i_res = iget(dir->sb, inode))) { + return -EACCES; + } + iput(dir); + return 0; + } + } else { + break; + } + } + iput(dir); + return -ENOENT; +} diff --git a/fs/ext2/super.c b/fs/ext2/super.c new file mode 100644 index 00000000..c2f10027 --- /dev/null +++ b/fs/ext2/super.c @@ -0,0 +1,142 @@ +/* + * fiwix/fs/ext2/super.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct fs_operations ext2_fsop = { + FSOP_REQUIRES_DEV, + NULL, + + NULL, /* open */ + NULL, /* close */ + NULL, /* read */ + NULL, /* write */ + NULL, /* ioctl */ + NULL, /* lseek */ + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lookup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + ext2_read_inode, + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + ext2_statfs, + ext2_read_superblock, + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +static void check_superblock(struct ext2_super_block *sb) +{ + if(!(sb->s_state & EXT2_VALID_FS)) { + printk("WARNING: filesystem unchecked, fsck recommended.\n"); + } else if((sb->s_state & EXT2_ERROR_FS)) { + printk("WARNING: filesystem contains errors, fsck recommended.\n"); + } else if(sb->s_max_mnt_count >= 0 && sb->s_mnt_count >= (unsigned short int)sb->s_max_mnt_count) { + printk("WARNING: maximal mount count reached, fsck recommended.\n"); + } else if(sb->s_checkinterval && (sb->s_lastcheck + sb->s_checkinterval <= CURRENT_TIME)) { + printk("WARNING: checktime reached, fsck recommended.\n"); + } +} + +void ext2_statfs(struct superblock *sb, struct statfs *statfsbuf) +{ + statfsbuf->f_type = EXT2_SUPER_MAGIC; + statfsbuf->f_bsize = sb->s_blocksize; + statfsbuf->f_blocks = sb->u.ext2.s_blocks_count; + statfsbuf->f_bfree = sb->u.ext2.s_free_blocks_count; + if(statfsbuf->f_bfree >= sb->u.ext2.s_r_blocks_count) { + statfsbuf->f_bavail = statfsbuf->f_bfree - sb->u.ext2.s_r_blocks_count; + } else { + statfsbuf->f_bavail = 0; + } + statfsbuf->f_files = sb->u.ext2.s_inodes_count; + statfsbuf->f_ffree = sb->u.ext2.s_free_inodes_count; + /* statfsbuf->f_fsid = ? */ + statfsbuf->f_namelen = EXT2_NAME_LEN; +} + +int ext2_read_superblock(__dev_t dev, struct superblock *sb) +{ + struct buffer *buf; + struct ext2_super_block *ext2sb; + + superblock_lock(sb); + if(!(buf = bread(dev, SUPERBLOCK, BLKSIZE_1K))) { + superblock_unlock(sb); + return -EIO; + } + + ext2sb = (struct ext2_super_block *)buf->data; + if(ext2sb->s_magic != EXT2_SUPER_MAGIC) { + printk("WARNING: %s(): invalid filesystem type or bad superblock on device %d,%d.\n", __FUNCTION__, MAJOR(dev), MINOR(dev)); + superblock_unlock(sb); + brelse(buf); + return -EINVAL; + } + + /* sparse-superblock feature not supported (only for read-write mode) */ + if(!sb->flags & MS_RDONLY) { + if(ext2sb->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) { + printk("WARNING: %s(): sparse-superblock feature is not supported.\n", __FUNCTION__); + printk("filesystem structure not supported. Try with '-o ro'.\n"); + superblock_unlock(sb); + brelse(buf); + return -EINVAL; + } + } + + sb->dev = dev; + sb->fsop = &ext2_fsop; + sb->s_blocksize = EXT2_MIN_BLOCK_SIZE << ext2sb->s_log_block_size; + memcpy_b(&sb->u.ext2, ext2sb, sizeof(struct ext2_super_block)); + + if(!(sb->root = iget(sb, EXT2_ROOT_INO))) { + printk("WARNING: %s(): unable to get root inode.\n", __FUNCTION__); + superblock_unlock(sb); + brelse(buf); + return -EINVAL; + } + + superblock_unlock(sb); + check_superblock(ext2sb); + brelse(buf); + return 0; +} + +int ext2_init(void) +{ + return register_filesystem("ext2", &ext2_fsop); +} diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c new file mode 100644 index 00000000..f60840e7 --- /dev/null +++ b/fs/ext2/symlink.c @@ -0,0 +1,134 @@ +/* + * fiwix/fs/ext2/symlink.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct fs_operations ext2_symlink_fsop = { + 0, + 0, + + NULL, /* open */ + NULL, /* close */ + NULL, /* read */ + NULL, /* write */ + NULL, /* ioctl */ + NULL, /* lseek */ + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + ext2_readlink, + ext2_followlink, + NULL, /* bmap */ + NULL, /* lookup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +int ext2_readlink(struct inode *i, char *buffer, __size_t count) +{ + __u32 blksize; + struct buffer *buf; + + if(!S_ISLNK(i->i_mode)) { + printk("%s(): Oops, inode '%d' is not a symlink (!?).\n", __FUNCTION__, i->inode); + return 0; + } + + inode_lock(i); + blksize = i->sb->s_blocksize; + count = MIN(count, i->i_size); + if(!count) { + inode_unlock(i); + return 0; + } + count = MIN(count, blksize); + if(i->i_blocks) { /* slow symlink */ + if(!(buf = bread(i->dev, i->u.ext2.i_block[0], blksize))) { + inode_unlock(i); + return -EIO; + } + memcpy_b(buffer, buf->data, count); + brelse(buf); + } else { /* fast symlink */ + memcpy_b(buffer, (char *)i->u.ext2.i_block, count); + } + buffer[count] = NULL; + inode_unlock(i); + return count; +} + +int ext2_followlink(struct inode *dir, struct inode *i, struct inode **i_res) +{ + struct buffer *buf; + char *name; + __ino_t errno; + + if(!i) { + return -ENOENT; + } + + if(!S_ISLNK(i->i_mode)) { + printk("%s(): Oops, inode '%d' is not a symlink (!?).\n", __FUNCTION__, i->inode); + return 0; + } + + if(current->loopcnt > MAX_SYMLINKS) { + printk("%s(): too many nested symbolic links!\n", __FUNCTION__); + return -ELOOP; + } + + inode_lock(i); + if(i->i_blocks) { /* slow symlink */ + if(!(buf = bread(i->dev, i->u.ext2.i_block[0], i->sb->s_blocksize))) { + inode_unlock(i); + return -EIO; + } + name = buf->data; + } else { /* fast symlink */ + buf = NULL; + name = (char *)i->u.ext2.i_block; + } + inode_unlock(i); + + current->loopcnt++; + iput(i); + if(buf) { + brelse(buf); + } + errno = parse_namei(name, dir, i_res, NULL, FOLLOW_LINKS); + current->loopcnt--; + return errno; +} diff --git a/fs/fd.c b/fs/fd.c new file mode 100644 index 00000000..46ed0864 --- /dev/null +++ b/fs/fd.c @@ -0,0 +1,50 @@ +/* + * fiwix/fs/fd.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include + +struct fd *fd_table; + +static struct resource fd_resource = { NULL, NULL }; + +int get_new_fd(struct inode *i) +{ + unsigned int n; + + lock_resource(&fd_resource); + + for(n = 1; n < NR_OPENS; n++) { + if(fd_table[n].count == 0) { + memset_b(&fd_table[n], NULL, sizeof(struct fd)); + fd_table[n].inode = i; + fd_table[n].count = 1; + unlock_resource(&fd_resource); + return n; + } + } + + unlock_resource(&fd_resource); + + return -ENFILE; +} + +void release_fd(unsigned int fd) +{ + lock_resource(&fd_resource); + fd_table[fd].count = 0; + unlock_resource(&fd_resource); +} + +void fd_init(void) +{ + memset_b(fd_table, NULL, fd_table_size); +} diff --git a/fs/filesystems.c b/fs/filesystems.c new file mode 100644 index 00000000..e3426278 --- /dev/null +++ b/fs/filesystems.c @@ -0,0 +1,81 @@ +/* + * fiwix/fs/filesystems.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int register_filesystem(const char *name, struct fs_operations *fsop) +{ + int n; + __dev_t dev; + + for(n = 0; n < NR_FILESYSTEMS; n++) { + if(filesystems_table[n].name) { + if(strcmp(filesystems_table[n].name, name) == 0) { + printk("WARNING: %s(): filesystem '%s' already registered!\n", __FUNCTION__, name); + return 1; + } + } + if(!filesystems_table[n].name) { + filesystems_table[n].name = name; + filesystems_table[n].fsop = fsop; + if((fsop->flags & FSOP_KERN_MOUNT)) { + dev = fsop->fsdev; + return kern_mount(dev, &filesystems_table[n]); + } + return 0; + } + } + printk("WARNING: %s(): filesystems table is full!\n", __FUNCTION__); + return 1; +} + +struct filesystems * get_filesystem(const char *name) +{ + int n; + + if(!name) { + return NULL; + } + for(n = 0; n < NR_FILESYSTEMS; n++) { + if(!filesystems_table[n].name) { + continue; + } + if(strcmp(filesystems_table[n].name, name) == 0) { + return &filesystems_table[n]; + } + } + return NULL; +} + +void fs_init(void) +{ + memset_b(filesystems_table, NULL, sizeof(filesystems_table)); + + if(minix_init()) { + printk("%s(): unable to register 'minix' filesystem.\n", __FUNCTION__); + } + if(ext2_init()) { + printk("%s(): unable to register 'ext2' filesystem.\n", __FUNCTION__); + } + if(pipefs_init()) { + printk("%s(): unable to register 'pipefs' filesystem.\n", __FUNCTION__); + } + if(iso9660_init()) { + printk("%s(): unable to register 'iso9660' filesystem.\n", __FUNCTION__); + } + if(procfs_init()) { + printk("%s(): unable to register 'procfs' filesystem.\n", __FUNCTION__); + } +} diff --git a/fs/inode.c b/fs/inode.c new file mode 100644 index 00000000..9381e1eb --- /dev/null +++ b/fs/inode.c @@ -0,0 +1,459 @@ +/* + * fiwix/fs/inode.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +/* + * inode.c implements a cache with a free list as a doubly circular linked + * list and a chained hash table with doubly linked lists. + * + * hash table + * +--------+ +--------------+ +--------------+ +--------------+ + * | index | |prev|data|next| |prev|data|next| |prev|data|next| + * | 0 --> | / | | ---> <--- | | ---> <--- | | / | + * +--------+ +--------------+ +--------------+ +--------------+ + * +--------+ +--------------+ +--------------+ +--------------+ + * | index | |prev|data|next| |prev|data|next| |prev|data|next| + * | 1 --> | / | | ---> <--- | | ---> <--- | | / | + * +--------+ +--------------+ +--------------+ +--------------+ + * (inode) (inode) (inode) + * ... + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define INODE_HASH(dev, inode) (((__dev_t)(dev) ^ (__ino_t)(inode)) % (NR_INO_HASH)) +#define NR_INODES (inode_table_size / sizeof(struct inode)) +#define NR_INO_HASH (inode_hash_table_size / sizeof(unsigned int)) + +struct inode *inode_table; /* inode pool */ +struct inode *inode_head; /* inode pool head */ +struct inode **inode_hash_table; + +int inodes_on_free_list = 0; +static struct resource sync_resource = { NULL, NULL }; + +static void insert_to_hash(struct inode *i) +{ + struct inode **h; + int n; + + n = INODE_HASH(i->dev, i->inode); + h = &inode_hash_table[n]; + + if(!*h) { + *h = i; + (*h)->prev_hash = (*h)->next_hash = NULL; + } else { + i->prev_hash = NULL; + i->next_hash = *h; + (*h)->prev_hash = i; + *h = i; + } +} + +static void remove_from_hash(struct inode *i) +{ + struct inode **h; + int n; + + n = INODE_HASH(i->dev, i->inode); + h = &inode_hash_table[n]; + + while(*h) { + if(*h == i) { + if((*h)->next_hash) { + (*h)->next_hash->prev_hash = (*h)->prev_hash; + } + if((*h)->prev_hash) { + (*h)->prev_hash->next_hash = (*h)->next_hash; + } + if(h == &inode_hash_table[n]) { + *h = (*h)->next_hash; + } + break; + } + h = &(*h)->next_hash; + } +} + +static void remove_from_free_list(struct inode *i) +{ + i->prev_free->next_free = i->next_free; + i->next_free->prev_free = i->prev_free; + inodes_on_free_list--; + if(i == inode_head) { + inode_head = i->next_free; + } +} + +static void inode_wait(struct inode *i) +{ + unsigned long int flags; + + for(;;) { + SAVE_FLAGS(flags); CLI(); + if(i->locked) { + RESTORE_FLAGS(flags); + sleep(i, PROC_UNINTERRUPTIBLE); + } else { + break; + } + } + RESTORE_FLAGS(flags); +} + +static struct inode * get_free_inode(void) +{ + struct inode *i; + + /* no more inodes on free list */ + if(inode_head == inode_head->next_free) { + return NULL; + } + + i = inode_head; + inode_head->next_free->prev_free = inode_head->prev_free; + inode_head->prev_free->next_free = inode_head->next_free; + inode_head = inode_head->next_free; + + return i; +} + +static int read_inode(struct inode *i) +{ + int errno; + + inode_lock(i); + if(i->sb && i->sb->fsop && i->sb->fsop->read_inode) { + errno = i->sb->fsop->read_inode(i); + inode_unlock(i); + return errno; + } + inode_unlock(i); + return -EINVAL; +} + +static int write_inode(struct inode *i) +{ + int errno; + + errno = 1; + + inode_lock(i); + if(i->sb && i->sb->fsop && i->sb->fsop->write_inode) { + errno = i->sb->fsop->write_inode(i); + } else { + /* i.e. PIPE_DEV inodes can't be flushed on disk */ + i->dirty = 0; + errno = 0; + } + inode_unlock(i); + + return errno; +} + +static struct inode * search_inode_hash(__dev_t dev, __ino_t inode) +{ + struct inode *i; + int n; + + n = INODE_HASH(dev, inode); + i = inode_hash_table[n]; + + while(i) { + if(i->dev == dev && i->inode == inode) { + return i; + } + i = i->next_hash; + } + + return NULL; +} + +static struct inode * get_blank_inode(void) +{ + unsigned long int flags; + struct inode *i; + + SAVE_FLAGS(flags); CLI(); + + if((i = get_free_inode())) { + remove_from_free_list(i); + remove_from_hash(i); + i->i_mode = 0; + i->i_uid = 0; + i->i_size = 0; + i->i_atime = 0; + i->i_ctime = 0; + i->i_mtime = 0; + i->i_gid = 0; + i->i_nlink = 0; + i->i_blocks = 0; + i->i_flags = 0; + i->locked = 0; + i->dirty = 0; + i->mount_point = NULL; + i->dev = 0; + i->inode = 0; + i->count = 0; + i->rdev = 0; + i->fsop = NULL; + i->sb = NULL; + memset_b(&i->u, NULL, sizeof(i->u)); + } + RESTORE_FLAGS(flags); + return i; +} + +void inode_lock(struct inode *i) +{ + unsigned long int flags; + + for(;;) { + SAVE_FLAGS(flags); CLI(); + if(i->locked) { + RESTORE_FLAGS(flags); + sleep(i, PROC_UNINTERRUPTIBLE); + } else { + break; + } + } + i->locked = 1; + RESTORE_FLAGS(flags); +} + +void inode_unlock(struct inode *i) +{ + unsigned long int flags; + + SAVE_FLAGS(flags); CLI(); + i->locked = 0; + wakeup(i); + RESTORE_FLAGS(flags); +} + +struct inode * ialloc(struct superblock *sb) +{ + int errno; + struct inode *i; + + if((i = get_blank_inode())) { + i->sb = sb; + i->rdev = sb->dev; + if(i->sb && i->sb->fsop && i->sb->fsop->ialloc) { + errno = i->sb->fsop->ialloc(i); + } else { + printk("WARNING: this filesystem does not have the ialloc() method!\n"); + i->count = 1; + i->sb = NULL; + iput(i); + return NULL; + } + if(errno) { + i->count = 1; + i->sb = NULL; + iput(i); + return NULL; + } + i->dev = sb->dev; + insert_to_hash(i); + return i; + } + printk("WARNING: %s(): no more inodes on free list!\n", __FUNCTION__); + return NULL; +} + +struct inode * iget(struct superblock *sb, __ino_t inode) +{ + unsigned long int flags; + struct inode *i; + + if(!inode) { + return NULL; + } + + for(;;) { + if((i = search_inode_hash(sb->dev, inode))) { + inode_wait(i); + SAVE_FLAGS(flags); CLI(); + + /* update superblock pointer from mount_table */ + i->sb = sb; + + if(i->mount_point) { + i = i->mount_point; + } + /* FIXME: i->locked = 1; ? */ + if(++i->count == 1) { + remove_from_free_list(i); + } + RESTORE_FLAGS(flags); + return i; + } + + if(!(i = get_blank_inode())) { + printk("WARNING: %s(): no more inodes on free list! (%d).\n", __FUNCTION__, inodes_on_free_list); + return NULL; + } + + SAVE_FLAGS(flags); CLI(); + i->dev = i->rdev = sb->dev; + i->inode = inode; + i->sb = sb; + i->count = 1; + RESTORE_FLAGS(flags); + if(read_inode(i)) { + iput(i); + return NULL; + } + insert_to_hash(i); + /* FIXME: i->locked = 1; ? */ + return i; + } +} + +int bmap(struct inode *i, __off_t offset, int mode) +{ + if(i->fsop && i->fsop->bmap) { + return i->fsop->bmap(i, offset, mode); + } + return -EPERM; +} + +int check_fs_busy(__dev_t dev, struct inode *root) +{ + struct inode *i; + unsigned int n; + + i = &inode_table[0]; + for(n = 0; n < NR_INODES; n++, i = &inode_table[n]) { + if(i->dev == dev && i->count) { + if(i == root && i->count == 1) { + continue; + } + /* FIXME: to be removed */ + printk("WARNING: root %d with count %d (on dev %d,%d)\n", root->inode, root->count, MAJOR(i->dev), MINOR(i->dev)); + printk("WARNING: inode %d with count %d (on dev %d,%d)\n", i->inode, i->count, MAJOR(i->dev), MINOR(i->dev)); + return 1; + } + } + return 0; +} + +void iput(struct inode *i) +{ + unsigned long int flags; + + /* this solves the problem with rmdir('/') and iput(dir) which is NULL */ + if(!i) { + return; + } + + if(!i->count) { + printk("WARNING: %s(): trying to free an already freed inode (%d)!\n", __FUNCTION__, i->inode); + return; + } + + SAVE_FLAGS(flags); CLI(); + + if(--i->count == 0) { + if(!i->i_nlink) { + if(i->sb && i->sb->fsop && i->sb->fsop->ifree) { + inode_lock(i); + i->sb->fsop->ifree(i); + remove_from_hash(i); + inode_unlock(i); + } + } + if(i->dirty) { + if(write_inode(i)) { + printk("WARNING: %s(): can't write inode %d (%d,%d), will remain as dirty.\n", __FUNCTION__, i->inode, MAJOR(i->dev), MINOR(i->dev)); + RESTORE_FLAGS(flags); + return; + } + } + if(!inode_head) { + i->prev_free = i->next_free = i; + inode_head = i; + } else { + i->next_free = inode_head; + i->prev_free = inode_head->prev_free; + inode_head->prev_free->next_free = i; + inode_head->prev_free = i; + } + inodes_on_free_list++; + } + + RESTORE_FLAGS(flags); +} + +void sync_inodes(__dev_t dev) +{ + struct inode *i; + int n; + + i = &inode_table[0]; + + lock_resource(&sync_resource); + for(n = 0; n < NR_INODES; n++) { + if(i->dirty) { + if(!dev || i->dev == dev) { + inode_wait(i); + if(write_inode(i)) { + printk("WARNING: %s(): can't write inode %d (%d,%d), will remain as dirty.\n", __FUNCTION__, i->inode, MAJOR(i->dev), MINOR(i->dev)); + } + } + } + i++; + } + unlock_resource(&sync_resource); + return; +} + +void invalidate_inodes(__dev_t dev) +{ + unsigned long int flags; + unsigned int n; + struct inode *i; + + i = &inode_table[0]; + SAVE_FLAGS(flags); CLI(); + + for(n = 0; n < NR_INODES; n++) { + if(i->dev == dev) { + inode_wait(i); + remove_from_hash(i); + i->locked = 0; + wakeup(&inode_wait); + } + i++; + } + + RESTORE_FLAGS(flags); +} + +void inode_init(void) +{ + struct inode *i; + unsigned int n; + + memset_b(inode_table, NULL, inode_table_size); + memset_b(inode_hash_table, NULL, inode_hash_table_size); + for(n = 0; n < NR_INODES; n++) { + i = &inode_table[n]; + i->count = 1; + iput(i); + } +} diff --git a/fs/iso9660/Makefile b/fs/iso9660/Makefile new file mode 100644 index 00000000..0f28b797 --- /dev/null +++ b/fs/iso9660/Makefile @@ -0,0 +1,19 @@ +# fiwix/fs/iso9660/Makefile +# +# Copyright 2018, Jordi Sanfeliu. All rights reserved. +# Distributed under the terms of the Fiwix License. +# + +.S.o: + $(CC) -traditional -I$(INCLUDE) -c -o $@ $< +.c.o: + $(CC) $(CFLAGS) -c -o $@ $< + +OBJS = inode.o super.o namei.o dir.o file.o rrip.o symlink.o + +iso9660:$(OBJS) + $(LD) $(LDFLAGS) -r $(OBJS) -o iso9660.o + +clean: + rm -f *.o + diff --git a/fs/iso9660/dir.c b/fs/iso9660/dir.c new file mode 100644 index 00000000..0b878640 --- /dev/null +++ b/fs/iso9660/dir.c @@ -0,0 +1,173 @@ +/* + * fiwix/fs/iso9660/dir.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct fs_operations iso9660_dir_fsop = { + 0, + 0, + + iso9660_dir_open, + iso9660_dir_close, + iso9660_dir_read, + NULL, /* write */ + NULL, /* ioctl */ + NULL, /* lseek */ + iso9660_dir_readdir, + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + iso9660_bmap, + iso9660_lookup, + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statsfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +int iso9660_dir_open(struct inode *i, struct fd *fd_table) +{ + fd_table->offset = 0; + return 0; +} + +int iso9660_dir_close(struct inode *i, struct fd *fd_table) +{ + return 0; +} + +int iso9660_dir_read(struct inode *i, struct fd *fd_table, char *buffer, __size_t count) +{ + return -EISDIR; +} + +int iso9660_dir_readdir(struct inode *i, struct fd *fd_table, struct dirent *dirent, unsigned int count) +{ + __blk_t block; + unsigned int doffset, offset; + unsigned int size, dirent_len; + struct iso9660_directory_record *d; + int base_dirent_len; + int blksize; + struct buffer *buf; + int nm_len; + char nm_name[NAME_MAX + 1]; + + if(!(S_ISDIR(i->i_mode))) { + return -EBADF; + } + + blksize = i->sb->s_blocksize; + if(fd_table->offset > i->i_size) { + fd_table->offset = i->i_size; + } + + base_dirent_len = sizeof(dirent->d_ino) + sizeof(dirent->d_off) + sizeof(dirent->d_reclen); + doffset = offset = size = 0; + + while(doffset < count) { + if((block = bmap(i, fd_table->offset, FOR_READING)) < 0) { + return block; + } + if(block) { + if(!(buf = bread(i->dev, block, blksize))) { + return -EIO; + } + + doffset = fd_table->offset; + offset = fd_table->offset % blksize; + + while(doffset < i->i_size && offset < blksize) { + d = (struct iso9660_directory_record *)(buf->data + offset); + if(isonum_711(d->length)) { + dirent_len = (base_dirent_len + (isonum_711(d->name_len) + 1)) + 3; + dirent_len &= ~3; /* round up */ + if((size + isonum_711(d->length)) < count) { + dirent->d_ino = (block << ISO9660_INODE_BITS) + (doffset & ISO9660_INODE_MASK); + dirent->d_off = doffset; + dirent->d_reclen = dirent_len; + if(isonum_711(d->name_len) == 1 && d->name[0] == 0) { + dirent->d_name[0] = '.'; + dirent->d_name[1] = NULL; + } else if(isonum_711(d->name_len) == 1 && d->name[0] == 1) { + dirent->d_name[0] = '.'; + dirent->d_name[1] = '.'; + dirent->d_name[2] = NULL; + dirent_len = 16; + dirent->d_reclen = 16; + if(i->u.iso9660.i_parent) { + dirent->d_ino = i->u.iso9660.i_parent->inode; + } else { + dirent->d_ino = i->inode; + } + } else { + nm_len = 0; + if(i->sb->u.iso9660.rrip) { + nm_len = get_rrip_filename(d, i, nm_name); + } + if(nm_len) { + memcpy_b(dirent->d_name, nm_name, nm_len); + dirent->d_name[nm_len] = NULL; + dirent->d_reclen = (base_dirent_len + nm_len + 1) + 3; + dirent->d_reclen &= ~3; /* round up */ + dirent_len = dirent->d_reclen; + } else { + memcpy_b(dirent->d_name, d->name, isonum_711(d->name_len)); + dirent->d_name[isonum_711(d->name_len)] = NULL; + } + } + if(!((char)d->flags[0] & ISO9660_FILE_ISDIR)) { + iso9660_cleanfilename(dirent->d_name, isonum_711(d->name_len)); + } + dirent = (struct dirent *)((char *)dirent + dirent_len); + size += dirent_len; + } else { + break; + } + doffset += isonum_711(d->length); + offset += isonum_711(d->length); + } else { + break; + } + } + brelse(buf); + } + fd_table->offset &= ~(blksize - 1); + doffset = fd_table->offset; + doffset += blksize; + fd_table->offset += blksize; + } + return size; +} diff --git a/fs/iso9660/file.c b/fs/iso9660/file.c new file mode 100644 index 00000000..5ea20921 --- /dev/null +++ b/fs/iso9660/file.c @@ -0,0 +1,78 @@ +/* + * fiwix/fs/iso9660/file.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct fs_operations iso9660_file_fsop = { + 0, + 0, + + iso9660_file_open, + iso9660_file_close, + file_read, + NULL, /* write */ + NULL, /* ioctl */ + iso9660_file_lseek, + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + iso9660_bmap, + NULL, /* lookup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +int iso9660_file_open(struct inode *i, struct fd *fd_table) +{ + if(fd_table->flags & (O_WRONLY | O_RDWR | O_TRUNC | O_APPEND)) { + return -ENOENT; + } + fd_table->offset = 0; + return 0; +} + +int iso9660_file_close(struct inode *i, struct fd *fd_table) +{ + return 0; +} + +int iso9660_file_lseek(struct inode *i, __off_t offset) +{ + return offset; +} diff --git a/fs/iso9660/inode.c b/fs/iso9660/inode.c new file mode 100644 index 00000000..e0c4b5f6 --- /dev/null +++ b/fs/iso9660/inode.c @@ -0,0 +1,184 @@ +/* + * fiwix/fs/iso9660/inode.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int read_pathtable(struct inode *i) +{ + int n, offset, pt_len, pt_blk; + struct iso9660_sb_info *sbi; + struct iso9660_pathtable_record *ptr; + struct buffer *buf; + + sbi = (struct iso9660_sb_info *)&i->sb->u.iso9660; + pt_len = isonum_733(sbi->sb->path_table_size); + pt_blk = isonum_731(sbi->sb->type_l_path_table); + + if(pt_len > PAGE_SIZE) { + printk("WARNING: %s(): path table record size (%d) > 4096, not supported yet.\n", __FUNCTION__, pt_len); + return -EINVAL; + } + + if(!(sbi->pathtable_raw = (void *)kmalloc())) { + return -ENOMEM; + } + offset = 0; + while(offset < pt_len) { + if(!(buf = bread(i->dev, pt_blk++, BLKSIZE_2K))) { + kfree((unsigned int)sbi->pathtable_raw); + return -EIO; + } + memcpy_b(sbi->pathtable_raw + offset, (void *)buf->data, MIN(pt_len - offset, BLKSIZE_2K)); + offset += MIN(pt_len - offset, BLKSIZE_2K); + brelse(buf); + } + + /* allocate and count the number of records in the Path Table */ + offset = n = 0; + if(!(sbi->pathtable = (struct iso9660_pathtable_record **)kmalloc())) { + kfree((unsigned int)sbi->pathtable_raw); + return -ENOMEM; + } + sbi->pathtable[n] = NULL; + while(offset < pt_len) { + ptr = (struct iso9660_pathtable_record *)(sbi->pathtable_raw + offset); + sbi->pathtable[++n] = ptr; + offset += sizeof(struct iso9660_pathtable_record) + isonum_711(ptr->length) + (isonum_711(ptr->length) & 1); + } + sbi->paths = n; + + return 0; +} + +static int get_parent_dir_size(struct superblock *sb, __blk_t extent) +{ + int n; + struct iso9660_pathtable_record *ptr; + __blk_t parent; + + for(n = 0; n < sb->u.iso9660.paths; n++) { + ptr = (struct iso9660_pathtable_record *)sb->u.iso9660.pathtable[n]; + if(isonum_731(ptr->extent) == extent) { + + parent = isonum_723(ptr->parent); + ptr = (struct iso9660_pathtable_record *)sb->u.iso9660.pathtable[parent]; + parent = isonum_731(ptr->extent); + return parent; + } + } + printk("WARNING: %s(): unable to locate extent '%d' in path table.\n", __FUNCTION__, extent); + return 0; +} + +int iso9660_read_inode(struct inode *i) +{ + int errno; + __u32 blksize; + struct superblock *sb; + struct iso9660_directory_record *d; + struct buffer *buf; + __blk_t dblock; + __off_t doffset; + + sb = (struct superblock *)i->sb; + if(!sb->u.iso9660.pathtable) { + if((errno = read_pathtable(i))) { + return errno; + } + } + + dblock = (i->inode & ~ISO9660_INODE_MASK) >> ISO9660_INODE_BITS; + doffset = i->inode & ISO9660_INODE_MASK; + blksize = i->sb->s_blocksize; + + /* FIXME: it only looks in one directory block */ + if(!(buf = bread(i->dev, dblock, blksize))) { + return -EIO; + } + + if(doffset >= blksize) { + printk("WARNING: %s(): inode %d (dblock=%d, doffset=%d) not found in directory entry.\n", __FUNCTION__, i->inode, dblock, doffset); + brelse(buf); + return -EIO; + } + d = (struct iso9660_directory_record *)(buf->data + doffset); + + i->i_mode = S_IFREG; + if((char)d->flags[0] & ISO9660_FILE_ISDIR) { + i->i_mode = S_IFDIR; + } + if(!((char)d->flags[0] & ISO9660_FILE_HASOWNER)) { + i->i_mode |= S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + } + i->i_uid = 0; + i->i_size = isonum_733(d->size); + i->i_atime = isodate(d->date); + i->i_ctime = isodate(d->date); + i->i_mtime = isodate(d->date); + i->i_gid = 0; + i->i_nlink = 1; + i->i_blocks = 0; /* FIXME */ + i->i_flags = 0; /* FIXME */ + i->locked = 1; + i->dirty = 0; + i->mount_point = NULL; + i->count = 1; + + i->u.iso9660.i_extent = isonum_733(d->extent); + check_rrip_inode(d, i); + brelse(buf); + + switch(i->i_mode & S_IFMT) { + case S_IFCHR: + i->fsop = &def_chr_fsop; + break; + case S_IFBLK: + i->fsop = &def_blk_fsop; + break; + case S_IFIFO: + i->fsop = &pipefs_fsop; + /* it's a union so we need to clear pipefs_inode */ + memset_b(&i->u.pipefs, NULL, sizeof(struct pipefs_inode)); + break; + case S_IFDIR: + i->fsop = &iso9660_dir_fsop; + i->i_nlink++; + break; + case S_IFREG: + i->fsop = &iso9660_file_fsop; + break; + case S_IFLNK: + i->fsop = &iso9660_symlink_fsop; + break; + case S_IFSOCK: + i->fsop = NULL; + break; + default: + PANIC("invalid inode (%d) mode %08o.\n", i->inode, i->i_mode); + } + return 0; +} + +int iso9660_bmap(struct inode *i, __off_t offset, int mode) +{ + __blk_t block; + + block = i->u.iso9660.i_extent + (offset / i->sb->s_blocksize); + return block; +} diff --git a/fs/iso9660/namei.c b/fs/iso9660/namei.c new file mode 100644 index 00000000..044d6551 --- /dev/null +++ b/fs/iso9660/namei.c @@ -0,0 +1,122 @@ +/* + * fiwix/fs/iso9660/namei.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int iso9660_lookup(const char *name, struct inode *dir, struct inode **i_res) +{ + __blk_t dblock; + __u32 blksize; + int len, dnlen; + unsigned int offset, doffset; + struct buffer *buf; + struct iso9660_directory_record *d; + __ino_t inode; + int nm_len; + char *nm_name; + + blksize = dir->sb->s_blocksize; + inode = offset = 0; + len = strlen(name); + dir->count++; + + while(offset < dir->i_size && !inode) { + if((dblock = bmap(dir, offset, FOR_READING)) < 0) { + return dblock; + } + if(dblock) { + if(!(buf = bread(dir->dev, dblock, blksize))) { + return -EIO; + } + doffset = 0; + do { + d = (struct iso9660_directory_record *)(buf->data + doffset); + if(isonum_711(d->length) == 0) { + break; + } + if(len == 1) { + if(name[0] == '.' && name[1] == NULL) { + if(isonum_711(d->name_len) == 1 && d->name[0] == 0) { + inode = dir->inode; + } + } + } + if(len == 2) { + if(name[0] == '.' && name[1] == '.' && name[2] == NULL) { + if(isonum_711(d->name_len) == 1 && d->name[0] == 1) { + inode = dir->u.iso9660.i_parent->inode; + } + } + } + if(!(nm_name = (char *)kmalloc())) { + return -ENOMEM; + } + nm_len = 0; + if(dir->sb->u.iso9660.rrip) { + nm_len = get_rrip_filename(d, dir, nm_name); + } + if(nm_len) { + dnlen = nm_len; + } else { + dnlen = isonum_711(d->name_len); + if(!((char)d->flags[0] & ISO9660_FILE_ISDIR)) { + iso9660_cleanfilename(d->name, dnlen); + dnlen = strlen(d->name); + } + } + if(len == dnlen) { + if(nm_len) { + if(strncmp(nm_name, name, dnlen) == 0) { + inode = (dblock << ISO9660_INODE_BITS) + (doffset & ISO9660_INODE_MASK); + } + } else { + if(strncmp(d->name, name, dnlen) == 0) { + inode = (dblock << ISO9660_INODE_BITS) + (doffset & ISO9660_INODE_MASK); + } + } + } + kfree((unsigned int)nm_name); + doffset += isonum_711(d->length); + } while((doffset < blksize) && (!inode)); + brelse(buf); + offset += blksize; + if(inode) { + /* + * This prevents a deadlock in iget() when + * trying to lock '.' when 'dir' is the same + * directory (ls -lai ). + */ + if(inode == dir->inode) { + *i_res = dir; + return 0; + } + + if(!(*i_res = iget(dir->sb, inode))) { + return -EACCES; + } + if(S_ISDIR((*i_res)->i_mode)) { + if(!(*i_res)->u.iso9660.i_parent) { + (*i_res)->u.iso9660.i_parent = dir; + } + } + iput(dir); + return 0; + } + } + } + iput(dir); + return -ENOENT; +} diff --git a/fs/iso9660/rrip.c b/fs/iso9660/rrip.c new file mode 100644 index 00000000..1038650f --- /dev/null +++ b/fs/iso9660/rrip.c @@ -0,0 +1,340 @@ +/* + * fiwix/fs/iso9660/rrip.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void check_rrip_inode(struct iso9660_directory_record *d, struct inode *i) +{ + unsigned int total_len; + unsigned int len; + unsigned int sig; + int n, nm_len, rootflag; + struct susp_rrip *rrip; + unsigned int dev_h, dev_l; + unsigned int ce_block, ce_offset, ce_size; + struct buffer *buf; + unsigned char *sue; + int sl_len; + struct rrip_sl_component *slc; + + ce_block = ce_offset = ce_size = 0; + buf = NULL; + total_len = isonum_711(d->length); + len = isonum_711(d->name_len); + if(!(len % 2)) { + len++; + } + sue = (unsigned char *)d->name; + nm_len = 0; + +loop: + if(ce_block && ce_size) { + /* FIXME: it only looks in one directory block */ + if(!(buf = bread(i->dev, ce_block, i->sb->s_blocksize))) { + return; + } + sue = (unsigned char *)buf->data + ce_offset; + total_len = ce_size; + len = 0; + } + + while(len < total_len) { + rrip = (struct susp_rrip *)(sue + len); + if(rrip->len == 0) { + break; + } + sig = GET_SIG(rrip->signature[0], rrip->signature[1]); + switch(sig) { + case GET_SIG('S', 'P'): + if(rrip->u.sp.magic[0] != SP_MAGIC1 || rrip->u.sp.magic[1] != SP_MAGIC2) { + if(ce_block) { + brelse(buf); + } + return; + } + break; + case GET_SIG('C', 'E'): + if(ce_block) { + brelse(buf); + } + ce_block = isonum_733(rrip->u.ce.block); + ce_offset = isonum_733(rrip->u.ce.offset); + ce_size = isonum_733(rrip->u.ce.size); + goto loop; + break; + case GET_SIG('E', 'R'): + i->sb->u.iso9660.rrip = 1; + printk("ISO 9660 Extensions: "); + for(n = 0; n < rrip->u.er.len_id; n++) { + printk("%c", rrip->u.er.data[n]); + } + printk("\n"); + break; + case GET_SIG('P', 'X'): + i->i_mode = isonum_733(rrip->u.px.mode); + i->i_nlink = isonum_733(rrip->u.px.nlink); + i->i_uid = isonum_733(rrip->u.px.uid); + i->i_gid = isonum_733(rrip->u.px.gid); + break; + case GET_SIG('P', 'N'): + if(S_ISBLK(i->i_mode) || S_ISCHR(i->i_mode)) { + dev_h = isonum_733(rrip->u.pn.dev_h); + dev_l = isonum_733(rrip->u.pn.dev_l); + i->rdev = MKDEV(dev_h, dev_l); + } + break; + case GET_SIG('S', 'L'): + sl_len = rootflag = 0; + slc = (struct rrip_sl_component *)&rrip->u.sl.area; + while(sl_len < (rrip->len - 5)) { + if(sl_len && !rootflag) { + nm_len++; + } + rootflag = 0; + switch(slc->flags & 0xE) { + case 0: + nm_len += slc->len; + break; + case SL_CURRENT: + nm_len += 1; + break; + case SL_PARENT: + nm_len += 2; + break; + case SL_ROOT: + nm_len += 1; + rootflag = 1; + break; + default: + printk("WARNING: %s(): unsupported RRIP SL flags %d.\n", __FUNCTION__, slc->flags & 0xE); + } + slc = (struct rrip_sl_component *)(((char *)slc) + slc->len + sizeof(struct rrip_sl_component)); + sl_len += slc->len + sizeof(struct rrip_sl_component); + } + i->i_size = nm_len; + break; + case GET_SIG('T', 'F'): + n = 0; + if(rrip->u.tf.flags & TF_CREATION) { + i->i_ctime = isodate(rrip->u.tf.times[n++].time); + } + if(rrip->u.tf.flags & TF_MODIFY) { + i->i_mtime = isodate(rrip->u.tf.times[n++].time); + } + if(rrip->u.tf.flags & TF_ACCESS) { + i->i_atime = isodate(rrip->u.tf.times[n++].time); + } + if(rrip->u.tf.flags & TF_ATTRIBUTES) { + i->i_ctime = isodate(rrip->u.tf.times[n++].time); + } + break; + } + len += rrip->len; + } + if(ce_block) { + brelse(buf); + } +} + +int get_rrip_filename(struct iso9660_directory_record *d, struct inode *i, char *name) +{ + unsigned int total_len; + unsigned int len; + unsigned int sig; + int nm_len; + struct susp_rrip *rrip; + unsigned int ce_block, ce_offset, ce_size; + struct buffer *buf; + unsigned char *sue; + + ce_block = ce_offset = ce_size = 0; + buf = NULL; + total_len = isonum_711(d->length); + len = isonum_711(d->name_len); + if(!(len % 2)) { + len++; + } + sue = (unsigned char *)d->name; + nm_len = 0; + +loop: + if(ce_block && ce_size) { + /* FIXME: it only looks in one directory block */ + if(!(buf = bread(i->dev, ce_block, i->sb->s_blocksize))) { + return 0; + } + sue = (unsigned char *)buf->data + ce_offset; + total_len = ce_size; + len = 0; + } + + while(len < total_len) { + rrip = (struct susp_rrip *)(sue + len); + if(rrip->len == 0) { + break; + } + sig = GET_SIG(rrip->signature[0], rrip->signature[1]); + switch(sig) { + case GET_SIG('S', 'P'): + if(rrip->u.sp.magic[0] != SP_MAGIC1 || rrip->u.sp.magic[1] != SP_MAGIC2) { + if(ce_block) { + brelse(buf); + } + return 0; + } + break; + case GET_SIG('C', 'E'): + if(ce_block) { + brelse(buf); + } + ce_block = isonum_733(rrip->u.ce.block); + ce_offset = isonum_733(rrip->u.ce.offset); + ce_size = isonum_733(rrip->u.ce.size); + goto loop; + case GET_SIG('N', 'M'): + if(rrip->u.nm.flags) { /* FIXME: & ~(NM_CONTINUE | NM_CURRENT | NM_PARENT)) { */ + printk("WARNING: %s(): unsupported NM flag settings (%d).\n", __FUNCTION__, rrip->u.nm.flags); + if(ce_block) { + brelse(buf); + } + return 0; + } + nm_len = rrip->len - 5; + memcpy_b(name, rrip->u.nm.name, nm_len); + name[nm_len] = NULL; + break; + } + len += rrip->len; + } + if(ce_block) { + brelse(buf); + } + return nm_len; +} + +int get_rrip_symlink(struct inode *i, char *name) +{ + unsigned int total_len; + unsigned int len; + unsigned int sig; + int nm_len; + struct susp_rrip *rrip; + unsigned int ce_block, ce_offset, ce_size; + struct buffer *buf; + struct buffer *buf2; + unsigned char *sue; + struct iso9660_directory_record *d; + __blk_t dblock; + __off_t doffset; + int sl_len, rootflag; + struct rrip_sl_component *slc; + + dblock = (i->inode & ~ISO9660_INODE_MASK) >> ISO9660_INODE_BITS; + doffset = i->inode & ISO9660_INODE_MASK; + /* FIXME: it only looks in one directory block */ + if(!(buf = bread(i->dev, dblock, i->sb->s_blocksize))) { + return -EIO; + } + d = (struct iso9660_directory_record *)(buf->data + doffset); + + ce_block = ce_offset = ce_size = 0; + buf2 = NULL; + total_len = isonum_711(d->length); + len = isonum_711(d->name_len); + if(!(len % 2)) { + len++; + } + sue = (unsigned char *)d->name; + nm_len = 0; + +loop: + if(ce_block && ce_size) { + /* FIXME: it only looks in one directory block */ + if(!(buf2 = bread(i->dev, ce_block, i->sb->s_blocksize))) { + return 0; + } + sue = (unsigned char *)buf2->data + ce_offset; + total_len = ce_size; + len = 0; + } + + while(len < total_len) { + rrip = (struct susp_rrip *)(sue + len); + if(rrip->len == 0) { + break; + } + sig = GET_SIG(rrip->signature[0], rrip->signature[1]); + switch(sig) { + case GET_SIG('S', 'P'): + if(rrip->u.sp.magic[0] != SP_MAGIC1 || rrip->u.sp.magic[1] != SP_MAGIC2) { + if(ce_block) { + brelse(buf2); + } + return 0; + } + break; + case GET_SIG('C', 'E'): + if(ce_block) { + brelse(buf2); + } + ce_block = isonum_733(rrip->u.ce.block); + ce_offset = isonum_733(rrip->u.ce.offset); + ce_size = isonum_733(rrip->u.ce.size); + goto loop; + case GET_SIG('S', 'L'): + sl_len = rootflag = 0; + slc = (struct rrip_sl_component *)&rrip->u.sl.area; + while(sl_len < (rrip->len - 5)) { + if(sl_len && !rootflag) { + strcat(name, "/"); + nm_len++; + } + rootflag = 0; + switch(slc->flags & 0xE) { + case 0: + nm_len += slc->len; + strncat(name, slc->name, slc->len); + break; + case SL_CURRENT: + nm_len += 1; + strcat(name, "."); + break; + case SL_PARENT: + nm_len += 2; + strcat(name, ".."); + break; + case SL_ROOT: + nm_len += 1; + rootflag = 1; + strcat(name, "/"); + break; + default: + printk("WARNING: %s(): unsupported RRIP SL flags %d.\n", __FUNCTION__, slc->flags & 0xE); + } + slc = (struct rrip_sl_component *)(((char *)slc) + slc->len + sizeof(struct rrip_sl_component)); + sl_len += slc->len + sizeof(struct rrip_sl_component); + } + name[nm_len] = NULL; + break; + } + len += rrip->len; + } + if(ce_block) { + brelse(buf2); + } + brelse(buf); + return nm_len; +} diff --git a/fs/iso9660/super.c b/fs/iso9660/super.c new file mode 100644 index 00000000..51ac3ca9 --- /dev/null +++ b/fs/iso9660/super.c @@ -0,0 +1,224 @@ +/* + * fiwix/fs/iso9660/super.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct fs_operations iso9660_fsop = { + FSOP_REQUIRES_DEV, + NULL, + + NULL, /* open */ + NULL, /* close */ + NULL, /* read */ + NULL, /* write */ + NULL, /* ioctl */ + NULL, /* lseek */ + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lookup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + iso9660_read_inode, + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + iso9660_statfs, + iso9660_read_superblock, + NULL, /* remount_fs */ + NULL, /* write_superblock */ + iso9660_release_superblock +}; + +int isonum_711(char *str) +{ + unsigned char *le; + + le = (unsigned char *)str; + return le[0]; +} + +/* return a 16bit little-endian number */ +int isonum_723(char *str) +{ + unsigned char *le; + + le = (unsigned char *)str; + return le[0] | (le[1] << 8); +} + +/* return a 32bit little-endian number */ +int isonum_731(char *str) +{ + unsigned char *le; + + le = (unsigned char *)str; + return le[0] | (le[1] << 8) | (le[2] << 16) | (le[3] << 24); +} + +/* return a 32bit little-endian number */ +int isonum_733(char *p) +{ + return isonum_731(p); +} + +/* return a date and time format */ +unsigned long int isodate(char *p) +{ + struct mt mt; + + if(!p[0]) { + return 0; + } + + mt.mt_sec = p[5]; + mt.mt_min = p[4]; + mt.mt_hour = p[3]; + mt.mt_day = p[2]; + mt.mt_month = p[1]; + mt.mt_year = p[0]; + mt.mt_year += 1900; + mt.mt_min += p[6] * 15; + + return mktime(&mt); +} + +/* return a clean filename */ +int iso9660_cleanfilename(char *filename, int len) +{ + int n; + char *p; + + p = filename; + if(len > 2) { + for(n = 0; n < len; n++) { + if((len - n) == 2) { + if(p[n] == ';' && p[n + 1] == '1') { + filename[n] = NULL; + if(p[n - 1] == '.') { + filename[n - 1] = NULL; + } + return 0; + } + } + } + } + return 1; +} + +void iso9660_statfs(struct superblock *sb, struct statfs *statfsbuf) +{ + statfsbuf->f_type = ISO9660_SUPER_MAGIC; + statfsbuf->f_bsize = sb->s_blocksize; + statfsbuf->f_blocks = isonum_733(sb->u.iso9660.sb->volume_space_size); + statfsbuf->f_bfree = 0; + statfsbuf->f_bavail = 0; + statfsbuf->f_files = 0; /* FIXME */ + statfsbuf->f_ffree = 0; + /* statfsbuf->f_fsid = ? */ + statfsbuf->f_namelen = NAME_MAX; +} + +int iso9660_read_superblock(__dev_t dev, struct superblock *sb) +{ + struct buffer *buf; + struct iso9660_super_block *iso9660sb; + struct iso9660_super_block *pvd; + struct iso9660_directory_record *dr; + __ino_t root_inode; + int n; + + superblock_lock(sb); + pvd = NULL; + + for(n = 0; n < ISO9660_MAX_VD; n++) { + if(!(buf = bread(dev, ISO9660_SUPERBLOCK + n, BLKSIZE_2K))) { + superblock_unlock(sb); + return -EIO; + } + + iso9660sb = (struct iso9660_super_block *)buf->data; + if(strncmp(iso9660sb->id, ISO9660_STANDARD_ID, sizeof(iso9660sb->id)) || (isonum_711(iso9660sb->type) == ISO9660_VD_END)) { + break; + } + if(isonum_711(iso9660sb->type) == ISO9660_VD_PRIMARY) { + pvd = (struct iso9660_super_block *)buf->data; + break; + } + brelse(buf); + } + if(!pvd) { + printk("WARNING: %s(): invalid filesystem type or bad superblock on device %d,%d.\n", __FUNCTION__, MAJOR(dev), MINOR(dev)); + superblock_unlock(sb); + brelse(buf); + return -EINVAL; + } + + dr = (struct iso9660_directory_record *)pvd->root_directory_record; + root_inode = isonum_711(dr->extent); + + sb->dev = dev; + sb->fsop = &iso9660_fsop; + sb->flags = MS_RDONLY; + sb->s_blocksize = isonum_723(pvd->logical_block_size); + sb->u.iso9660.rrip = 0; + if(!(sb->u.iso9660.sb = (void *)kmalloc())) { + superblock_unlock(sb); + brelse(buf); + return -ENOMEM; + } + memcpy_b(sb->u.iso9660.sb, pvd, sizeof(struct iso9660_super_block)); + brelse(buf); + + root_inode = (root_inode << ISO9660_INODE_BITS) + (0 & ISO9660_INODE_MASK); + if(!(sb->root = iget(sb, root_inode))) { + printk("WARNING: %s(): unable to get root inode.\n", __FUNCTION__); + superblock_unlock(sb); + return -EINVAL; + } + sb->u.iso9660.s_root_inode = root_inode; + + superblock_unlock(sb); + return 0; +} + +void iso9660_release_superblock(struct superblock *sb) +{ + kfree((unsigned int)sb->u.iso9660.sb); + kfree((unsigned int)sb->u.iso9660.pathtable); + kfree((unsigned int)sb->u.iso9660.pathtable_raw); +} + +int iso9660_init(void) +{ + return register_filesystem("iso9660", &iso9660_fsop); +} diff --git a/fs/iso9660/symlink.c b/fs/iso9660/symlink.c new file mode 100644 index 00000000..debe33a9 --- /dev/null +++ b/fs/iso9660/symlink.c @@ -0,0 +1,109 @@ +/* + * fiwix/fs/iso9660/symlink.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct fs_operations iso9660_symlink_fsop = { + 0, + 0, + + NULL, /* open */ + NULL, /* close */ + NULL, /* read */ + NULL, /* write */ + NULL, /* ioctl */ + NULL, /* lseek */ + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + iso9660_readlink, + iso9660_followlink, + NULL, /* bmap */ + NULL, /* lookup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +int iso9660_readlink(struct inode *i, char *buffer, __size_t count) +{ + __off_t size_read; + char *name; + + if(!(name = (char *)kmalloc())) { + return -ENOMEM; + } + + inode_lock(i); + name[0] = NULL; + if((size_read = get_rrip_symlink(i, name))) { + size_read = MIN(size_read, count); + memcpy_b(buffer, name, size_read); + } + kfree((unsigned int)name); + inode_unlock(i); + return size_read; +} + +int iso9660_followlink(struct inode *dir, struct inode *i, struct inode **i_res) +{ + char *name; + __off_t size_read; + __ino_t errno; + + if(!i) { + return -ENOENT; + } + if(!S_ISLNK(i->i_mode)) { + printk("%s(): Oops, inode '%d' is not a symlink (!?).\n", __FUNCTION__, i->inode); + return 0; + } + + if(!(name = (char *)kmalloc())) { + return -ENOMEM; + } + + name[0] = NULL; + if((size_read = get_rrip_symlink(i, name))) { + iput(i); + if((errno = parse_namei(name, dir, i_res, NULL, FOLLOW_LINKS))) { + kfree((unsigned int)name); + return errno; + } + } + kfree((unsigned int)name); + return 0; +} diff --git a/fs/locks.c b/fs/locks.c new file mode 100644 index 00000000..a43eef25 --- /dev/null +++ b/fs/locks.c @@ -0,0 +1,208 @@ +/* + * fiwix/fs/locks.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct resource flock_resource = { NULL, NULL }; + +static struct flock_file * get_new_flock(struct inode *i) +{ + int n; + struct flock_file *ff; + + lock_resource(&flock_resource); + + for(n = 0; n < NR_FLOCKS; n++) { + ff = &flock_file_table[n]; + if(!ff->inode) { + ff->inode = i; /* mark it as busy */ + unlock_resource(&flock_resource); + return ff; + } + } + + printk("WARNING: %s(): no more free slots in flock file table.\n"); + unlock_resource(&flock_resource); + return NULL; +} + +static void release_flock(struct flock_file *ff) +{ + memset_b(ff, 0, sizeof(struct flock_file)); +} + +static struct flock_file * get_flock_file(struct inode *i, int op, struct proc *p) +{ + int n; + struct flock_file *ff; + + lock_resource(&flock_resource); + + ff = NULL; + for(n = 0; n < NR_FLOCKS; n++) { + ff = &flock_file_table[n]; + if(ff->inode != i) { + continue; + } + if(p && p != ff->proc) { + continue; + } + break; + } + unlock_resource(&flock_resource); + return ff; +} + +int posix_lock(int ufd, int cmd, struct flock *fl) +{ + int n; + struct flock_file *ff; + struct inode *i; + unsigned char type; + + lock_resource(&flock_resource); + i = fd_table[current->fd[ufd]].inode; + for(n = 0; n < NR_FLOCKS; n++) { + ff = &flock_file_table[n]; + if(ff->inode != i) { + continue; + } + break; + } + unlock_resource(&flock_resource); + if(cmd == F_GETLK) { + if(ff->inode == i) { + fl->l_type = ff->type & LOCK_SH ? F_RDLCK : F_WRLCK; + fl->l_whence = SEEK_SET; + fl->l_start = 0; + fl->l_len = 0; + fl->l_pid = ff->proc->pid; + } else { + fl->l_type = F_UNLCK; + } + } + + switch(fl->l_type) { + case F_RDLCK: + type = LOCK_SH; + break; + case F_WRLCK: + type = LOCK_EX; + break; + case F_UNLCK: + type = LOCK_UN; + break; + default: + return -EINVAL; + } + if(cmd == F_SETLK) { + return flock_inode(i, type); + } + if(cmd == F_SETLKW) { + return flock_inode(i, type | LOCK_NB); + } + return 0; +} + +void flock_release_inode(struct inode *i) +{ + int n; + struct flock_file *ff; + + lock_resource(&flock_resource); + for(n = 0; n < NR_FLOCKS; n++) { + ff = &flock_file_table[n]; + if(ff->inode != i) { + continue; + } + if(ff->proc != current) { + continue; + } + wakeup(ff); + release_flock(ff); + } + unlock_resource(&flock_resource); +} + +int flock_inode(struct inode *i, int op) +{ + int n; + struct flock_file *ff, *new; + + if(op & LOCK_UN) { + if((ff = get_flock_file(i, op, current))) { + wakeup(ff); + release_flock(ff); + } + return 0; + } + +loop: + lock_resource(&flock_resource); + new = NULL; + for(n = 0; n < NR_FLOCKS; n++) { + ff = &flock_file_table[n]; + if(ff->inode != i) { + continue; + } + if(op & LOCK_SH) { + if(ff->type & LOCK_EX) { + if(ff->proc == current) { + new = ff; + wakeup(ff); + break; + } + unlock_resource(&flock_resource); + if(op & LOCK_NB) { + return -EWOULDBLOCK; + } + if(sleep(ff, PROC_INTERRUPTIBLE)) { + return -EINTR; + } + goto loop; + } + } + if(op & LOCK_EX) { + if(ff->proc == current) { + new = ff; + continue; + } + unlock_resource(&flock_resource); + if(op & LOCK_NB) { + return -EWOULDBLOCK; + } + if(sleep(ff, PROC_INTERRUPTIBLE)) { + return -EINTR; + } + goto loop; + } + } + unlock_resource(&flock_resource); + + if(!new) { + if(!(new = get_new_flock(i))) { + return -ENOLCK; + } + } + new->inode = i; + new->type = op; + new->proc = current; + + return 0; +} + +void flock_init(void) +{ + memset_b(flock_file_table, NULL, sizeof(flock_file_table)); +} diff --git a/fs/minix/Makefile b/fs/minix/Makefile new file mode 100644 index 00000000..46e5adb8 --- /dev/null +++ b/fs/minix/Makefile @@ -0,0 +1,19 @@ +# fiwix/fs/minix/Makefile +# +# Copyright 2018, Jordi Sanfeliu. All rights reserved. +# Distributed under the terms of the Fiwix License. +# + +.S.o: + $(CC) -traditional -I$(INCLUDE) -c -o $@ $< +.c.o: + $(CC) $(CFLAGS) -c -o $@ $< + +OBJS = super.o bitmaps.o inode.o namei.o symlink.o dir.o file.o v1_inode.o v2_inode.o + +minix: $(OBJS) + $(LD) $(LDFLAGS) -r $(OBJS) -o minix.o + +clean: + rm -f *.o + diff --git a/fs/minix/bitmaps.c b/fs/minix/bitmaps.c new file mode 100644 index 00000000..0ba6b3c3 --- /dev/null +++ b/fs/minix/bitmaps.c @@ -0,0 +1,166 @@ +/* + * fiwix/fs/minix/bitmaps.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define COUNT 1 +#define FIRST_ZERO 2 + +static int count_bits(struct superblock *sb, __blk_t offset, int num, int blocks, int mode) +{ + unsigned char c; + int blksize; + int n, n2, last, bits, count, mapb; + struct buffer *buf; + + count = mapb = 0; + blksize = sb->s_blocksize; + + while(offset < blocks) { + if(!(buf = bread(sb->dev, offset, blksize))) { + return -EIO; + } + last = (num / 8) > blksize ? blksize : (num / 8); + for(n = 0; n < last; n++) { + c = (unsigned char)buf->data[n]; + bits = n < last ? 8 : num & 8; + for(n2 = 0; n2 < bits; n2++) { + if(c & (1 << n2)) { + if(mode == COUNT) { + count++; + } + } else { + if(mode == FIRST_ZERO) { + brelse(buf); + return n2 + ((n * 8) + (mapb * blksize * 8)); + } + } + } + } + offset++; + mapb++; + num -= (blksize * 8); + brelse(buf); + } + return count; +} + +int minix_change_bit(int mode, struct superblock *sb, int map, int item) +{ + int byte, bit, mask; + struct buffer *buf; + + map += item / (sb->s_blocksize * 8); + byte = (item % (sb->s_blocksize * 8)) / 8; + bit = (item % (sb->s_blocksize * 8)) % 8; + mask = 1 << bit; + + if(!(buf = bread(sb->dev, map, sb->s_blocksize))) { + return -EIO; + } + + if(mode == CLEAR_BIT) { + if(!(buf->data[byte] & mask)) { + brelse(buf); + return 1; + } + buf->data[byte] &= ~mask; + } + if(mode == SET_BIT) { + if((buf->data[byte] & mask)) { + brelse(buf); + return 1; + } + buf->data[byte] |= mask; + } + + bwrite(buf); + return 0; +} + +int minix_balloc(struct superblock *sb) +{ + int map, block, errno; + + superblock_lock(sb); + + map = 1 + SUPERBLOCK + sb->u.minix.sb.s_imap_blocks; + + if(!(block = minix_find_first_zero(sb, map, sb->u.minix.nzones, map + sb->u.minix.sb.s_zmap_blocks))) { + superblock_unlock(sb); + return -ENOSPC; + } + + errno = minix_change_bit(SET_BIT, sb, map, block); + block += sb->u.minix.sb.s_firstdatazone - 1; + + if(errno) { + if(errno < 0) { + printk("WARNING: %s(): unable to set block %d.\n", __FUNCTION__, block); + } else { + printk("WARNING: %s(): block %d is already marked as used!\n", __FUNCTION__, block); + } + } + + superblock_unlock(sb); + return block; +} + +void minix_bfree(struct superblock *sb, int block) +{ + int map, errno; + + if(block < sb->u.minix.sb.s_firstdatazone || block > sb->u.minix.nzones) { + printk("WARNING: %s(): block %d is not in datazone.\n", __FUNCTION__, block); + return; + } + + superblock_lock(sb); + + map = 1 + SUPERBLOCK + sb->u.minix.sb.s_imap_blocks; + block -= sb->u.minix.sb.s_firstdatazone - 1; + errno = minix_change_bit(CLEAR_BIT, sb, map, block); + + if(errno) { + if(errno < 0) { + printk("WARNING: %s(): unable to free block %d.\n", __FUNCTION__, block); + } else { + printk("WARNING: %s(): block %d is already marked as free!\n", __FUNCTION__, block); + } + } + + superblock_unlock(sb); + return; +} + +int minix_count_free_inodes(struct superblock *sb) +{ + __blk_t offset; + + offset = 1 + SUPERBLOCK; + return count_bits(sb, offset, sb->u.minix.sb.s_ninodes, offset + sb->u.minix.sb.s_imap_blocks, COUNT); +} + +int minix_count_free_blocks(struct superblock *sb) +{ + __blk_t offset; + + offset = 1 + SUPERBLOCK + sb->u.minix.sb.s_imap_blocks; + return count_bits(sb, offset, sb->u.minix.nzones, offset + sb->u.minix.sb.s_zmap_blocks, COUNT); +} + +int minix_find_first_zero(struct superblock *sb, __blk_t offset, int num, int blocks) +{ + return count_bits(sb, offset, num, blocks, FIRST_ZERO); +} diff --git a/fs/minix/dir.c b/fs/minix/dir.c new file mode 100644 index 00000000..e94f07ad --- /dev/null +++ b/fs/minix/dir.c @@ -0,0 +1,144 @@ +/* + * fiwix/fs/minix/dir.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct fs_operations minix_dir_fsop = { + 0, + 0, + + minix_dir_open, + minix_dir_close, + minix_dir_read, + minix_dir_write, + NULL, /* ioctl */ + NULL, /* lseek */ + minix_dir_readdir, + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + minix_bmap, + minix_lookup, + minix_rmdir, + minix_link, + minix_unlink, + minix_symlink, + minix_mkdir, + minix_mknod, + NULL, /* truncate */ + minix_create, + minix_rename, + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +int minix_dir_open(struct inode *i, struct fd *fd_table) +{ + fd_table->offset = 0; + return 0; +} + +int minix_dir_close(struct inode *i, struct fd *fd_table) +{ + return 0; +} + +int minix_dir_read(struct inode *i, struct fd *fd_table, char *buffer, __size_t count) +{ + return -EISDIR; +} + +int minix_dir_write(struct inode *i, struct fd *fd_table, const char *buffer, __size_t count) +{ + return -EBADF; +} + +int minix_dir_readdir(struct inode *i, struct fd *fd_table, struct dirent *dirent, unsigned int count) +{ + __blk_t block; + unsigned int doffset, offset; + unsigned int size, dirent_len; + struct minix_dir_entry *d; + int base_dirent_len; + int blksize; + struct buffer *buf; + + if(!(S_ISDIR(i->i_mode))) { + return -EBADF; + } + + blksize = i->sb->s_blocksize; + if(fd_table->offset > i->i_size) { + fd_table->offset = i->i_size; + } + + base_dirent_len = sizeof(dirent->d_ino) + sizeof(dirent->d_off) + sizeof(dirent->d_reclen); + doffset = offset = size = 0; + + while(doffset < count) { + if((block = bmap(i, fd_table->offset, FOR_READING)) < 0) { + return block; + } + if(block) { + if(!(buf = bread(i->dev, block, blksize))) { + return -EIO; + } + + doffset = fd_table->offset; + offset = fd_table->offset % blksize; + while(doffset < i->i_size && offset < blksize) { + d = (struct minix_dir_entry *)(buf->data + offset); + if(d->inode) { + dirent_len = (base_dirent_len + (strlen(d->name) + 1)) + 3; + dirent_len &= ~3; /* round up */ + dirent->d_ino = d->inode; + if((size + dirent_len) < count) { + dirent->d_off = doffset; + dirent->d_reclen = dirent_len; + memcpy_b(dirent->d_name, d->name, strlen(d->name)); + dirent->d_name[strlen(d->name)] = NULL; + dirent = (struct dirent *)((char *)dirent + dirent_len); + size += dirent_len; + } else { + break; + } + } + doffset += i->sb->u.minix.dirsize; + offset += i->sb->u.minix.dirsize; + } + brelse(buf); + } + fd_table->offset &= ~(blksize - 1); + doffset = fd_table->offset; + fd_table->offset += offset; + doffset += blksize; + } + + return size; +} diff --git a/fs/minix/file.c b/fs/minix/file.c new file mode 100644 index 00000000..7ff2ae11 --- /dev/null +++ b/fs/minix/file.c @@ -0,0 +1,133 @@ +/* + * fiwix/fs/minix/file.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct fs_operations minix_file_fsop = { + 0, + 0, + + minix_file_open, + minix_file_close, + file_read, + minix_file_write, + NULL, /* ioctl */ + minix_file_lseek, + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + minix_bmap, + NULL, /* lookup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + minix_truncate, + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +int minix_file_open(struct inode *i, struct fd *fd_table) +{ + if(fd_table->flags & O_APPEND) { + fd_table->offset = i->i_size; + } else { + fd_table->offset = 0; + } + if(fd_table->flags & O_TRUNC) { + i->i_size = 0; + minix_truncate(i, 0); + } + return 0; +} + +int minix_file_close(struct inode *i, struct fd *fd_table) +{ + return 0; +} + +int minix_file_write(struct inode *i, struct fd *fd_table, const char *buffer, __size_t count) +{ + __blk_t block; + __off_t total_written; + unsigned int boffset, bytes; + int blksize; + struct buffer *buf; + + inode_lock(i); + + blksize = i->sb->s_blocksize; + total_written = 0; + + if(fd_table->flags & O_APPEND) { + fd_table->offset = i->i_size; + } + + while(total_written < count) { + boffset = fd_table->offset % blksize; + if((block = bmap(i, fd_table->offset, FOR_WRITING)) < 0) { + inode_unlock(i); + return block; + } + bytes = blksize - boffset; + bytes = MIN(bytes, (count - total_written)); + if(!(buf = bread(i->dev, block, blksize))) { + inode_unlock(i); + return -EIO; + } + memcpy_b(buf->data + boffset, buffer + total_written, bytes); + update_page_cache(i, fd_table->offset, buffer + total_written, bytes); + bwrite(buf); + total_written += bytes; + boffset += bytes; + boffset %= blksize; + fd_table->offset += bytes; + } + + if(fd_table->offset > i->i_size) { + i->i_size = fd_table->offset; + } + i->i_ctime = CURRENT_TIME; + i->i_mtime = CURRENT_TIME; + i->dirty = 1; + + inode_unlock(i); + return total_written; +} + +int minix_file_lseek(struct inode *i, __off_t offset) +{ + return offset; +} diff --git a/fs/minix/inode.c b/fs/minix/inode.c new file mode 100644 index 00000000..24efc8ad --- /dev/null +++ b/fs/minix/inode.c @@ -0,0 +1,75 @@ +/* + * fiwix/fs/minix/inode.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int minix_read_inode(struct inode *i) +{ + if(i->sb->u.minix.version == 1) { + return v1_minix_read_inode(i); + } + + return v2_minix_read_inode(i); +} + +int minix_write_inode(struct inode *i) +{ + if(i->sb->u.minix.version == 1) { + return v1_minix_write_inode(i); + } + + return v2_minix_write_inode(i); +} + +int minix_ialloc(struct inode *i) +{ + if(i->sb->u.minix.version == 1) { + return v1_minix_ialloc(i); + } + + return v2_minix_ialloc(i); +} + +void minix_ifree(struct inode *i) +{ + if(i->sb->u.minix.version == 1) { + return v1_minix_ifree(i); + } + + return v2_minix_ifree(i); +} + +int minix_bmap(struct inode *i, __off_t offset, int mode) +{ + if(i->sb->u.minix.version == 1) { + return v1_minix_bmap(i, offset, mode); + } + + return v2_minix_bmap(i, offset, mode); +} + +int minix_truncate(struct inode *i, __off_t length) +{ + if(i->sb->u.minix.version == 1) { + return v1_minix_truncate(i, length); + } + + return v2_minix_truncate(i, length); +} diff --git a/fs/minix/namei.c b/fs/minix/namei.c new file mode 100644 index 00000000..afcb7a2e --- /dev/null +++ b/fs/minix/namei.c @@ -0,0 +1,699 @@ +/* + * fiwix/fs/minix/namei.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int is_dir_empty(struct inode *dir) +{ + __blk_t block; + unsigned int blksize; + unsigned int offset, doffset; + struct buffer *buf; + struct minix_dir_entry *d; + + blksize = dir->sb->s_blocksize; + doffset = dir->sb->u.minix.dirsize * 2; /* accept only "." and ".." */ + offset = 0; + + while(offset < dir->i_size) { + if((block = bmap(dir, offset, FOR_READING)) < 0) { + break; + } + if(block) { + if(!(buf = bread(dir->dev, block, blksize))) { + break; + } + do { + if(doffset + offset >= dir->i_size) { + break; + } + d = (struct minix_dir_entry *)(buf->data + doffset); + if(d->inode) { + brelse(buf); + return 0; + } + doffset += dir->sb->u.minix.dirsize; + } while(doffset < blksize); + brelse(buf); + offset += blksize; + doffset = 0; + } else { + break; + } + } + + return 1; +} + +/* finds the entry 'name' with inode 'i' in the directory 'dir' */ +static struct buffer * find_dir_entry(struct inode *dir, struct inode *i, struct minix_dir_entry **d_res, char *name) +{ + __blk_t block; + unsigned int blksize; + unsigned int offset, doffset; + struct buffer *buf; + + blksize = dir->sb->s_blocksize; + offset = 0; + + while(offset < dir->i_size) { + if((block = bmap(dir, offset, FOR_READING)) < 0) { + break; + } + if(block) { + if(!(buf = bread(dir->dev, block, blksize))) { + break; + } + doffset = 0; + do { + *d_res = (struct minix_dir_entry *)(buf->data + doffset); + if(!i) { + /* returns the first empty entry */ + if(!(*d_res)->inode || (doffset + offset >= dir->i_size)) { + if(doffset + offset >= dir->i_size) { + dir->i_size += dir->sb->u.minix.dirsize; + } + return buf; + } + } else { + if((*d_res)->inode == i->inode) { + /* returns the first matching inode */ + if(!name) { + return buf; + } + /* returns the matching inode and name */ + if(!strcmp((*d_res)->name, name)) { + return buf; + } + } + } + doffset += dir->sb->u.minix.dirsize; + } while(doffset < blksize); + brelse(buf); + offset += blksize; + } else { + break; + } + } + + *d_res = NULL; + return NULL; +} + +static struct buffer * add_dir_entry(struct inode *dir, struct minix_dir_entry **d_res) +{ + __blk_t block; + struct buffer *buf; + + if(!(buf = find_dir_entry(dir, NULL, d_res, NULL))) { + if((block = bmap(dir, dir->i_size, FOR_WRITING)) < 0) { + return NULL; + } + if(!(buf = bread(dir->dev, block, dir->sb->s_blocksize))) { + return NULL; + } + *d_res = (struct minix_dir_entry *)buf->data; + dir->i_size += dir->sb->u.minix.dirsize; + } + + return buf; +} + +static int is_prefix(struct inode *dir_new, struct inode *i_old) +{ + __ino_t inode; + int errno; + + errno = 0; + for(;;) { + if(dir_new == i_old) { + errno = 1; + break; + } + inode = dir_new->inode; + if(minix_lookup("..", dir_new, &dir_new)) { + break; + } + iput(dir_new); /* lookup eats 1 dir_new */ + if(dir_new->inode == inode) { + break; + } + } + return errno; +} + +int minix_lookup(const char *name, struct inode *dir, struct inode **i_res) +{ + __blk_t block; + unsigned int blksize; + unsigned int offset, doffset; + struct buffer *buf; + struct minix_dir_entry *d; + __ino_t inode; + + blksize = dir->sb->s_blocksize; + inode = offset = 0; + dir->count++; + + while(offset < dir->i_size && !inode) { + if((block = bmap(dir, offset, FOR_READING)) < 0) { + iput(dir); + return block; + } + if(block) { + if(!(buf = bread(dir->dev, block, blksize))) { + iput(dir); + return -EIO; + } + doffset = 0; + do { + d = (struct minix_dir_entry *)(buf->data + doffset); + if(d->inode) { + if(strlen(d->name) == strlen(name)) { + if(!(strcmp(d->name, name))) { + inode = d->inode; + } + } + } + doffset += dir->sb->u.minix.dirsize; + } while((doffset < blksize) && (!inode)); + + brelse(buf); + if(inode) { + if(!(*i_res = iget(dir->sb, inode))) { + iput(dir); + return -EACCES; + } + iput(dir); + return 0; + } + offset += blksize; + } else { + break; + } + } + iput(dir); + return -ENOENT; +} + +int minix_rmdir(struct inode *dir, struct inode *i) +{ + struct buffer *buf; + struct minix_dir_entry *d; + + inode_lock(i); + + if(!is_dir_empty(i)) { + inode_unlock(i); + return -ENOTEMPTY; + } + + inode_lock(dir); + + if(!(buf = find_dir_entry(dir, i, &d, NULL))) { + inode_unlock(i); + inode_unlock(dir); + return -ENOENT; + } + + d->inode = 0; + i->i_nlink = 0; + dir->i_nlink--; + + i->i_ctime = CURRENT_TIME; + dir->i_mtime = CURRENT_TIME; + dir->i_ctime = CURRENT_TIME; + + i->dirty = 1; + dir->dirty = 1; + + bwrite(buf); + + inode_unlock(i); + inode_unlock(dir); + return 0; +} + +int minix_link(struct inode *i_old, struct inode *dir_new, char *name) +{ + struct buffer *buf; + struct minix_dir_entry *d; + int n; + + inode_lock(i_old); + inode_lock(dir_new); + + if(!(buf = add_dir_entry(dir_new, &d))) { + inode_unlock(i_old); + inode_unlock(dir_new); + return -ENOSPC; + } + + d->inode = i_old->inode; + for(n = 0; n < i_old->sb->u.minix.namelen; n++) { + d->name[n] = name[n]; + if(!name[n]) { + break; + } + } + for(; n < i_old->sb->u.minix.namelen; n++) { + d->name[n] = 0; + } + + i_old->i_nlink++; + i_old->i_ctime = CURRENT_TIME; + dir_new->i_mtime = CURRENT_TIME; + dir_new->i_ctime = CURRENT_TIME; + + i_old->dirty = 1; + dir_new->dirty = 1; + + bwrite(buf); + + inode_unlock(i_old); + inode_unlock(dir_new); + return 0; +} + +int minix_unlink(struct inode *dir, struct inode *i, char *name) +{ + struct buffer *buf; + struct minix_dir_entry *d; + + inode_lock(dir); + inode_lock(i); + + if(!(buf = find_dir_entry(dir, i, &d, name))) { + inode_unlock(dir); + inode_unlock(i); + return -ENOENT; + } + + d->inode = 0; + i->i_nlink--; + + i->i_ctime = CURRENT_TIME; + dir->i_mtime = CURRENT_TIME; + dir->i_ctime = CURRENT_TIME; + + i->dirty = 1; + dir->dirty = 1; + + bwrite(buf); + + inode_unlock(dir); + inode_unlock(i); + return 0; +} + +int minix_symlink(struct inode *dir, char *name, char *oldname) +{ + struct buffer *buf, *buf_new; + struct inode *i; + struct minix_dir_entry *d; + unsigned int blksize; + int n, block; + char c; + + inode_lock(dir); + + if(!(i = ialloc(dir->sb))) { + inode_unlock(dir); + return -ENOSPC; + } + + i->i_mode = S_IFLNK | (S_IRWXU | S_IRWXG | S_IRWXO); + i->i_uid = current->euid; + i->i_gid = current->egid; + i->i_nlink = 1; + i->dev = dir->dev; + i->count = 1; + i->fsop = &minix_symlink_fsop; + i->dirty = 1; + + block = minix_balloc(dir->sb); + if(block < 0) { + i->i_nlink = 0; + iput(i); + inode_unlock(dir); + return -ENOSPC; + } + + if(i->sb->u.minix.version == 1) { + i->u.minix.u.i1_zone[0] = block; + } else { + i->u.minix.u.i2_zone[0] = block; + } + blksize = dir->sb->s_blocksize; + if(!(buf_new = bread(dir->dev, block, blksize))) { + minix_bfree(dir->sb, block); + i->i_nlink = 0; + iput(i); + inode_unlock(dir); + return -EIO; + } + + if(!(buf = add_dir_entry(dir, &d))) { + minix_bfree(dir->sb, block); + i->i_nlink = 0; + iput(i); + inode_unlock(dir); + return -ENOSPC; + } + + d->inode = i->inode; + for(n = 0; n < i->sb->u.minix.namelen; n++) { + d->name[n] = name[n]; + if(!name[n]) { + break; + } + } + for(; n < i->sb->u.minix.namelen; n++) { + d->name[n] = 0; + } + + for(n = 0; n < NAME_MAX; n++) { + if((c = oldname[n])) { + buf_new->data[n] = c; + continue; + } + break; + } + buf_new->data[n] = 0; + i->i_size = n; + + dir->i_mtime = CURRENT_TIME; + dir->i_ctime = CURRENT_TIME; + dir->dirty = 1; + + bwrite(buf); + bwrite(buf_new); + iput(i); + inode_unlock(dir); + return 0; +} + +int minix_mkdir(struct inode *dir, char *name, __mode_t mode) +{ + struct buffer *buf, *buf_new; + struct inode *i; + struct minix_dir_entry *d, *d_new; + unsigned int blksize; + int n, block; + + if(strlen(name) > dir->sb->u.minix.namelen) { + return -ENAMETOOLONG; + } + + inode_lock(dir); + + if(!(i = ialloc(dir->sb))) { + inode_unlock(dir); + return -ENOSPC; + } + + i->i_mode = ((mode & (S_IRWXU | S_IRWXG | S_IRWXO)) & ~current->umask); + i->i_mode |= S_IFDIR; + i->i_uid = current->euid; + i->i_gid = current->egid; + i->i_nlink = 1; + i->dev = dir->dev; + i->count = 1; + i->fsop = &minix_dir_fsop; + i->dirty = 1; + + if((block = bmap(i, 0, FOR_WRITING)) < 0) { + i->i_nlink = 0; + iput(i); + inode_unlock(dir); + return -ENOSPC; + } + + blksize = dir->sb->s_blocksize; + if(!(buf_new = bread(i->dev, block, blksize))) { + minix_bfree(dir->sb, block); + i->i_nlink = 0; + iput(i); + inode_unlock(dir); + return -EIO; + } + + if(!(buf = add_dir_entry(dir, &d))) { + minix_bfree(dir->sb, block); + i->i_nlink = 0; + iput(i); + inode_unlock(dir); + return -ENOSPC; + } + + d->inode = i->inode; + for(n = 0; n < i->sb->u.minix.namelen; n++) { + d->name[n] = name[n]; + if(!name[n] || name[n] == '/') { + break; + } + } + for(; n < i->sb->u.minix.namelen; n++) { + d->name[n] = 0; + } + + d_new = (struct minix_dir_entry *)buf_new->data; + d_new->inode = i->inode; + d_new->name[0] = '.'; + d_new->name[1] = 0; + i->i_size += i->sb->u.minix.dirsize; + i->i_nlink++; + d_new = (struct minix_dir_entry *)(buf_new->data + i->sb->u.minix.dirsize); + d_new->inode = dir->inode; + d_new->name[0] = '.'; + d_new->name[1] = '.'; + d_new->name[2] = 0; + i->i_size += i->sb->u.minix.dirsize; + + dir->i_mtime = CURRENT_TIME; + dir->i_ctime = CURRENT_TIME; + dir->i_nlink++; + dir->dirty = 1; + + bwrite(buf); + bwrite(buf_new); + iput(i); + inode_unlock(dir); + return 0; +} + +int minix_mknod(struct inode *dir, char *name, __mode_t mode, __dev_t dev) +{ + struct buffer *buf; + struct inode *i; + struct minix_dir_entry *d; + int n; + + inode_lock(dir); + + if(!(i = ialloc(dir->sb))) { + inode_unlock(dir); + return -ENOSPC; + } + + if(!(buf = add_dir_entry(dir, &d))) { + i->i_nlink = 0; + iput(i); + inode_unlock(dir); + return -ENOSPC; + } + + d->inode = i->inode; + for(n = 0; n < i->sb->u.minix.namelen; n++) { + d->name[n] = name[n]; + if(!name[n]) { + break; + } + } + for(; n < i->sb->u.minix.namelen; n++) { + d->name[n] = 0; + } + + i->i_mode = (mode & ~current->umask) & ~S_IFMT; + i->i_uid = current->euid; + i->i_gid = current->egid; + i->i_nlink = 1; + i->dev = dir->dev; + i->count = 1; + i->dirty = 1; + + switch(mode & S_IFMT) { + case S_IFCHR: + i->fsop = &def_chr_fsop; + i->rdev = dev; + i->i_mode |= S_IFCHR; + break; + case S_IFBLK: + i->fsop = &def_blk_fsop; + i->rdev = dev; + i->i_mode |= S_IFBLK; + break; + case S_IFIFO: + i->fsop = &pipefs_fsop; + i->i_mode |= S_IFIFO; + /* it's a union so we need to clear pipefs_i */ + memset_b(&i->u.pipefs, NULL, sizeof(struct pipefs_inode)); + break; + } + + dir->i_mtime = CURRENT_TIME; + dir->i_ctime = CURRENT_TIME; + dir->dirty = 1; + + bwrite(buf); + iput(i); + inode_unlock(dir); + return 0; +} + +int minix_create(struct inode *dir, char *name, __mode_t mode, struct inode **i_res) +{ + struct buffer *buf; + struct inode *i; + struct minix_dir_entry *d; + int n; + + if(IS_RDONLY_FS(dir)) { + return -EROFS; + } + + inode_lock(dir); + + if(!(i = ialloc(dir->sb))) { + inode_unlock(dir); + return -ENOSPC; + } + + if(!(buf = add_dir_entry(dir, &d))) { + i->i_nlink = 0; + iput(i); + inode_unlock(dir); + return -ENOSPC; + } + + d->inode = i->inode; + for(n = 0; n < i->sb->u.minix.namelen; n++) { + d->name[n] = name[n]; + if(!name[n]) { + break; + } + } + for(; n < i->sb->u.minix.namelen; n++) { + d->name[n] = 0; + } + + i->i_mode = (mode & ~current->umask) & ~S_IFMT; + i->i_mode |= S_IFREG; + i->i_uid = current->euid; + i->i_gid = current->egid; + i->i_nlink = 1; + i->dev = dir->dev; + i->fsop = &minix_file_fsop; + i->count = 1; + i->dirty = 1; + + dir->i_mtime = CURRENT_TIME; + dir->i_ctime = CURRENT_TIME; + dir->dirty = 1; + + *i_res = i; + bwrite(buf); + inode_unlock(dir); + return 0; +} + +int minix_rename(struct inode *i_old, struct inode *dir_old, struct inode *i_new, struct inode *dir_new, char *oldpath, char *newpath) +{ + struct buffer *buf_old, *buf_new; + struct minix_dir_entry *d_old, *d_new; + int errno; + + errno = 0; + buf_new = NULL; + + if(is_prefix(dir_new, i_old)) { + return -EINVAL; + } + + inode_lock(i_old); + inode_lock(dir_old); + if(dir_old != dir_new) { + inode_lock(dir_new); + } + + if(!(buf_old = find_dir_entry(dir_old, i_old, &d_old, oldpath))) { + errno = -ENOENT; + goto end; + } + if(dir_old == dir_new) { + buf_old->locked = 0; + } + + if(i_new) { + if(S_ISDIR(i_old->i_mode)) { + if(!is_dir_empty(i_new)) { + brelse(buf_old); + errno = -ENOTEMPTY; + goto end; + } + } + if(!(buf_new = find_dir_entry(dir_new, i_new, &d_new, newpath))) { + brelse(buf_old); + errno = -ENOENT; + goto end; + } + } else { + if(!(buf_new = add_dir_entry(dir_new, &d_new))) { + brelse(buf_old); + errno = -ENOSPC; + goto end; + } + } + if(i_new) { + i_new->i_nlink--; + } else { + i_new = i_old; + strcpy(d_new->name, newpath); + } + + d_old->inode = 0; + d_new->inode = i_old->inode; + dir_new->i_mtime = CURRENT_TIME; + dir_new->i_ctime = CURRENT_TIME; + i_new->dirty = 1; + dir_new->dirty = 1; + + dir_old->i_mtime = CURRENT_TIME; + dir_old->i_ctime = CURRENT_TIME; + i_old->dirty = 1; + dir_old->dirty = 1; + + bwrite(buf_old); + if(buf_new) { + bwrite(buf_new); + } + +end: + inode_unlock(i_old); + inode_unlock(dir_old); + inode_unlock(dir_new); + return errno; +} diff --git a/fs/minix/super.c b/fs/minix/super.c new file mode 100644 index 00000000..783aca76 --- /dev/null +++ b/fs/minix/super.c @@ -0,0 +1,267 @@ +/* + * fiwix/fs/minix/super.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct fs_operations minix_fsop = { + FSOP_REQUIRES_DEV, + NULL, + + NULL, /* open */ + NULL, /* close */ + NULL, /* read */ + NULL, /* write */ + NULL, /* ioctl */ + NULL, /* lseek */ + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lookup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + minix_read_inode, + minix_write_inode, + minix_ialloc, + minix_ifree, + minix_statfs, + minix_read_superblock, + minix_remount_fs, + minix_write_superblock, + minix_release_superblock +}; + +static void check_superblock(struct minix_super_block *sb) +{ + if(!(sb->s_state & MINIX_VALID_FS)) { + printk("WARNING: filesystem not checked, fsck recommended.\n"); + } + if(sb->s_state & MINIX_ERROR_FS) { + printk("WARNING: filesystem contains errors, fsck recommended.\n"); + } +} + +void minix_statfs(struct superblock *sb, struct statfs *statfsbuf) +{ + statfsbuf->f_type = sb->u.minix.sb.s_magic; + statfsbuf->f_bsize = sb->s_blocksize; + statfsbuf->f_blocks = sb->u.minix.nzones << sb->u.minix.sb.s_log_zone_size; + statfsbuf->f_bfree = sb->u.minix.nzones - minix_count_free_blocks(sb); + statfsbuf->f_bavail = statfsbuf->f_bfree; + + statfsbuf->f_files = sb->u.minix.sb.s_ninodes; + statfsbuf->f_ffree = sb->u.minix.sb.s_ninodes - minix_count_free_inodes(sb); + /* statfsbuf->f_fsid = ? */ + statfsbuf->f_namelen = sb->u.minix.namelen; +} + +int minix_read_superblock(__dev_t dev, struct superblock *sb) +{ + struct buffer *buf; + int maps; + + superblock_lock(sb); + if(!(buf = bread(dev, SUPERBLOCK, BLKSIZE_1K))) { + printk("WARNING: %s(): I/O error on device %d,%d.\n", __FUNCTION__, MAJOR(dev), MINOR(dev)); + superblock_unlock(sb); + return -EIO; + } + memcpy_b(&sb->u.minix.sb, buf->data, sizeof(struct minix_super_block)); + + switch(sb->u.minix.sb.s_magic) { + case MINIX_SUPER_MAGIC: + sb->u.minix.namelen = 14; + sb->u.minix.dirsize = sizeof(__u16) + sb->u.minix.namelen; + sb->u.minix.version = 1; + sb->u.minix.nzones = sb->u.minix.sb.s_nzones; + printk("minix v1 (14 char names) filesystem detected on device %d,%d.\n", MAJOR(dev), MINOR(dev)); + break; + case MINIX_SUPER_MAGIC2: + sb->u.minix.namelen = 30; + sb->u.minix.dirsize = sizeof(__u16) + sb->u.minix.namelen; + sb->u.minix.version = 1; + sb->u.minix.nzones = sb->u.minix.sb.s_nzones; + printk("minix v1 (30 char names) filesystem detected on device %d,%d.\n", MAJOR(dev), MINOR(dev)); + break; + case MINIX2_SUPER_MAGIC: + sb->u.minix.namelen = 14; + sb->u.minix.dirsize = sizeof(__u16) + sb->u.minix.namelen; + sb->u.minix.version = 2; + sb->u.minix.nzones = sb->u.minix.sb.s_zones; + printk("minix v2 (14 char names) filesystem detected on device %d,%d.\n", MAJOR(dev), MINOR(dev)); + break; + case MINIX2_SUPER_MAGIC2: + sb->u.minix.namelen = 30; + sb->u.minix.dirsize = sizeof(__u16) + sb->u.minix.namelen; + sb->u.minix.version = 2; + sb->u.minix.nzones = sb->u.minix.sb.s_zones; + printk("minix v2 (30 char names) filesystem detected on device %d,%d.\n", MAJOR(dev), MINOR(dev)); + break; + default: + printk("ERROR: %s(): invalid filesystem type or bad superblock on device %d,%d.\n", __FUNCTION__, MAJOR(dev), MINOR(dev)); + superblock_unlock(sb); + brelse(buf); + return -EINVAL; + } + + sb->dev = dev; + sb->fsop = &minix_fsop; + sb->s_blocksize = BLKSIZE_1K << sb->u.minix.sb.s_log_zone_size; + + if(sb->s_blocksize != BLKSIZE_1K) { + printk("ERROR: %s(): block sizes > %d not supported in this filesystem.\n", __FUNCTION__, BLKSIZE_1K); + superblock_unlock(sb); + brelse(buf); + return -EINVAL; + } + + /* + printk("s_ninodes = %d\n", sb->u.minix.sb.s_ninodes); + printk("s_nzones = %d (nzones = %d)\n", sb->u.minix.sb.s_nzones, sb->u.minix.nzones); + printk("s_imap_blocks = %d\n", sb->u.minix.sb.s_imap_blocks); + printk("s_zmap_blocks = %d\n", sb->u.minix.sb.s_zmap_blocks); + printk("s_firstdatazone = %d\n", sb->u.minix.sb.s_firstdatazone); + printk("s_log_zone_size = %d\n", sb->u.minix.sb.s_log_zone_size); + printk("s_max_size = %d\n", sb->u.minix.sb.s_max_size); + printk("s_magic = %x\n", sb->u.minix.sb.s_magic); + printk("s_state = %d\n", sb->u.minix.sb.s_state); + printk("s_zones = %d\n", sb->u.minix.sb.s_zones); + */ + + /* Minix fs size is limited to: # of bitmaps * 8192 * 1024 */ + if(sb->u.minix.version == 1) { + maps = V1_MAX_BITMAP_BLOCKS; /* 64MB limit */ + } + if(sb->u.minix.version == 2) { + maps = V2_MAX_BITMAP_BLOCKS; /* 1GB limit */ + } + + if(sb->u.minix.sb.s_imap_blocks > maps) { + printk("ERROR: %s(): number of imap blocks (%d) is greater than %d!\n", __FUNCTION__, sb->u.minix.sb.s_imap_blocks, maps); + superblock_unlock(sb); + brelse(buf); + return -EINVAL; + } + if(sb->u.minix.sb.s_zmap_blocks > maps) { + printk("ERROR: %s(): number of zmap blocks (%d) is greater than %d!\n", __FUNCTION__, sb->u.minix.sb.s_zmap_blocks, maps); + superblock_unlock(sb); + brelse(buf); + return -EINVAL; + } + + superblock_unlock(sb); + + if(!(sb->root = iget(sb, MINIX_ROOT_INO))) { + printk("ERROR: %s(): unable to get root inode.\n", __FUNCTION__); + brelse(buf); + return -EINVAL; + } + + check_superblock(&sb->u.minix.sb); + + if(!(sb->flags & MS_RDONLY)) { + sb->u.minix.sb.s_state &= ~MINIX_VALID_FS; + memcpy_b(buf->data, &sb->u.minix.sb, sizeof(struct minix_super_block)); + bwrite(buf); + } else { + brelse(buf); + } + + return 0; +} + +int minix_remount_fs(struct superblock *sb, int flags) +{ + struct buffer *buf; + struct minix_super_block *ms; + + if((flags & MS_RDONLY) == (sb->flags & MS_RDONLY)) { + return 0; + } + + superblock_lock(sb); + if(!(buf = bread(sb->dev, SUPERBLOCK, BLKSIZE_1K))) { + superblock_unlock(sb); + return -EIO; + } + ms = (struct minix_super_block *)buf->data; + + if(flags & MS_RDONLY) { + /* switching from RW to RO */ + sb->u.minix.sb.s_state |= MINIX_VALID_FS; + ms->s_state |= MINIX_VALID_FS; + } else { + /* switching from RO to RW */ + check_superblock(ms); + sb->u.minix.sb.s_state &= ~MINIX_VALID_FS; + ms->s_state &= ~MINIX_VALID_FS; + } + + sb->dirty = 1; + superblock_unlock(sb); + bwrite(buf); + return 0; +} + +int minix_write_superblock(struct superblock *sb) +{ + struct buffer *buf; + + superblock_lock(sb); + if(!(buf = bread(sb->dev, SUPERBLOCK, BLKSIZE_1K))) { + superblock_unlock(sb); + return -EIO; + } + + memcpy_b(buf->data, &sb->u.minix.sb, sizeof(struct minix_super_block)); + sb->dirty = 0; + superblock_unlock(sb); + bwrite(buf); + return 0; +} + +void minix_release_superblock(struct superblock *sb) +{ + if(sb->flags & MS_RDONLY) { + return; + } + + superblock_lock(sb); + + sb->u.minix.sb.s_state |= MINIX_VALID_FS; + sb->dirty = 1; + + superblock_unlock(sb); +} + +int minix_init(void) +{ + return register_filesystem("minix", &minix_fsop); +} diff --git a/fs/minix/symlink.c b/fs/minix/symlink.c new file mode 100644 index 00000000..431c41ef --- /dev/null +++ b/fs/minix/symlink.c @@ -0,0 +1,136 @@ +/* + * fiwix/fs/minix/symlink.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct fs_operations minix_symlink_fsop = { + 0, + 0, + + NULL, /* open */ + NULL, /* close */ + NULL, /* read */ + NULL, /* write */ + NULL, /* ioctl */ + NULL, /* lseek */ + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + minix_readlink, + minix_followlink, + NULL, /* bmap */ + NULL, /* lookup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +int minix_readlink(struct inode *i, char *buffer, __size_t count) +{ + __u32 blksize; + struct buffer *buf; + + if(!S_ISLNK(i->i_mode)) { + printk("%s(): Oops, inode '%d' is not a symlink (!?).\n", __FUNCTION__, i->inode); + return 0; + } + + inode_lock(i); + blksize = i->sb->s_blocksize; + count = MIN(count, i->i_size); + if(!count) { + inode_unlock(i); + return 0; + } + count = MIN(count, blksize); + if(i->sb->u.minix.version == 1) { + if(!(buf = bread(i->dev, i->u.minix.u.i1_zone[0], blksize))) { + inode_unlock(i); + return -EIO; + } + } else { + if(!(buf = bread(i->dev, i->u.minix.u.i2_zone[0], blksize))) { + inode_unlock(i); + return -EIO; + } + } + memcpy_b(buffer, buf->data, count); + brelse(buf); + buffer[count] = NULL; + inode_unlock(i); + return count; +} + +int minix_followlink(struct inode *dir, struct inode *i, struct inode **i_res) +{ + struct buffer *buf; + char *name; + __ino_t errno; + + if(!i) { + return -ENOENT; + } + + if(!S_ISLNK(i->i_mode)) { + printk("%s(): Oops, inode '%d' is not a symlink (!?).\n", __FUNCTION__, i->inode); + return 0; + } + + if(current->loopcnt > MAX_SYMLINKS) { + printk("%s(): too many nested symbolic links!\n", __FUNCTION__); + return -ELOOP; + } + + inode_lock(i); + if(i->sb->u.minix.version == 1) { + if(!(buf = bread(i->dev, i->u.minix.u.i1_zone[0], i->sb->s_blocksize))) { + inode_unlock(i); + return -EIO; + } + } else { + if(!(buf = bread(i->dev, i->u.minix.u.i2_zone[0], i->sb->s_blocksize))) { + inode_unlock(i); + return -EIO; + } + } + name = buf->data; + inode_unlock(i); + + current->loopcnt++; + iput(i); + brelse(buf); + errno = parse_namei(name, dir, i_res, NULL, FOLLOW_LINKS); + current->loopcnt--; + return errno; +} diff --git a/fs/minix/v1_inode.c b/fs/minix/v1_inode.c new file mode 100644 index 00000000..e65c2b52 --- /dev/null +++ b/fs/minix/v1_inode.c @@ -0,0 +1,407 @@ +/* + * fiwix/fs/minix/v1_inode.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BLOCKS_PER_IND_BLOCK(sb) (sb->s_blocksize / sizeof(__u16)) +#define MINIX_INODES_PER_BLOCK(sb) (sb->s_blocksize / sizeof(struct minix_inode)) + +#define MINIX_NDIR_BLOCKS 7 +#define MINIX_IND_BLOCK MINIX_NDIR_BLOCKS +#define MINIX_DIND_BLOCK (MINIX_NDIR_BLOCKS + 1) + +static void free_zone(struct inode *i, int block, int offset) +{ + int n; + struct buffer *buf; + __u16 *zone; + + if(!(buf = bread(i->dev, block, i->sb->s_blocksize))) { + printk("WARNING: %s(): error reading block %d.\n", __FUNCTION__, block); + return; + } + zone = (__u16 *)buf->data; + for(n = offset; n < BLOCKS_PER_IND_BLOCK(i->sb); n++) { + if(zone[n]) { + minix_bfree(i->sb, zone[n]); + zone[n] = 0; + } + } + bwrite(buf); +} + +int v1_minix_read_inode(struct inode *i) +{ + __ino_t block; + short int offset; + struct minix_inode *ii; + struct buffer *buf; + int errno; + + block = 1 + SUPERBLOCK + i->sb->u.minix.sb.s_imap_blocks + i->sb->u.minix.sb.s_zmap_blocks + (i->inode - 1) / MINIX_INODES_PER_BLOCK(i->sb); + + if(!(buf = bread(i->dev, block, i->sb->s_blocksize))) { + return -EIO; + } + offset = (i->inode - 1) % MINIX_INODES_PER_BLOCK(i->sb); + ii = ((struct minix_inode *)buf->data) + offset; + + i->i_mode = ii->i_mode; + i->i_uid = ii->i_uid; + i->i_size = ii->i_size; + i->i_atime = ii->i_time; + i->i_ctime = ii->i_time; + i->i_mtime = ii->i_time; + i->i_gid = ii->i_gid; + i->i_nlink = ii->i_nlinks; + memcpy_b(i->u.minix.u.i1_zone, ii->i_zone, sizeof(ii->i_zone)); + i->count = 1; + + errno = 0; + switch(i->i_mode & S_IFMT) { + case S_IFCHR: + i->fsop = &def_chr_fsop; + i->rdev = ii->i_zone[0]; + break; + case S_IFBLK: + i->fsop = &def_blk_fsop; + i->rdev = ii->i_zone[0]; + break; + case S_IFIFO: + i->fsop = &pipefs_fsop; + /* it's a union so we need to clear pipefs_i */ + memset_b(&i->u.pipefs, NULL, sizeof(struct pipefs_inode)); + break; + case S_IFDIR: + i->fsop = &minix_dir_fsop; + break; + case S_IFREG: + i->fsop = &minix_file_fsop; + break; + case S_IFLNK: + i->fsop = &minix_symlink_fsop; + break; + case S_IFSOCK: + i->fsop = NULL; + break; + default: + printk("WARNING: %s(): invalid inode (%d) mode %o.\n", __FUNCTION__, i->inode, i->i_mode); + errno = -ENOENT; + break; + } + + brelse(buf); + return errno; +} + +int v1_minix_write_inode(struct inode *i) +{ + __ino_t block; + short int offset; + struct minix_inode *ii; + struct buffer *buf; + + block = 1 + SUPERBLOCK + i->sb->u.minix.sb.s_imap_blocks + i->sb->u.minix.sb.s_zmap_blocks + (i->inode - 1) / MINIX_INODES_PER_BLOCK(i->sb); + + if(!(buf = bread(i->dev, block, i->sb->s_blocksize))) { + return -EIO; + } + offset = (i->inode - 1) % MINIX_INODES_PER_BLOCK(i->sb); + ii = ((struct minix_inode *)buf->data) + offset; + + ii->i_mode = i->i_mode; + ii->i_uid = i->i_uid; + ii->i_size = i->i_size; + ii->i_time = i->i_mtime; + ii->i_gid = i->i_gid; + ii->i_nlinks = i->i_nlink; + if(S_ISCHR(i->i_mode) || S_ISBLK(i->i_mode)) { + ii->i_zone[0] = i->rdev; + } else { + memcpy_b(ii->i_zone, i->u.minix.u.i1_zone, sizeof(i->u.minix.u.i1_zone)); + } + i->dirty = 0; + bwrite(buf); + return 0; +} + +int v1_minix_ialloc(struct inode *i) +{ + __blk_t offset; + int inode, errno; + struct superblock *sb; + + sb = i->sb; + superblock_lock(sb); + + offset = 1 + SUPERBLOCK; + + if(!(inode = minix_find_first_zero(sb, offset, sb->u.minix.sb.s_ninodes, offset + sb->u.minix.sb.s_imap_blocks))) { + superblock_unlock(sb); + return -ENOSPC; + } + + errno = minix_change_bit(SET_BIT, sb, offset, inode); + + if(errno) { + if(errno < 0) { + printk("WARNING: %s(): unable to set inode %d.\n", __FUNCTION__, i->inode); + } else { + printk("WARNING: %s(): inode %d is already marked as used!\n", __FUNCTION__, i->inode); + } + } + + i->inode = inode; + i->i_atime = CURRENT_TIME; + i->i_mtime = CURRENT_TIME; + i->i_ctime = CURRENT_TIME; + superblock_unlock(sb); + return 0; +} + +void v1_minix_ifree(struct inode *i) +{ + int errno; + struct superblock *sb; + + minix_truncate(i, 0); + + sb = i->sb; + superblock_lock(sb); + + errno = minix_change_bit(CLEAR_BIT, i->sb, 1 + SUPERBLOCK, i->inode); + + if(errno) { + if(errno < 0) { + printk("WARNING: %s(): unable to clear inode %d.\n", __FUNCTION__, i->inode); + } else { + printk("WARNING: %s(): inode %d is already marked as free!\n", __FUNCTION__, i->inode); + } + } + + i->i_size = 0; + i->i_mtime = CURRENT_TIME; + i->i_ctime = CURRENT_TIME; + i->dirty = 1; + superblock_unlock(sb); +} + +int v1_minix_bmap(struct inode *i, __off_t offset, int mode) +{ + unsigned char level; + __u16 *indblock, *dindblock; + __blk_t block, iblock, dblock, newblock; + int blksize; + struct buffer *buf, *buf2, *buf3; + + blksize = i->sb->s_blocksize; + block = offset / blksize; + level = 0; + + if(block < MINIX_NDIR_BLOCKS) { + level = MINIX_NDIR_BLOCKS - 1; + } else { + if(block < (BLOCKS_PER_IND_BLOCK(i->sb) + MINIX_NDIR_BLOCKS)) { + level = MINIX_IND_BLOCK; + } else { + level = MINIX_DIND_BLOCK; + } + block -= MINIX_NDIR_BLOCKS; + } + + if(level < MINIX_NDIR_BLOCKS) { + if(!i->u.minix.u.i1_zone[block] && mode == FOR_WRITING) { + if((newblock = minix_balloc(i->sb)) < 0) { + return -ENOSPC; + } + /* initialize the new block */ + if(!(buf = bread(i->dev, newblock, blksize))) { + minix_bfree(i->sb, newblock); + return -EIO; + } + memset_b(buf->data, 0, blksize); + bwrite(buf); + i->u.minix.u.i1_zone[block] = newblock; + } + return i->u.minix.u.i1_zone[block]; + } + + if(!i->u.minix.u.i1_zone[level]) { + if(mode == FOR_WRITING) { + if((newblock = minix_balloc(i->sb)) < 0) { + return -ENOSPC; + } + /* initialize the new block */ + if(!(buf = bread(i->dev, newblock, blksize))) { + minix_bfree(i->sb, newblock); + return -EIO; + } + memset_b(buf->data, 0, blksize); + bwrite(buf); + i->u.minix.u.i1_zone[level] = newblock; + } else { + return 0; + } + } + if(!(buf = bread(i->dev, i->u.minix.u.i1_zone[level], blksize))) { + return -EIO; + } + indblock = (__u16 *)buf->data; + dblock = block - BLOCKS_PER_IND_BLOCK(i->sb); + + if(level == MINIX_DIND_BLOCK) { + block = dblock / BLOCKS_PER_IND_BLOCK(i->sb); + } + + if(!indblock[block]) { + if(mode == FOR_WRITING) { + if((newblock = minix_balloc(i->sb)) < 0) { + brelse(buf); + return -ENOSPC; + } + /* initialize the new block */ + if(!(buf2 = bread(i->dev, newblock, blksize))) { + minix_bfree(i->sb, newblock); + brelse(buf); + return -EIO; + } + memset_b(buf2->data, 0, blksize); + bwrite(buf2); + indblock[block] = newblock; + if(level == MINIX_IND_BLOCK) { + bwrite(buf); + return newblock; + } + buf->dirty = 1; + buf->valid = 1; + } else { + brelse(buf); + return 0; + } + } + if(level == MINIX_IND_BLOCK) { + newblock = indblock[block]; + brelse(buf); + return newblock; + } + + iblock = block; + if(!(buf2 = bread(i->dev, indblock[iblock], blksize))) { + printk("%s(): returning -EIO\n", __FUNCTION__); + brelse(buf); + return -EIO; + } + dindblock = (__u16 *)buf2->data; + block = dindblock[dblock - (iblock * BLOCKS_PER_IND_BLOCK(i->sb))]; + if(!block && mode == FOR_WRITING) { + if((newblock = minix_balloc(i->sb)) < 0) { + brelse(buf); + brelse(buf2); + return -ENOSPC; + } + /* initialize the new block */ + if(!(buf3 = bread(i->dev, newblock, blksize))) { + minix_bfree(i->sb, newblock); + brelse(buf); + brelse(buf2); + return -EIO; + } + memset_b(buf3->data, 0, blksize); + bwrite(buf3); + dindblock[dblock - (iblock * BLOCKS_PER_IND_BLOCK(i->sb))] = newblock; + buf2->dirty = 1; + buf2->valid = 1; + block = newblock; + } + brelse(buf); + brelse(buf2); + return block; +} + +int v1_minix_truncate(struct inode *i, __off_t length) +{ + int n; + __blk_t block, dblock; + __u16 *zone; + struct buffer *buf; + + block = length / i->sb->s_blocksize; + + if(!S_ISDIR(i->i_mode) && !S_ISREG(i->i_mode) && !S_ISLNK(i->i_mode)) { + return -EINVAL; + } + + if(block < MINIX_NDIR_BLOCKS) { + for(n = block; n < MINIX_NDIR_BLOCKS; n++) { + if(i->u.minix.u.i1_zone[n]) { + minix_bfree(i->sb, i->u.minix.u.i1_zone[n]); + i->u.minix.u.i1_zone[n] = 0; + } + } + block = 0; + } + + if(!block || block < (BLOCKS_PER_IND_BLOCK(i->sb) + MINIX_NDIR_BLOCKS)) { + if(block) { + block -= MINIX_NDIR_BLOCKS; + } + if(i->u.minix.u.i1_zone[MINIX_IND_BLOCK]) { + free_zone(i, i->u.minix.u.i1_zone[MINIX_IND_BLOCK], block); + if(!block) { + minix_bfree(i->sb, i->u.minix.u.i1_zone[MINIX_IND_BLOCK]); + i->u.minix.u.i1_zone[MINIX_IND_BLOCK] = 0; + } + } + block = 0; + } + + if(block) { + block -= MINIX_NDIR_BLOCKS; + block -= BLOCKS_PER_IND_BLOCK(i->sb); + } + if(i->u.minix.u.i1_zone[MINIX_DIND_BLOCK]) { + if(!(buf = bread(i->dev, i->u.minix.u.i1_zone[MINIX_DIND_BLOCK], i->sb->s_blocksize))) { + printk("%s(): error reading block %d.\n", __FUNCTION__, i->u.minix.u.i1_zone[MINIX_DIND_BLOCK]); + } + zone = (__u16 *)buf->data; + dblock = block % BLOCKS_PER_IND_BLOCK(i->sb); + for(n = block / BLOCKS_PER_IND_BLOCK(i->sb); n < BLOCKS_PER_IND_BLOCK(i->sb); n++) { + if(zone[n]) { + free_zone(i, zone[n], dblock); + if(!dblock) { + minix_bfree(i->sb, zone[n]); + } + } + dblock = 0; + } + bwrite(buf); + if(!block) { + minix_bfree(i->sb, i->u.minix.u.i1_zone[MINIX_DIND_BLOCK]); + i->u.minix.u.i1_zone[MINIX_DIND_BLOCK] = 0; + } + } + + i->i_mtime = CURRENT_TIME; + i->i_ctime = CURRENT_TIME; + i->i_size = length; + i->dirty = 1; + + return 0; +} diff --git a/fs/minix/v2_inode.c b/fs/minix/v2_inode.c new file mode 100644 index 00000000..6a50c013 --- /dev/null +++ b/fs/minix/v2_inode.c @@ -0,0 +1,471 @@ +/* + * fiwix/fs/minix/v2_inode.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BLOCKS_PER_IND_BLOCK(sb) (sb->s_blocksize / sizeof(__u32)) +#define MINIX2_INODES_PER_BLOCK(sb) (sb->s_blocksize / sizeof(struct minix2_inode)) + +#define MINIX_NDIR_BLOCKS 7 +#define MINIX_IND_BLOCK MINIX_NDIR_BLOCKS +#define MINIX_DIND_BLOCK (MINIX_NDIR_BLOCKS + 1) +#define MINIX_TIND_BLOCK (MINIX_NDIR_BLOCKS + 2) + +static void free_zone(struct inode *i, int block, int offset) +{ + int n; + struct buffer *buf; + __u32 *zone; + + if(!(buf = bread(i->dev, block, i->sb->s_blocksize))) { + printk("WARNING: %s(): error reading block %d.\n", __FUNCTION__, block); + return; + } + zone = (__u32 *)buf->data; + for(n = offset; n < BLOCKS_PER_IND_BLOCK(i->sb); n++) { + if(zone[n]) { + minix_bfree(i->sb, zone[n]); + zone[n] = 0; + } + } + bwrite(buf); +} + +int v2_minix_read_inode(struct inode *i) +{ + __ino_t block; + short int offset; + struct minix2_inode *ii; + struct buffer *buf; + int errno; + + block = 1 + SUPERBLOCK + i->sb->u.minix.sb.s_imap_blocks + i->sb->u.minix.sb.s_zmap_blocks + (i->inode - 1) / MINIX2_INODES_PER_BLOCK(i->sb); + + if(!(buf = bread(i->dev, block, i->sb->s_blocksize))) { + return -EIO; + } + offset = (i->inode - 1) % MINIX2_INODES_PER_BLOCK(i->sb); + ii = ((struct minix2_inode *)buf->data) + offset; + + i->i_mode = ii->i_mode; + i->i_nlink = ii->i_nlink; + i->i_uid = ii->i_uid; + i->i_gid = ii->i_gid; + i->i_size = ii->i_size; + i->i_atime = ii->i_atime; + i->i_mtime = ii->i_mtime; + i->i_ctime = ii->i_ctime; + memcpy_b(i->u.minix.u.i2_zone, ii->i_zone, sizeof(ii->i_zone)); + i->count = 1; + + errno = 0; + switch(i->i_mode & S_IFMT) { + case S_IFCHR: + i->fsop = &def_chr_fsop; + i->rdev = ii->i_zone[0]; + break; + case S_IFBLK: + i->fsop = &def_blk_fsop; + i->rdev = ii->i_zone[0]; + break; + case S_IFIFO: + i->fsop = &pipefs_fsop; + /* it's a union so we need to clear pipefs_i */ + memset_b(&i->u.pipefs, NULL, sizeof(struct pipefs_inode)); + break; + case S_IFDIR: + i->fsop = &minix_dir_fsop; + break; + case S_IFREG: + i->fsop = &minix_file_fsop; + break; + case S_IFLNK: + i->fsop = &minix_symlink_fsop; + break; + case S_IFSOCK: + i->fsop = NULL; + break; + default: + printk("WARNING: %s(): invalid inode (%d) mode %o.\n", __FUNCTION__, i->inode, i->i_mode); + errno = -ENOENT; + break; + } + + brelse(buf); + return errno; +} + +int v2_minix_write_inode(struct inode *i) +{ + __ino_t block; + short int offset; + struct minix2_inode *ii; + struct buffer *buf; + + block = 1 + SUPERBLOCK + i->sb->u.minix.sb.s_imap_blocks + i->sb->u.minix.sb.s_zmap_blocks + (i->inode - 1) / MINIX2_INODES_PER_BLOCK(i->sb); + + if(!(buf = bread(i->dev, block, i->sb->s_blocksize))) { + return -EIO; + } + offset = (i->inode - 1) % MINIX2_INODES_PER_BLOCK(i->sb); + ii = ((struct minix2_inode *)buf->data) + offset; + + ii->i_mode = i->i_mode; + ii->i_nlink = i->i_nlink; + ii->i_uid = i->i_uid; + ii->i_gid = i->i_gid; + ii->i_size = i->i_size; + ii->i_atime = i->i_atime; + ii->i_mtime = i->i_mtime; + ii->i_ctime = i->i_ctime; + if(S_ISCHR(i->i_mode) || S_ISBLK(i->i_mode)) { + ii->i_zone[0] = i->rdev; + } else { + memcpy_b(ii->i_zone, i->u.minix.u.i2_zone, sizeof(i->u.minix.u.i2_zone)); + } + i->dirty = 0; + bwrite(buf); + return 0; +} + +int v2_minix_ialloc(struct inode *i) +{ + __blk_t offset; + int inode, errno; + struct superblock *sb; + + sb = i->sb; + superblock_lock(sb); + + offset = 1 + SUPERBLOCK; + + if(!(inode = minix_find_first_zero(sb, offset, sb->u.minix.sb.s_ninodes, offset + sb->u.minix.sb.s_imap_blocks))) { + superblock_unlock(sb); + return -ENOSPC; + } + + errno = minix_change_bit(SET_BIT, sb, offset, inode); + + if(errno) { + if(errno < 0) { + printk("WARNING: %s(): unable to set inode %d.\n", __FUNCTION__, i->inode); + } else { + printk("WARNING: %s(): inode %d is already marked as used!\n", __FUNCTION__, i->inode); + } + } + + i->inode = inode; + i->i_atime = CURRENT_TIME; + i->i_mtime = CURRENT_TIME; + i->i_ctime = CURRENT_TIME; + superblock_unlock(sb); + return 0; +} + +void v2_minix_ifree(struct inode *i) +{ + int errno; + struct superblock *sb; + + minix_truncate(i, 0); + + sb = i->sb; + superblock_lock(sb); + + errno = minix_change_bit(CLEAR_BIT, i->sb, 1 + SUPERBLOCK, i->inode); + + if(errno) { + if(errno < 0) { + printk("WARNING: %s(): unable to clear inode %d.\n", __FUNCTION__, i->inode); + } else { + printk("WARNING: %s(): inode %d is already marked as free!\n", __FUNCTION__, i->inode); + } + } + + i->i_size = 0; + i->i_mtime = CURRENT_TIME; + i->i_ctime = CURRENT_TIME; + i->dirty = 1; + superblock_unlock(sb); +} + +int v2_minix_bmap(struct inode *i, __off_t offset, int mode) +{ + unsigned char level; + __u32 *indblock, *dindblock, *tindblock; + __blk_t block, iblock, dblock, tblock, newblock; + int blksize; + struct buffer *buf, *buf2, *buf3, *buf4; + + blksize = i->sb->s_blocksize; + block = offset / blksize; + level = 0; + buf3 = NULL; /* makes GCC happy */ + + if(block < MINIX_NDIR_BLOCKS) { + level = MINIX_NDIR_BLOCKS - 1; + } else { + if(block < (BLOCKS_PER_IND_BLOCK(i->sb) + MINIX_NDIR_BLOCKS)) { + level = MINIX_IND_BLOCK; + } else if(block < ((BLOCKS_PER_IND_BLOCK(i->sb) * BLOCKS_PER_IND_BLOCK(i->sb)) + BLOCKS_PER_IND_BLOCK(i->sb) + MINIX_NDIR_BLOCKS)) { + level = MINIX_DIND_BLOCK; + } else { + level = MINIX_TIND_BLOCK; + } + block -= MINIX_NDIR_BLOCKS; + } + + if(level < MINIX_NDIR_BLOCKS) { + if(!i->u.minix.u.i2_zone[block] && mode == FOR_WRITING) { + if((newblock = minix_balloc(i->sb)) < 0) { + return -ENOSPC; + } + /* initialize the new block */ + if(!(buf = bread(i->dev, newblock, blksize))) { + minix_bfree(i->sb, newblock); + return -EIO; + } + memset_b(buf->data, 0, blksize); + bwrite(buf); + i->u.minix.u.i2_zone[block] = newblock; + } + return i->u.minix.u.i2_zone[block]; + } + + if(!i->u.minix.u.i2_zone[level]) { + if(mode == FOR_WRITING) { + if((newblock = minix_balloc(i->sb)) < 0) { + return -ENOSPC; + } + /* initialize the new block */ + if(!(buf = bread(i->dev, newblock, blksize))) { + minix_bfree(i->sb, newblock); + return -EIO; + } + memset_b(buf->data, 0, blksize); + bwrite(buf); + i->u.minix.u.i2_zone[level] = newblock; + } else { + return 0; + } + } + if(!(buf = bread(i->dev, i->u.minix.u.i2_zone[level], blksize))) { + return -EIO; + } + indblock = (__u32 *)buf->data; + dblock = block - BLOCKS_PER_IND_BLOCK(i->sb); + tblock = block - (BLOCKS_PER_IND_BLOCK(i->sb) * BLOCKS_PER_IND_BLOCK(i->sb)) - BLOCKS_PER_IND_BLOCK(i->sb); + + if(level == MINIX_DIND_BLOCK) { + block = dblock / BLOCKS_PER_IND_BLOCK(i->sb); + } + if(level == MINIX_TIND_BLOCK) { + block = tblock / (BLOCKS_PER_IND_BLOCK(i->sb) * BLOCKS_PER_IND_BLOCK(i->sb)); + } + + if(!indblock[block]) { + if(mode == FOR_WRITING) { + if((newblock = minix_balloc(i->sb)) < 0) { + brelse(buf); + return -ENOSPC; + } + /* initialize the new block */ + if(!(buf2 = bread(i->dev, newblock, blksize))) { + minix_bfree(i->sb, newblock); + brelse(buf); + return -EIO; + } + memset_b(buf2->data, 0, blksize); + bwrite(buf2); + indblock[block] = newblock; + if(level == MINIX_IND_BLOCK) { + bwrite(buf); + return newblock; + } + buf->dirty = 1; + buf->valid = 1; + } else { + brelse(buf); + return 0; + } + } + if(level == MINIX_IND_BLOCK) { + newblock = indblock[block]; + brelse(buf); + return newblock; + } + + if(level == MINIX_TIND_BLOCK) { + if(!(buf3 = bread(i->dev, indblock[block], blksize))) { + printk("%s(): returning -EIO\n", __FUNCTION__); + brelse(buf); + return -EIO; + } + tindblock = (__u32 *)buf3->data; + block = tindblock[tblock / BLOCKS_PER_IND_BLOCK(i->sb)]; + if(!block) { + if(mode == FOR_WRITING) { + if((newblock = minix_balloc(i->sb)) < 0) { + brelse(buf); + brelse(buf3); + return -ENOSPC; + } + /* initialize the new block */ + if(!(buf4 = bread(i->dev, newblock, blksize))) { + minix_bfree(i->sb, newblock); + brelse(buf); + brelse(buf3); + return -EIO; + } + memset_b(buf4->data, 0, blksize); + bwrite(buf4); + tindblock[tblock / BLOCKS_PER_IND_BLOCK(i->sb)] = newblock; + buf3->dirty = 1; + buf3->valid = 1; + block = newblock; + } else { + brelse(buf); + brelse(buf3); + return 0; + } + } + dblock = tblock; + iblock = tblock / BLOCKS_PER_IND_BLOCK(i->sb); + if(!(buf2 = bread(i->dev, block, blksize))) { + printk("%s(): returning -EIO\n", __FUNCTION__); + brelse(buf); + brelse(buf3); + return -EIO; + } + } else { + iblock = block; + if(!(buf2 = bread(i->dev, indblock[iblock], blksize))) { + printk("%s(): returning -EIO\n", __FUNCTION__); + brelse(buf); + return -EIO; + } + } + + dindblock = (__u32 *)buf2->data; + block = dindblock[dblock - (iblock * BLOCKS_PER_IND_BLOCK(i->sb))]; + if(!block && mode == FOR_WRITING) { + if((newblock = minix_balloc(i->sb)) < 0) { + brelse(buf); + if(level == MINIX_TIND_BLOCK) { + brelse(buf3); + } + brelse(buf2); + return -ENOSPC; + } + /* initialize the new block */ + if(!(buf4 = bread(i->dev, newblock, blksize))) { + minix_bfree(i->sb, newblock); + brelse(buf); + if(level == MINIX_TIND_BLOCK) { + brelse(buf3); + } + brelse(buf2); + return -EIO; + } + memset_b(buf4->data, 0, blksize); + bwrite(buf4); + dindblock[dblock - (iblock * BLOCKS_PER_IND_BLOCK(i->sb))] = newblock; + buf2->dirty = 1; + buf2->valid = 1; + block = newblock; + } + brelse(buf); + if(level == MINIX_TIND_BLOCK) { + brelse(buf3); + } + brelse(buf2); + return block; +} + +int v2_minix_truncate(struct inode *i, __off_t length) +{ + int n; + __blk_t block, dblock; + __u32 *zone; + struct buffer *buf; + + block = length / i->sb->s_blocksize; + + if(!S_ISDIR(i->i_mode) && !S_ISREG(i->i_mode) && !S_ISLNK(i->i_mode)) { + return -EINVAL; + } + + if(block < MINIX_NDIR_BLOCKS) { + for(n = block; n < MINIX_NDIR_BLOCKS; n++) { + if(i->u.minix.u.i2_zone[n]) { + minix_bfree(i->sb, i->u.minix.u.i2_zone[n]); + i->u.minix.u.i2_zone[n] = 0; + } + } + block = 0; + } + + if(!block || block < (BLOCKS_PER_IND_BLOCK(i->sb) + MINIX_NDIR_BLOCKS)) { + if(block) { + block -= MINIX_NDIR_BLOCKS; + } + if(i->u.minix.u.i2_zone[MINIX_IND_BLOCK]) { + free_zone(i, i->u.minix.u.i2_zone[MINIX_IND_BLOCK], block); + if(!block) { + minix_bfree(i->sb, i->u.minix.u.i2_zone[MINIX_IND_BLOCK]); + i->u.minix.u.i2_zone[MINIX_IND_BLOCK] = 0; + } + } + block = 0; + } + + if(block) { + block -= MINIX_NDIR_BLOCKS; + block -= BLOCKS_PER_IND_BLOCK(i->sb); + } + if(i->u.minix.u.i2_zone[MINIX_DIND_BLOCK]) { + if(!(buf = bread(i->dev, i->u.minix.u.i2_zone[MINIX_DIND_BLOCK], i->sb->s_blocksize))) { + printk("%s(): error reading block %d.\n", __FUNCTION__, i->u.minix.u.i2_zone[MINIX_DIND_BLOCK]); + } + zone = (__u32 *)buf->data; + dblock = block % BLOCKS_PER_IND_BLOCK(i->sb); + for(n = block / BLOCKS_PER_IND_BLOCK(i->sb); n < BLOCKS_PER_IND_BLOCK(i->sb); n++) { + if(zone[n]) { + free_zone(i, zone[n], dblock); + if(!dblock) { + minix_bfree(i->sb, zone[n]); + } + } + dblock = 0; + } + bwrite(buf); + if(!block) { + minix_bfree(i->sb, i->u.minix.u.i2_zone[MINIX_DIND_BLOCK]); + i->u.minix.u.i2_zone[MINIX_DIND_BLOCK] = 0; + } + } + + i->i_mtime = CURRENT_TIME; + i->i_ctime = CURRENT_TIME; + i->i_size = length; + i->dirty = 1; + + return 0; +} diff --git a/fs/namei.c b/fs/namei.c new file mode 100644 index 00000000..2ba2cdaa --- /dev/null +++ b/fs/namei.c @@ -0,0 +1,178 @@ +/* + * fiwix/fs/namei.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int namei_lookup(char *name, struct inode *dir, struct inode **i_res) +{ + if(dir->fsop && dir->fsop->lookup) { + return dir->fsop->lookup(name, dir, i_res); + } + return -EACCES; +} + +static int do_namei(char *path, struct inode *dir, struct inode **i_res, struct inode **d_res, int follow_links) +{ + char *name, *ptr_name; + struct inode *i; + struct superblock *sb; + + int errno; + + *i_res = dir; + for(;;) { + while(*path == '/') { + path++; + } + if(*path == NULL) { + return 0; + } + + /* extracts the next component of the path */ + if(!(name = (char *)kmalloc())) { + return -ENOMEM; + } + ptr_name = name; + while(*path != NULL && *path != '/') { + if(ptr_name > name + NAME_MAX - 1) { + break; + } + *ptr_name++ = *path++; + } + *ptr_name = NULL; + + /* + * If the inode is the root of a file system, then return the + * inode on which the file system was mounted. + */ + if(name[0] == '.' && name[1] == '.' && name[2] == NULL) { + if(dir == dir->sb->root) { + sb = dir->sb; + iput(dir); + dir = sb->dir; + dir->count++; + } + } + + if((errno = check_permission(TO_EXEC, dir))) { + break; + } + + if((errno = namei_lookup(name, dir, &i))) { + break; + } + + kfree((unsigned int)name); + if(*path == '/') { + if(!S_ISDIR(i->i_mode) && !S_ISLNK(i->i_mode)) { + iput(dir); + iput(i); + return -ENOTDIR; + } + if(S_ISLNK(i->i_mode)) { + if(i->fsop && i->fsop->followlink) { + if((errno = i->fsop->followlink(dir, i, &i))) { + iput(dir); + return errno; + } + } + } + } else { + if((i->fsop && i->fsop->followlink) && follow_links) { + if((errno = i->fsop->followlink(dir, i, &i))) { + iput(dir); + return errno; + } + } + } + + if(d_res) { + if(*d_res) { + iput(*d_res); + } + *d_res = dir; + } else { + iput(dir); + } + dir = i; + *i_res = i; + } + + kfree((unsigned int)name); + if(d_res) { + if(*d_res) { + iput(*d_res); + } + /* + * If that was the last component of the path, + * then return the directory. + */ + if(*path == NULL) { + *d_res = dir; + dir->count++; + } else { + /* that's an non-existent directory */ + *d_res = NULL; + errno = -ENOTDIR; + } + iput(dir); + *i_res = NULL; + } else { + iput(dir); + } + + return errno; +} + +int parse_namei(char *path, struct inode *base_dir, struct inode **i_res, struct inode **d_res, int follow_links) +{ + struct inode *dir; + int errno; + + if(!path) { + return -EFAULT; + } + if(*path == NULL) { + return -ENOENT; + } + + if(!(dir = base_dir)) { + dir = current->pwd; + } + + /* it is definitely an absolute path */ + if(path[0] == '/') { + dir = current->root; + } + dir->count++; + errno = do_namei(path, dir, i_res, d_res, follow_links); + return errno; +} + +/* + * namei() returns: + * i_res -> the inode of the last component of the path, or NULL. + * d_res -> the inode of the directory where i_res resides, or NULL. + */ +int namei(char *path, struct inode **i_res, struct inode **d_res, int follow_links) +{ + *i_res = NULL; + if(d_res) { + *d_res = NULL; + } + return parse_namei(path, NULL, i_res, d_res, follow_links); +} diff --git a/fs/pipefs/Makefile b/fs/pipefs/Makefile new file mode 100644 index 00000000..c0dd48ff --- /dev/null +++ b/fs/pipefs/Makefile @@ -0,0 +1,19 @@ +# fiwix/fs/pipefs/Makefile +# +# Copyright 2018, Jordi Sanfeliu. All rights reserved. +# Distributed under the terms of the Fiwix License. +# + +.S.o: + $(CC) -traditional -I$(INCLUDE) -c -o $@ $< +.c.o: + $(CC) $(CFLAGS) -c -o $@ $< + +OBJS = super.o fifo.o pipe.o + +pipefs: $(OBJS) + $(LD) $(LDFLAGS) -r $(OBJS) -o pipefs.o + +clean: + rm -f *.o + diff --git a/fs/pipefs/fifo.c b/fs/pipefs/fifo.c new file mode 100644 index 00000000..e5b7f103 --- /dev/null +++ b/fs/pipefs/fifo.c @@ -0,0 +1,73 @@ +/* + * fiwix/fs/pipefs/fifo.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int fifo_open(struct inode *i, struct fd *fd_table) +{ + /* first open */ + if(i->count == 1) { + if(!(i->u.pipefs.i_data = (void *)kmalloc())) { + return -ENOMEM; + } + i->u.pipefs.i_readoff = 0; + i->u.pipefs.i_writeoff = 0; + } + + if((fd_table->flags & O_ACCMODE) == O_RDONLY) { + i->u.pipefs.i_readers++; + wakeup(&pipefs_write); + if(!(fd_table->flags & O_NONBLOCK)) { + while(!i->u.pipefs.i_writers) { + if(sleep(&pipefs_read, PROC_INTERRUPTIBLE)) { + if(!--i->u.pipefs.i_readers) { + wakeup(&pipefs_write); + } + return -EINTR; + } + } + } + } + + if((fd_table->flags & O_ACCMODE) == O_WRONLY) { + if((fd_table->flags & O_NONBLOCK) && !i->u.pipefs.i_readers) { + return -ENXIO; + } + + i->u.pipefs.i_writers++; + wakeup(&pipefs_read); + if(!(fd_table->flags & O_NONBLOCK)) { + while(!i->u.pipefs.i_readers) { + if(sleep(&pipefs_write, PROC_INTERRUPTIBLE)) { + if(!--i->u.pipefs.i_writers) { + wakeup(&pipefs_read); + } + return -EINTR; + } + } + } + } + + if((fd_table->flags & O_ACCMODE) == O_RDWR) { + i->u.pipefs.i_readers++; + i->u.pipefs.i_writers++; + wakeup(&pipefs_write); + wakeup(&pipefs_read); + } + + return 0; +} diff --git a/fs/pipefs/pipe.c b/fs/pipefs/pipe.c new file mode 100644 index 00000000..295e5ee2 --- /dev/null +++ b/fs/pipefs/pipe.c @@ -0,0 +1,202 @@ +/* + * fiwix/fs/pipefs/pipe.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct resource pipe_resource = { NULL, NULL }; + +int pipefs_close(struct inode *i, struct fd *fd_table) +{ + if((fd_table->flags & O_ACCMODE) == O_RDONLY) { + if(!--i->u.pipefs.i_readers) { + wakeup(&pipefs_write); + } + } + if((fd_table->flags & O_ACCMODE) == O_WRONLY) { + if(!--i->u.pipefs.i_writers) { + wakeup(&pipefs_read); + } + } + if((fd_table->flags & O_ACCMODE) == O_RDWR) { + if(!--i->u.pipefs.i_readers) { + wakeup(&pipefs_write); + } + if(!--i->u.pipefs.i_writers) { + wakeup(&pipefs_read); + } + } + return 0; +} + +int pipefs_read(struct inode *i, struct fd *fd_table, char *buffer, __size_t count) +{ + __off_t bytes_read; + __size_t n, limit; + char *data; + + bytes_read = 0; + data = i->u.pipefs.i_data; + + while(count) { + if(i->u.pipefs.i_writeoff) { + if(i->u.pipefs.i_readoff >= i->u.pipefs.i_writeoff) { + limit = PIPE_BUF - i->u.pipefs.i_readoff; + } else { + limit = i->u.pipefs.i_writeoff - i->u.pipefs.i_readoff; + } + } else { + limit = PIPE_BUF - i->u.pipefs.i_readoff; + } + n = MIN(limit, count); + if(i->i_size && n) { + lock_resource(&pipe_resource); + memcpy_b(buffer + bytes_read, data + i->u.pipefs.i_readoff, n); + bytes_read += n; + i->u.pipefs.i_readoff += n; + i->i_size -= n; + if(i->u.pipefs.i_writeoff >= PIPE_BUF) { + i->u.pipefs.i_writeoff = 0; + } + unlock_resource(&pipe_resource); + wakeup(&pipefs_write); + break; + } else { + if(i->u.pipefs.i_writers) { + if(fd_table->flags & O_NONBLOCK) { + return -EAGAIN; + } + if(sleep(&pipefs_read, PROC_INTERRUPTIBLE)) { + return -EINTR; + } + } else { + if(i->i_size) { + if(i->u.pipefs.i_readoff >= PIPE_BUF) { + i->u.pipefs.i_readoff = 0; + continue; + } + } + break; + } + } + } + if(!i->i_size) { + i->u.pipefs.i_readoff = 0; + i->u.pipefs.i_writeoff = 0; + } + return bytes_read; +} + +int pipefs_write(struct inode *i, struct fd *fd_table, const char *buffer, __size_t count) +{ + __off_t bytes_written; + __size_t n; + char *data; + int limit; + + bytes_written = 0; + data = i->u.pipefs.i_data; + + while(bytes_written < count) { + /* if the read end closes then send signal and return */ + if(!i->u.pipefs.i_readers) { + send_sig(current, SIGPIPE); + return -EPIPE; + } + + if(i->u.pipefs.i_readoff) { + if(i->u.pipefs.i_writeoff <= i->u.pipefs.i_readoff) { + limit = i->u.pipefs.i_readoff; + } else { + limit = PIPE_BUF; + } + } else { + limit = PIPE_BUF; + } + + n = MIN((count - bytes_written), (limit - i->u.pipefs.i_writeoff)); + + /* + * POSIX requires that any write operation involving fewer than + * PIPE_BUF bytes must be automatically executed and finished + * without being interleaved with write operations of other + * processes to the same pipe. + */ + if(n && n <= PIPE_BUF) { + lock_resource(&pipe_resource); + memcpy_b(data + i->u.pipefs.i_writeoff, buffer + bytes_written, n); + bytes_written += n; + i->u.pipefs.i_writeoff += n; + i->i_size += n; + if(i->u.pipefs.i_readoff >= PIPE_BUF) { + i->u.pipefs.i_readoff = 0; + } + unlock_resource(&pipe_resource); + wakeup(&pipefs_read); + continue; + } + + wakeup(&pipefs_read); + if(!(fd_table->flags & O_NONBLOCK)) { + if(sleep(&pipefs_write, PROC_INTERRUPTIBLE)) { + return -EINTR; + } + } else { + return -EAGAIN; + } + } + return bytes_written; +} + +int pipefs_ioctl(struct inode *i, int cmd, unsigned long int arg) +{ + int errno; + + switch(cmd) { + case FIONREAD: + if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int)))) { + return errno; + } + memcpy_b((void *)arg, &i->i_size, sizeof(unsigned int)); + break; + default: + return -EINVAL; + } + return 0; +} + +int pipefs_lseek(struct inode *i, __off_t offset) +{ + return -ESPIPE; +} + +int pipefs_select(struct inode *i, int flag) +{ + switch(flag) { + case SEL_R: + if(i->i_size || !i->u.pipefs.i_writers) { + return 1; + } + break; + case SEL_W: + if(i->i_size < PIPE_BUF || !i->u.pipefs.i_readers) { + return 1; + } + break; + } + return 0; +} diff --git a/fs/pipefs/super.c b/fs/pipefs/super.c new file mode 100644 index 00000000..7a6d69e6 --- /dev/null +++ b/fs/pipefs/super.c @@ -0,0 +1,113 @@ +/* + * fiwix/fs/pipefs/super.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned int i_counter; + +struct fs_operations pipefs_fsop = { + FSOP_KERN_MOUNT, + PIPE_DEV, + + fifo_open, + pipefs_close, + pipefs_read, + pipefs_write, + pipefs_ioctl, + pipefs_lseek, + NULL, /* readdir */ + NULL, /* mmap */ + pipefs_select, + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lookup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + pipefs_ialloc, + pipefs_ifree, + NULL, /* statfs */ + pipefs_read_superblock, + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +int pipefs_read_superblock(__dev_t dev, struct superblock *sb) +{ + superblock_lock(sb); + sb->dev = dev; + sb->fsop = &pipefs_fsop; + sb->s_blocksize = BLKSIZE_1K; + i_counter = 0; + superblock_unlock(sb); + return 0; +} + +int pipefs_ialloc(struct inode *i) +{ + struct superblock *sb = i->sb; + + superblock_lock(sb); + i_counter++; + superblock_unlock(sb); + + i->i_mode = S_IFIFO; + i->dev = i->rdev = sb->dev; + i->fsop = &pipefs_fsop; + i->inode = i_counter; + i->count = 2; + if(!(i->u.pipefs.i_data = (void *)kmalloc())) { + return -ENOMEM; + } + i->u.pipefs.i_readoff = 0; + i->u.pipefs.i_writeoff = 0; + i->u.pipefs.i_readers = 1; + i->u.pipefs.i_writers = 1; + return 0; +} + +void pipefs_ifree(struct inode *i) +{ + if(!i->u.pipefs.i_readers && !i->u.pipefs.i_writers) { + /* + * We need to ask before to kfree() because this function is + * also called to free removed (with sys_unlink) fifo files. + */ + if(i->u.pipefs.i_data) { + kfree((unsigned int)i->u.pipefs.i_data); + } + } +} + +int pipefs_init(void) +{ + return register_filesystem("pipefs", &pipefs_fsop); +} diff --git a/fs/procfs/Makefile b/fs/procfs/Makefile new file mode 100644 index 00000000..4bc89cca --- /dev/null +++ b/fs/procfs/Makefile @@ -0,0 +1,19 @@ +# fiwix/fs/procfs/Makefile +# +# Copyright 2018, Jordi Sanfeliu. All rights reserved. +# Distributed under the terms of the Fiwix License. +# + +.S.o: + $(CC) -traditional -I$(INCLUDE) -c -o $@ $< +.c.o: + $(CC) $(CFLAGS) -c -o $@ $< + +OBJS = super.o inode.o namei.o dir.o file.o symlink.o tree.o data.o + +procfs: $(OBJS) + $(LD) $(LDFLAGS) -r $(OBJS) -o procfs.o + +clean: + rm -f *.o + diff --git a/fs/procfs/data.c b/fs/procfs/data.c new file mode 100644 index 00000000..48c190bb --- /dev/null +++ b/fs/procfs/data.c @@ -0,0 +1,729 @@ +/* + * fiwix/fs/procfs/data.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FSHIFT16 16 +#define FIXED16_1 (1 << FSHIFT16) +#define LOAD_INT(x) ((x) >> FSHIFT16) +#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED16_1 - 1)) * 100) + +static const char *pstate[] = { + "? (unused!)", + "R (running)", + "S (sleeping)", + "Z (zombie)", + "T (stopped)", + "D (idle)", +}; + +/* + * procfs root directory related functions + * --------------------------------------- + */ +int data_proc_self(char *buffer, __pid_t pid) +{ + return sprintk(buffer, "%s", current->pidstr); +} + +int data_proc_cmdline(char *buffer, __pid_t pid) +{ + return sprintk(buffer, "%s\n", cmdline); +} + +int data_proc_cpuinfo(char *buffer, __pid_t pid) +{ + int size; + + size = sprintk(buffer, "processor : 0\n"); + size += sprintk(buffer + size, "cpu family : %d86\n", cpu_table.family <= 6 ? cpu_table.family : 6); + if(cpu_table.model >= 0) { + size += sprintk(buffer + size, "model : %d\n", cpu_table.model); + } else { + size += sprintk(buffer + size, "model : unknown\n"); + } + + if(cpu_table.vendor_id) { + size += sprintk(buffer + size, "vendor_id : %s\n", cpu_table.vendor_id); + } + if(cpu_table.model_name) { + size += sprintk(buffer + size, "model name : %s\n", cpu_table.model_name); + } + if(cpu_table.stepping >= 0) { + size += sprintk(buffer + size, "stepping : %d\n", cpu_table.stepping); + } else { + size += sprintk(buffer + size, "stepping : unknown\n"); + } + + size += sprintk(buffer + size, "cpu MHz : "); + if(cpu_table.hz) { + size += sprintk(buffer + size, "%d.%d\n", (cpu_table.hz / 1000000), ((cpu_table.hz % 1000000) / 100000)); + } else { + size += sprintk(buffer + size, "unknown\n"); + } + if(cpu_table.cache) { + size += sprintk(buffer + size, "cache size : %s\n", cpu_table.cache); + } + size += sprintk(buffer + size, "cpuid : %s\n", cpu_table.has_cpuid ? "yes" : "no"); + size += sprintk(buffer + size, "fpu : %s\n", cpu_table.has_fpu ? "yes" : "no"); + size += get_cpu_flags(buffer, size); + return size; +} + +int data_proc_devices(char *buffer, __pid_t pid) +{ + int n, size; + struct device *d; + + d = chr_device_table; + size = sprintk(buffer, "Character devices:\n"); + for(n = 0; n < NR_CHRDEV; n++, d++) { + if(d->major) { + size += sprintk(buffer + size, "%3d %s\n", d->major, d->name); + } + } + + size += sprintk(buffer + size, "\nBlock devices:\n"); + d = blk_device_table; + for(n = 0; n < NR_BLKDEV; n++, d++) { + if(d->major) { + size += sprintk(buffer + size, "%3d %s\n", d->major, d->name); + } + } + return size; +} + +int data_proc_dma(char *buffer, __pid_t pid) +{ + int n, size; + + size = 0; + for(n = 0; n < DMA_CHANNELS; n++) { + if(dma_resources[n]) { + size += sprintk(buffer + size, "%2d: %s\n", n, dma_resources[n]); + } + } + return size; +} + +int data_proc_filesystems(char *buffer, __pid_t pid) +{ + int n, size; + int nodev; + + size = 0; + for(n = 0; n < NR_FILESYSTEMS; n++) { + if(filesystems_table[n].name) { + nodev = 0; + if(filesystems_table[n].fsop->flags != FSOP_REQUIRES_DEV) { + nodev = 1; + } + size += sprintk(buffer + size, "%s %s\n", nodev ? "nodev" : " ", filesystems_table[n].name); + } + } + return size; +} + +int data_proc_interrupts(char *buffer, __pid_t pid) +{ + int n, size; + + size = 0; + for(n = 0; n < NR_IRQS; n++) { + if(irq_table[n].registered) { + size += sprintk(buffer + size, "%3d: %9u %s\n", n, irq_table[n].ticks, irq_table[n].name); + } + } + size += sprintk(buffer + size, "SPU: %9u %s\n", kstat.sirqs, "Spurious interrupts"); + return size; +} + +int data_proc_loadavg(char *buffer, __pid_t pid) +{ + int a, b, c; + int size; + struct proc *p; + int nrun = 0; + int nprocs = 0; + + a = avenrun[0] << (SI_LOAD_SHIFT - FSHIFT); + b = avenrun[1] << (SI_LOAD_SHIFT - FSHIFT); + c = avenrun[2] << (SI_LOAD_SHIFT - FSHIFT); + + FOR_EACH_PROCESS(p) { + if(p->state) { + nprocs++; + if(p->state == PROC_RUNNING) { + nrun++; + } + } + } + + size = sprintk(buffer, "%d.%02d %d.%02d %d.%02d %d/%d %d\n", LOAD_INT(a), LOAD_FRAC(a), LOAD_INT(b), LOAD_FRAC(b), LOAD_INT(c), LOAD_FRAC(c), nrun, nprocs, lastpid); + return size; +} + +int data_proc_locks(char *buffer, __pid_t pid) +{ + int n, size; + struct flock_file *ff; + + size = 0; + + for(n = 0; n < NR_FLOCKS; n++) { + ff = &flock_file_table[n]; + if(ff->inode) { + size += sprintk(buffer + size, "%d: FLOCK ADVISORY %s ", n + 1, ff->type & LOCK_SH ? "READ " : "WRITE"); + size += sprintk(buffer + size, "%d %x:%d:%d 0 EOF\n", ff->proc->pid, MAJOR(ff->inode->dev), MINOR(ff->inode->dev), ff->inode->inode); + } + } + + return size; +} + +int data_proc_meminfo(char *buffer, __pid_t pid) +{ + struct page *pg; + int n, size; + + kstat.shared = 0; + for(n = 0; n < kstat.physical_pages; n ++) { + pg = &page_table[n]; + if(pg->flags & PAGE_RESERVED) { + continue; + } + if(!pg->count) { + continue; + } + kstat.shared += pg->count - 1; + } + + size = 0; + size += sprintk(buffer + size, " total: used: free: shared: buffers: cached:\n"); + size += sprintk(buffer + size, "Mem: %8u %8u %8u %8u %8u %8u\n", kstat.total_mem_pages << PAGE_SHIFT, (kstat.total_mem_pages << PAGE_SHIFT) - (kstat.free_pages << PAGE_SHIFT), kstat.free_pages << PAGE_SHIFT, kstat.shared * 1024, kstat.buffers * 1024, kstat.cached * 1024); + size += sprintk(buffer + size, "Swap: %8u %8u %8u\n", 0, 0, 0); + size += sprintk(buffer + size, "MemTotal: %9d kB\n", kstat.total_mem_pages << 2); + size += sprintk(buffer + size, "MemFree: %9d kB\n", kstat.free_pages << 2); + size += sprintk(buffer + size, "MemShared:%9d kB\n", kstat.shared); + size += sprintk(buffer + size, "Buffers: %9d kB\n", kstat.buffers); + size += sprintk(buffer + size, "Cached: %9d kB\n", kstat.cached); + size += sprintk(buffer + size, "SwapTotal:%9d kB\n", 0); + size += sprintk(buffer + size, "SwapFree: %9d kB\n", 0); + return size; +} + +int data_proc_mounts(char *buffer, __pid_t pid) +{ + int n, size; + char *flag; + + size = 0; + for(n = 0; n < NR_MOUNT_POINTS; n++) { + if(mount_table[n].used) { + if(mount_table[n].fs->fsop->flags != FSOP_KERN_MOUNT) { + flag = "rw"; + if(mount_table[n].sb.flags & MS_RDONLY) { + flag = "ro"; + } + size += sprintk(buffer + size, "%s %s %s %s 0 0\n", mount_table[n].devname, mount_table[n].dirname, mount_table[n].fs->name, flag); + } + } + } + return size; +} + +int data_proc_partitions(char *buffer, __pid_t pid) +{ + int n, ctrl, drv, size; + int minor, major; + unsigned int blocks; + struct ide *ide; + struct ide_drv *drive; + + size = 0; + size += sprintk(buffer + size, "major minor #blocks name\n\n"); + + for(ctrl = 0; ctrl < NR_IDE_CTRLS; ctrl++) { + ide = &ide_table[ctrl]; + for(drv = 0; drv < NR_IDE_DRVS; drv++) { + drive = &ide->drive[drv]; + if(!drive->nr_sects) { + continue; + } + if(drive->flags & DEVICE_IS_DISK) { + major = (int)drive->major; + minor = (int)drive->minor_shift; + blocks = drive->nr_sects / 2; + size += sprintk(buffer + size, "%4d %4d %9d %s\n", major, 0, blocks, drive->dev_name); + for(n = 0; n < NR_PARTITIONS; n++) { + if(drive->part_table[n].type) { + blocks = drive->part_table[n].nr_sects / 2; + size += sprintk(buffer + size, "%4d %4d %9u %s%d\n", major, (n + 1) << minor, blocks, drive->dev_name, n + 1); + } + } + } + } + } + return size; +} + +int data_proc_rtc(char *buffer, __pid_t pid) +{ + int size; + short int sec, min, hour; + short int day, month, year, century; + + sec = cmos_read_date(CMOS_SEC); + min = cmos_read_date(CMOS_MIN); + hour = cmos_read_date(CMOS_HOUR); + day = cmos_read_date(CMOS_DAY); + month = cmos_read_date(CMOS_MONTH); + year = cmos_read_date(CMOS_YEAR); + century = cmos_read_date(CMOS_CENTURY); + year += century * 100; + + size = 0; + size += sprintk(buffer + size, "rtc_time\t: %02d:%02d:%02d\n", hour, min, sec); + size += sprintk(buffer + size, "rtc_date\t: %02d-%02d-%02d\n", year, month, day); + sec = cmos_read_date(CMOS_ASEC); + min = cmos_read_date(CMOS_AMIN); + hour = cmos_read_date(CMOS_AHOUR); + size += sprintk(buffer + size, "alarm\t\t: %02d:%02d:%02d\n", hour, min, sec); + size += sprintk(buffer + size, "DST_enable\t: %s\n", cmos_read(CMOS_STATB) & CMOS_STATB_DSE ? "yes" : "no"); + size += sprintk(buffer + size, "BCD\t\t: %s\n", cmos_read(CMOS_STATB) & CMOS_STATB_DM ? "no" : "yes"); + size += sprintk(buffer + size, "24hr\t\t: %s\n", cmos_read(CMOS_STATB) & CMOS_STATB_24H ? "yes" : "no"); + size += sprintk(buffer + size, "square_wave\t: %s\n", cmos_read(CMOS_STATB) & CMOS_STATB_SQWE ? "yes" : "no"); + size += sprintk(buffer + size, "alarm_IRQ\t: %s\n", cmos_read(CMOS_STATB) & CMOS_STATB_AIE ? "yes" : "no"); + size += sprintk(buffer + size, "update_IRQ\t: %s\n", cmos_read(CMOS_STATB) & CMOS_STATB_UIE ? "yes" : "no"); + size += sprintk(buffer + size, "periodic_IRQ\t: %s\n", cmos_read(CMOS_STATB) & CMOS_STATB_PIE ? "yes" : "no"); + size += sprintk(buffer + size, "periodic_freq\t: %s\n", (cmos_read(CMOS_STATA) & CMOS_STATA_IRQF) == 0x6 ? "1024" : "?"); + size += sprintk(buffer + size, "batt_status\t: %s\n", cmos_read(CMOS_STATD) & CMOS_STATD_VRT ? "okay" : "dead"); + return size; +} + +int data_proc_stat(char *buffer, __pid_t pid) +{ + int n, size; + unsigned int idle; + + idle = kstat.ticks - (kstat.cpu_user + kstat.cpu_nice + kstat.cpu_system); + size = 0; + size += sprintk(buffer + size, "cpu %d %d %d %d\n", kstat.cpu_user, kstat.cpu_nice, kstat.cpu_system, idle); + size += sprintk(buffer + size, "disk 0 0 0 0\n"); + size += sprintk(buffer + size, "page 0 0\n"); + size += sprintk(buffer + size, "swap 0 0\n"); + size += sprintk(buffer + size, "intr %u", kstat.irqs); + for(n = 0; n < NR_IRQS; n++) { + size += sprintk(buffer + size, " %u", irq_table[n].ticks); + } + size += sprintk(buffer + size, "\n"); + size += sprintk(buffer + size, "ctxt %u\n", kstat.ctxt); + size += sprintk(buffer + size, "btime %d\n", kstat.boot_time); + size += sprintk(buffer + size, "processes %d\n", kstat.processes); + return size; +} + +int data_proc_uptime(char *buffer, __pid_t pid) +{ + struct proc *p; + unsigned long int idle; + + p = &proc_table[IDLE]; + idle = tv2ticks(&p->usage.ru_utime); + idle += tv2ticks(&p->usage.ru_stime); + return sprintk(buffer, "%u.%02u %u.%02u\n", kstat.uptime, kstat.ticks % HZ, idle / HZ, idle % HZ); +} + +int data_proc_fullversion(char *buffer, __pid_t pid) +{ + return sprintk(buffer, "Fiwix version %s %s\n", UTS_RELEASE, UTS_VERSION); +} + +int data_proc_domainname(char *buffer, __pid_t pid) +{ + return sprintk(buffer, "%s\n", sys_utsname.domainname); +} + +int data_proc_filemax(char *buffer, __pid_t pid) +{ + return sprintk(buffer, "%d\n", NR_OPENS); +} + +int data_proc_filenr(char *buffer, __pid_t pid) +{ + int n, nr; + + nr = 0; + for(n = 1; n < NR_OPENS; n++) { + if(fd_table[n].count != 0) { + nr++; + } + } + return sprintk(buffer, "%d\n", nr); +} + +int data_proc_hostname(char *buffer, __pid_t pid) +{ + return sprintk(buffer, "%s\n", sys_utsname.nodename); +} + +int data_proc_inodemax(char *buffer, __pid_t pid) +{ + return sprintk(buffer, "%d\n", inode_table_size / sizeof(struct inode)); +} + +int data_proc_inodenr(char *buffer, __pid_t pid) +{ + return sprintk(buffer, "%d\n", (inode_table_size / sizeof(struct inode)) - inodes_on_free_list); +} + +int data_proc_osrelease(char *buffer, __pid_t pid) +{ + return sprintk(buffer, "%s\n", UTS_RELEASE); +} + +int data_proc_ostype(char *buffer, __pid_t pid) +{ + return sprintk(buffer, "%s\n", UTS_SYSNAME); +} + +int data_proc_version(char *buffer, __pid_t pid) +{ + return sprintk(buffer, "%s\n", UTS_VERSION); +} + + +/* + * PID directory related functions + * ------------------------------- + */ +int data_proc_pid_cmdline(char *buffer, __pid_t pid) +{ + int n, size; + char *arg; + char **argv; + unsigned int paddr, offset; + struct proc *p; + + size = 0; + if((p = get_proc_by_pid(pid))) { + if(p->argv) { + offset = (int)p->argv & ~PAGE_MASK; + paddr = get_mapped_addr(p, (int)p->argv) & PAGE_MASK; + paddr = P2V(paddr); + argv = (char **)(paddr + offset); + for(n = 0; argv[n]; n++) { + offset = (int)argv[n] & ~PAGE_MASK; + paddr = get_mapped_addr(p, (int)argv[n]) & PAGE_MASK; + paddr = P2V(paddr); + arg = (char *)(paddr + offset); + size += sprintk(buffer + size, "%s", arg); + buffer[size++] = NULL; + } + } + } + return size; +} + +int data_proc_pid_cwd(char *buffer, __pid_t pid) +{ + int size; + struct proc *p; + struct inode *i; + + size = 0; + if((p = get_proc_by_pid(pid))) { + + /* zombie processes don't have current working directory */ + if(!p->pwd) { + return -ENOENT; + } + + i = p->pwd; + size = sprintk(buffer, "[%02d%02d]:%d", MAJOR(i->rdev), MINOR(i->rdev), i->inode); + } + return size; +} + +int data_proc_pid_environ(char *buffer, __pid_t pid) +{ + int n, size; + char *env; + char **envp; + unsigned int paddr, offset; + struct proc *p; + + size = 0; + if((p = get_proc_by_pid(pid))) { + if(p->envp) { + offset = (int)p->envp & ~PAGE_MASK; + paddr = get_mapped_addr(p, (int)p->envp) & PAGE_MASK; + paddr = P2V(paddr); + envp = (char **)(paddr + offset); + for(n = 0; envp[n]; n++) { + offset = (int)envp[n] & ~PAGE_MASK; + paddr = get_mapped_addr(p, (int)envp[n]) & PAGE_MASK; + paddr = P2V(paddr); + env = (char *)(paddr + offset); + size += sprintk(buffer + size, "%s", env); + buffer[size++] = NULL; + } + } + } + return size; +} + +int data_proc_pid_exe(char *buffer, __pid_t pid) +{ + int size; + struct proc *p; + struct inode *i; + + size = 0; + if((p = get_proc_by_pid(pid))) { + + /* kernel and zombie processes are programless */ + if(!p->vma || !p->vma->inode) { + return -ENOENT; + } + + i = p->vma->inode; + size = sprintk(buffer, "[%02d%02d]:%d", MAJOR(i->rdev), MINOR(i->rdev), i->inode); + } + return size; +} + +int data_proc_pid_maps(char *buffer, __pid_t pid) +{ + unsigned int n; + int size, len; + __ino_t inode; + int major, minor; + char *section; + char r, w, x, f; + struct proc *p; + struct vma *vma; + + size = 0; + if((p = get_proc_by_pid(pid))) { + if(!p->vma) { + return 0; + } + vma = p->vma; + for(n = 0; n < VMA_REGIONS && vma->start; n++, vma++) { + r = vma->prot & PROT_READ ? 'r' : '-'; + w = vma->prot & PROT_WRITE ? 'w' : '-'; + x = vma->prot & PROT_EXEC ? 'x' : '-'; + if(vma->flags & MAP_SHARED) { + f = 's'; + } else if(vma->flags & MAP_PRIVATE) { + f = 'p'; + } else { + f = '-'; + } + switch(vma->s_type) { + case P_TEXT: section = "text"; + break; + case P_DATA: section = "data"; + break; + case P_BSS: section = "bss"; + break; + case P_HEAP: section = "heap"; + break; + case P_STACK: section = "stack"; + break; + case P_MMAP: section = "mmap"; + break; + default: + section = NULL; + break; + } + inode = major = minor = 0; + if(vma->inode) { + inode = vma->inode->inode; + major = MAJOR(vma->inode->dev); + minor = MINOR(vma->inode->dev); + } + len = sprintk(buffer + size, "%08x-%08x %c%c%c%c %08x %02d:%02d %- 10u [%s]\n", vma->start, vma->end, r, w, x, f, vma->offset, major, minor, inode, section); + size += len; + } + } + return size; +} + +int data_proc_pid_mountinfo(char *buffer, __pid_t pid) +{ + int n, size; + char *flag, *devname; + + size = 0; + for(n = 0; n < NR_MOUNT_POINTS; n++) { + if(mount_table[n].used) { + if(mount_table[n].fs->fsop->flags != FSOP_KERN_MOUNT) { + flag = "rw"; + if(mount_table[n].sb.flags & MS_RDONLY) { + flag = "ro"; + } + devname = mount_table[n].devname; + if(!strcmp(mount_table[n].devname, "/dev/root")) { + devname = _rootdevname; + } + size += sprintk(buffer + size, "%d 0 %d:%d %s %s %s - %s %s %s\n", n, MAJOR(mount_table[n].dev), MINOR(mount_table[n].dev), "/", mount_table[n].dirname, flag, mount_table[n].fs->name, devname, flag); + } + } + } + return size; +} + +int data_proc_pid_root(char *buffer, __pid_t pid) +{ + int size; + struct proc *p; + struct inode *i; + + size = 0; + if((p = get_proc_by_pid(pid))) { + + /* zombie processes don't have root directory */ + if(!p->root) { + return -ENOENT; + } + + i = p->root; + size = sprintk(buffer, "[%02d%02d]:%d", MAJOR(i->rdev), MINOR(i->rdev), i->inode); + } + return size; +} + +int data_proc_pid_stat(char *buffer, __pid_t pid) +{ + int size, vma_start, vma_end; + unsigned int esp, eip; + int signum, mask; + __sigset_t sigignored, sigcaught; + struct proc *p; + struct sigcontext *sc; + + size = vma_start = vma_end = 0; + if((p = get_proc_by_pid(pid))) { + if(p->vma) { + vma_start = p->vma[0].start; + vma_end = p->vma[0].end; + } + + sigignored = sigcaught = 0; + for(signum = 0, mask = 1; signum < NSIG; signum++, mask <<= 1) { + if(p->sigaction[signum].sa_handler == SIG_IGN) { + sigignored |= mask; + } + if(p->sigaction[signum].sa_handler == SIG_DFL) { + sigcaught |= mask; + } + } + + esp = eip = 0; + if(p->sp) { + sc = (struct sigcontext *)p->sp; + esp = sc->oldesp; + eip = sc->eip; + } + size = sprintk(buffer, "%d (%s) %c %d %d %d %d %d %d %d %d %d %d %u %u %u %u %d %d %d %d %d %d %u %u %u %u %u %u %u %d %d %u %u %u\n", + p->pid, + p->argv0, + pstate[p->state][0], + p->ppid, p->pgid, p->sid, + p->ctty ? p->ctty->dev : 0, + p->ctty ? p->ctty->pgid : - 1, + 0, /* flags */ + 0, 0, 0, 0, /* minflt, cminflt, majflt, cmajflt */ + tv2ticks(&p->usage.ru_utime), + tv2ticks(&p->usage.ru_stime), + tv2ticks(&p->cusage.ru_utime), + tv2ticks(&p->cusage.ru_stime), + 0, /* counter */ + 0, /* priority */ + 0, /* timeout */ + 0, /* itrealvalue */ + p->start_time, + 0, /* vsize */ + p->rss, + 0x7FFFFFFF, /* rlim */ + vma_start, /* startcode */ + vma_end, /* endcode */ + KERNEL_BASE_ADDR - 1, /* startstack */ + esp, /* kstkesp */ + eip, /* kstkeip */ + p->sigpending, + p->sigblocked, + sigignored, + sigcaught, + p->sleep_address + ); + } + return size; +} + +int data_proc_pid_status(char *buffer, __pid_t pid) +{ + int size; + int signum, mask; + __sigset_t sigignored, sigcaught; + struct proc *p; + + size = 0; + if((p = get_proc_by_pid(pid))) { + size = sprintk(buffer, "Name:\t%s\n", p->argv0); + size += sprintk(buffer + size, "State:\t%s\n", pstate[p->state]); + size += sprintk(buffer + size, "Pid:\t%d\n", p->pid); + size += sprintk(buffer + size, "PPid:\t%d\n", p->ppid); + size += sprintk(buffer + size, "Uid:\t%d\t%d\t%d\t-\n", p->uid, p->euid, p->suid); + size += sprintk(buffer + size, "Gid:\t%d\t%d\t%d\t-\n", p->gid, p->egid, p->sgid); + size += sprintk(buffer + size, "VmSize:\t\t%c kB\n", '-'); + size += sprintk(buffer + size, "VmLck:\t\t%c kB\n", '-'); + size += sprintk(buffer + size, "VmRSS:\t\t%c kB\n", '-'); + size += sprintk(buffer + size, "VmData:\t\t%c kB\n", '-'); + size += sprintk(buffer + size, "VmStk:\t\t%c kB\n", '-'); + size += sprintk(buffer + size, "VmExe:\t\t%c kB\n", '-'); + size += sprintk(buffer + size, "VmLib:\t\t%c kB\n", '-'); + size += sprintk(buffer + size, "SigPnd:\t%08x\n", p->sigpending); + size += sprintk(buffer + size, "SigBlk:\t%08x\n", p->sigblocked); + sigignored = sigcaught = 0; + for(signum = 0, mask = 1; signum < NSIG; signum++, mask <<= 1) { + if(p->sigaction[signum].sa_handler == SIG_IGN) { + sigignored |= mask; + } + if(p->sigaction[signum].sa_handler == SIG_DFL) { + sigcaught |= mask; + } + } + size += sprintk(buffer + size, "SigIgn:\t%08x\n", sigignored); + size += sprintk(buffer + size, "SigCgt:\t%08x\n", sigcaught); + } + return size; +} diff --git a/fs/procfs/dir.c b/fs/procfs/dir.c new file mode 100644 index 00000000..fd9ceed4 --- /dev/null +++ b/fs/procfs/dir.c @@ -0,0 +1,261 @@ +/* + * fiwix/fs/procfs/dir.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct fs_operations procfs_dir_fsop = { + 0, + 0, + + procfs_dir_open, + procfs_dir_close, + procfs_dir_read, + NULL, /* write */ + NULL, /* ioctl */ + NULL, /* lseek */ + procfs_dir_readdir, + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + procfs_bmap, + procfs_lookup, + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +static int proc_listdir(char *buffer) +{ + int n; + struct proc *p; + struct procfs_dir_entry *pd; + struct procfs_dir_entry d; + int size; + + size = n = 0; + pd = (struct procfs_dir_entry *)buffer; + + FOR_EACH_PROCESS(p) { + if(p->state) { + d.inode = PROC_PID_INO + (p->pid << 12); + d.mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + d.lev = -1; + d.name_len = 1; + n = p->pid; + while(n) { + n /= 10; + d.name_len++; + } + d.name = p->pidstr; + d.data_fn = NULL; + + if(size + sizeof(d) >= 4096) { + printk("WARNING: kmalloc() is limited to 4096 bytes.\n"); + break; + } + + size += sizeof(d); + memcpy_b((void *)pd, (void *)&d, sizeof(d)); + pd++; + } + } + memset_b((void *)pd + size, NULL, sizeof(d)); + return size; +} + +static int proc_listfd(struct inode *i, char *buffer) +{ + int n; + struct proc *p; + struct procfs_dir_entry *pd; + struct procfs_dir_entry d; + int size; + + size = 0; + pd = (struct procfs_dir_entry *)buffer; + + p = get_proc_by_pid((i->inode >> 12) & 0xFFFF); + for(n = 0; n < OPEN_MAX; n++) { + if(p->fd[n]) { + d.inode = PROC_PID_INO + (p->pid << 12) + n; + d.mode = S_IFREG | S_IRWXU; + d.nlink = 1; + d.lev = -1; + d.name_len = sprintk(d.name, "%d", n); + d.data_fn = NULL; + + if(size + sizeof(d) >= 4096) { + printk("WARNING: kmalloc() is limited to 4096 bytes.\n"); + break; + } + + size += sizeof(d); + memcpy_b((void *)pd, (void *)&d, sizeof(d)); + pd++; + } + } + memset_b((void *)pd + size, NULL, sizeof(d)); + return size; +} + +int procfs_dir_open(struct inode *i, struct fd *fd_table) +{ + fd_table->offset = 0; + return 0; +} + +int procfs_dir_close(struct inode *i, struct fd *fd_table) +{ + return 0; +} + +int procfs_dir_read(struct inode *i, struct fd *fd_table, char *buffer, __size_t count) +{ + __off_t total_read; + unsigned int boffset, bytes; + int blksize, len; + int lev; + char *buf; + + if(!(buf = (void *)kmalloc())) { + return -ENOMEM; + } + + /* create the list of directories for each process */ + len = 0; + if(i->inode == PROC_ROOT_INO) { + len = proc_listdir(buf); + } + + /* create the list of fds used for each process (TODO) + if((i->inode & 0xF0000FFF) == PROC_PID_FD) { + len = proc_listfd(i, buf); + } + */ + + /* add the rest of static files in the main directory */ + lev = i->u.procfs.i_lev; + if((len + sizeof(procfs_array[lev])) > (PAGE_SIZE - 1)) { + printk("WARNING: %s(): len > 4096 (%d)!\n", __FUNCTION__, len); + } + memcpy_b(buf + len, (char *)&procfs_array[lev], sizeof(procfs_array[lev])); + len += sizeof(procfs_array[lev]); + blksize = i->sb->s_blocksize; + if(fd_table->offset > len) { + fd_table->offset = len; + } + + total_read = 0; + + for(;;) { + count = (fd_table->offset + count > len) ? len - fd_table->offset : count; + if(!count) { + break; + } + + boffset = fd_table->offset % blksize; + bytes = blksize - boffset; + bytes = MIN(bytes, count); + memcpy_b(buffer + total_read, buf + boffset, bytes); + total_read += bytes; + count -= bytes; + boffset += bytes; + boffset %= blksize; + fd_table->offset += bytes; + } + + kfree((unsigned int)buf); + return total_read; +} + +int procfs_dir_readdir(struct inode *i, struct fd *fd_table, struct dirent *dirent, unsigned int count) +{ + unsigned int offset, boffset, dirent_offset, doffset; + int dirent_len; + unsigned int total_read; + struct procfs_dir_entry *d; + int base_dirent_len; + char *buffer; + int lev; + + if(!i->fsop || !i->fsop->read) { + return -EBADF; + } + if(!(buffer = (void *)kmalloc())) { + return -ENOMEM; + } + + lev = i->u.procfs.i_lev; + base_dirent_len = sizeof(dirent->d_ino) + sizeof(dirent->d_off) + sizeof(dirent->d_reclen); + + offset = fd_table->offset; + boffset = dirent_offset = doffset = 0; + + boffset = offset % i->sb->s_blocksize; + + total_read = i->fsop->read(i, fd_table, buffer, count); + if((count = MIN(total_read, count)) == 0) { + kfree((unsigned int)buffer); + return dirent_offset; + } + + while(boffset < total_read) { + d = (struct procfs_dir_entry *)(buffer + boffset); + if(!d->inode) { + break; + } + dirent_len = (base_dirent_len + (d->name_len + 1)) + 3; + dirent_len &= ~3; /* round up */ + if((doffset + sizeof(struct procfs_dir_entry)) <= count) { + boffset += sizeof(struct procfs_dir_entry); + offset += sizeof(struct procfs_dir_entry); + doffset += sizeof(struct procfs_dir_entry); + dirent->d_ino = d->inode; + dirent->d_off = offset; + dirent->d_reclen = dirent_len; + memcpy_b(dirent->d_name, d->name, d->name_len); + dirent->d_name[d->name_len] = NULL; + dirent = (struct dirent *)((char *)dirent + dirent_len); + dirent_offset += dirent_len; + } else { + break; + } + } + fd_table->offset = offset; + kfree((unsigned int)buffer); + return dirent_offset; +} diff --git a/fs/procfs/file.c b/fs/procfs/file.c new file mode 100644 index 00000000..e2948171 --- /dev/null +++ b/fs/procfs/file.c @@ -0,0 +1,124 @@ +/* + * fiwix/fs/procfs/file.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct fs_operations procfs_file_fsop = { + 0, + 0, + + procfs_file_open, + procfs_file_close, + procfs_file_read, + NULL, /* write */ + NULL, /* ioctl */ + procfs_file_lseek, + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + procfs_bmap, + NULL, /* lookup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +int procfs_file_open(struct inode *i, struct fd *fd_table) +{ + if(fd_table->flags & (O_WRONLY | O_RDWR | O_TRUNC | O_APPEND)) { + return -EINVAL; + } + fd_table->offset = 0; + return 0; +} + +int procfs_file_close(struct inode *i, struct fd *fd_table) +{ + return 0; +} + +int procfs_file_read(struct inode *i, struct fd *fd_table, char *buffer, __size_t count) +{ + __off_t total_read; + unsigned int boffset, bytes, size; + int blksize; + struct procfs_dir_entry *d; + char *buf; + + if(!(d = get_procfs_by_inode(i))) { + return -EINVAL; + } + if(!d->data_fn) { + return -EINVAL; + } + if(!(buf = (void *)kmalloc())) { + return -ENOMEM; + } + + size = d->data_fn(buf, (i->inode >> 12) & 0xFFFF); + blksize = i->sb->s_blocksize; + if(fd_table->offset > size) { + fd_table->offset = size; + } + + total_read = 0; + + for(;;) { + count = (fd_table->offset + count > size) ? size - fd_table->offset : count; + if(!count) { + break; + } + + boffset = fd_table->offset % blksize; + bytes = blksize - boffset; + bytes = MIN(bytes, count); + memcpy_b(buffer + total_read, buf + boffset, bytes); + total_read += bytes; + count -= bytes; + boffset += bytes; + boffset %= blksize; + fd_table->offset += bytes; + } + + kfree((unsigned int)buf); + return total_read; +} + +int procfs_file_lseek(struct inode *i, __off_t offset) +{ + return offset; +} diff --git a/fs/procfs/inode.c b/fs/procfs/inode.c new file mode 100644 index 00000000..b3678693 --- /dev/null +++ b/fs/procfs/inode.c @@ -0,0 +1,89 @@ +/* + * fiwix/fs/procfs/inode.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int procfs_read_inode(struct inode *i) +{ + int lev; + __mode_t mode; + __nlink_t nlink; + struct procfs_dir_entry *d; + + if((i->inode & 0xF0000FFF) == PROC_PID_INO) { /* dynamic PID dir */ + mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + nlink = 3; + lev = PROC_PID_LEV; + } else { + if(!(d = get_procfs_by_inode(i))) { + return NULL; + } + mode = d->mode; + nlink = d->nlink; + lev = d->lev; + } + + i->i_mode = mode; + i->i_uid = 0; + i->i_size = 0; + i->i_atime = CURRENT_TIME; + i->i_ctime = CURRENT_TIME; + i->i_mtime = CURRENT_TIME; + i->i_gid = 0; + i->i_nlink = nlink; + i->i_blocks = 0; + i->i_flags = 0; + i->locked = 1; + i->dirty = 0; + i->mount_point = NULL; + i->count = 1; + i->u.procfs.i_lev = lev; + switch(i->i_mode & S_IFMT) { + case S_IFDIR: + i->fsop = &procfs_dir_fsop; + break; + case S_IFREG: + i->fsop = &procfs_file_fsop; + break; + case S_IFLNK: + i->fsop = &procfs_symlink_fsop; + break; + default: + PANIC("invalid inode (%d) mode %08o.\n", i->inode, i->i_mode); + } + return 0; +} + +int procfs_bmap(struct inode *i, __off_t offset, int mode) +{ + return i->u.procfs.i_lev; +} + +void procfs_statfs(struct superblock *sb, struct statfs *statfsbuf) +{ + statfsbuf->f_type = PROC_SUPER_MAGIC; + statfsbuf->f_bsize = sb->s_blocksize; + statfsbuf->f_blocks = 0; + statfsbuf->f_bfree = 0; + statfsbuf->f_bavail = 0; + statfsbuf->f_files = 0; + statfsbuf->f_ffree = 0; + /* statfsbuf->f_fsid = ? */ + statfsbuf->f_namelen = NAME_MAX; +} diff --git a/fs/procfs/namei.c b/fs/procfs/namei.c new file mode 100644 index 00000000..5c39b313 --- /dev/null +++ b/fs/procfs/namei.c @@ -0,0 +1,89 @@ +/* + * fiwix/fs/procfs/namei.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int procfs_lookup(const char *name, struct inode *dir, struct inode **i_res) +{ + int len, lev; + __ino_t inode; + __pid_t pid; + struct proc *p; + struct procfs_dir_entry *pdirent; + + pid = inode = 0; + len = strlen(name); + if((dir->inode & 0xF0000000) == PROC_PID_INO) { + pid = (dir->inode >> 12) & 0xFFFF; + } + dir->count++; + + lev = bmap(dir, 0, FOR_READING); + pdirent = procfs_array[lev]; + while(pdirent->inode && !inode) { + if(len == pdirent->name_len) { + if(!(strcmp(pdirent->name, name))) { + inode = pdirent->inode; + if(pid) { + inode = (PROC_PID_INO + (pid << 12)) + (inode & 0xFFF); + if(strcmp(".", name) == 0) { + inode = dir->inode; + } + if(strcmp("..", name) == 0) { + inode = pdirent->inode; + } + } + } + } + if(inode) { + /* + * This prevents a deadlock in iget() when + * trying to lock '.' when 'dir' is the same + * directory (ls -lai ). + */ + if(inode == dir->inode) { + *i_res = dir; + return 0; + } + + if(!(*i_res = iget(dir->sb, inode))) { + return -EACCES; + } + iput(dir); + return 0; + } + pdirent++; + } + + FOR_EACH_PROCESS(p) { + if(p->state) { + if(len == strlen(p->pidstr)) { + if(!(strcmp(p->pidstr, name))) { + inode = PROC_PID_INO + (p->pid << 12); + } + } + if(inode) { + if(!(*i_res = iget(dir->sb, inode))) { + return -EACCES; + } + iput(dir); + return 0; + } + } + } + iput(dir); + return -ENOENT; +} diff --git a/fs/procfs/super.c b/fs/procfs/super.c new file mode 100644 index 00000000..4c79143d --- /dev/null +++ b/fs/procfs/super.c @@ -0,0 +1,82 @@ +/* + * fiwix/fs/procfs/super.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct fs_operations procfs_fsop = { + 0, + PROC_DEV, + + NULL, /* open */ + NULL, /* close */ + NULL, /* read */ + NULL, /* write */ + NULL, /* ioctl */ + NULL, /* lseek */ + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lookup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + procfs_read_inode, + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + procfs_statfs, + procfs_read_superblock, + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +int procfs_read_superblock(__dev_t dev, struct superblock *sb) +{ + superblock_lock(sb); + sb->dev = dev; + sb->fsop = &procfs_fsop; + sb->s_blocksize = PAGE_SIZE; + + if(!(sb->root = iget(sb, PROC_ROOT_INO))) { + printk("WARNING: %s(): unable to get root inode.\n", __FUNCTION__); + superblock_unlock(sb); + return -EINVAL; + } + sb->root->u.procfs.i_lev = 0; + + superblock_unlock(sb); + return 0; +} + +int procfs_init(void) +{ + return register_filesystem("proc", &procfs_fsop); +} diff --git a/fs/procfs/symlink.c b/fs/procfs/symlink.c new file mode 100644 index 00000000..717985bc --- /dev/null +++ b/fs/procfs/symlink.c @@ -0,0 +1,136 @@ +/* + * fiwix/fs/procfs/symlink.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct fs_operations procfs_symlink_fsop = { + 0, + 0, + + NULL, /* open */ + NULL, /* close */ + NULL, /* read */ + NULL, /* write */ + NULL, /* ioctl */ + NULL, /* lseek */ + NULL, /* readdir */ + NULL, /* mmap */ + NULL, /* select */ + + procfs_readlink, + procfs_followlink, + NULL, /* bmap */ + NULL, /* lookup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +int procfs_readlink(struct inode *i, char *buffer, __size_t count) +{ + __off_t size_read; + struct procfs_dir_entry *d; + + if(!(d = get_procfs_by_inode(i))) { + return -EINVAL; + } + + if(!d->data_fn) { + return -EINVAL; + } + + size_read = d->data_fn(buffer, (i->inode >> 12) & 0xFFFF); + return size_read; +} + +int procfs_followlink(struct inode *dir, struct inode *i, struct inode **i_res) +{ + __ino_t errno; + __pid_t pid; + struct proc *p; + + if(!i) { + return -ENOENT; + } + if(!(S_ISLNK(i->i_mode))) { + printk("%s(): Oops, inode '%d' is not a symlink (!?).\n", __FUNCTION__, i->inode); + return 0; + } + + p = NULL; + if((pid = (i->inode >> 12) & 0xFFFF)) { + if(!(p = get_proc_by_pid(pid))) { + return -ENOENT; + } + } + + /* FIXME! + if(p && p->root) { + printk("(pid %d) p->root->inode = %d (count = %d)\n", p->pid, p->root->inode, p->root->count); + } + */ + + switch(i->inode & 0xF0000FFF) { + case PROC_PID_CWD: + if(!p->pwd) { + return -ENOENT; + } + *i_res = p->pwd; + p->pwd->count++; + iput(i); + break; + case PROC_PID_EXE: + if(!p->vma || !p->vma->inode) { + return -ENOENT; + } + *i_res = p->vma->inode; + p->vma->inode->count++; + iput(i); + break; + case PROC_PID_ROOT: + if(!p->root) { + return -ENOENT; + } + *i_res = p->root; + p->root->count++; + iput(i); + break; + default: + iput(i); + if((errno = parse_namei(current->pidstr, dir, i_res, NULL, FOLLOW_LINKS))) { + return errno; + } + } + return 0; +} diff --git a/fs/procfs/tree.c b/fs/procfs/tree.c new file mode 100644 index 00000000..d90b9c75 --- /dev/null +++ b/fs/procfs/tree.c @@ -0,0 +1,114 @@ +/* + * fiwix/fs/procfs/tree.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DIR S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | \ + S_IXOTH /* dr-xr-xr-x */ +#define REG S_IFREG | S_IRUSR | S_IRGRP | S_IROTH /* -r--r--r-- */ +#define REGUSR S_IFREG | S_IRUSR /* -r-------- */ +#define LNK S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO /* lrwxrwxrwx */ +#define LNKPID S_IFLNK | S_IRWXU /* lrwx------ */ + +/* + * WARNING: every time a new entry is added to this array you must also change + * the PROC_ARRAY_ENTRIES value defined in fs_proc.h. + */ +struct procfs_dir_entry procfs_array[][PROC_ARRAY_ENTRIES + 1] = { + { /* [0] / */ + { 1, DIR, 2, 0, 1, ".", NULL }, + { 2, DIR, 2, 0, 2, "..", NULL }, + { 3, DIR, 3, 3, 3, "sys", NULL }, + { 4, REG, 1, 0, 7, "cmdline", data_proc_cmdline }, + { 5, REG, 1, 0, 7, "cpuinfo", data_proc_cpuinfo }, + { 6, REG, 1, 0, 7, "devices", data_proc_devices }, + { 7, REG, 1, 0, 3, "dma", data_proc_dma }, + { 8, REG, 1, 0, 11, "filesystems", data_proc_filesystems }, + { 9, REG, 1, 0, 10, "interrupts", data_proc_interrupts }, + { 10, REG, 1, 0, 7, "loadavg", data_proc_loadavg }, + { 11, REG, 1, 0, 5, "locks", data_proc_locks }, + { 12, REG, 1, 0, 7, "meminfo", data_proc_meminfo }, + { 13, REG, 1, 0, 6, "mounts", data_proc_mounts }, + { 14, REG, 1, 0, 10, "partitions", data_proc_partitions }, + { 15, REG, 1, 0, 3, "rtc", data_proc_rtc }, + { 16, LNK, 1, 0, 4, "self", data_proc_self }, + { 17, REG, 1, 0, 4, "stat", data_proc_stat }, + { 18, REG, 1, 0, 6, "uptime", data_proc_uptime }, + { 19, REG, 1, 0, 7, "version", data_proc_fullversion }, + { 0, 0, 0, 0, 0, NULL, NULL } + }, + { /* [1] /PID/ */ + { 1000, DIR, 2, 1, 1, ".", NULL }, + { 1, DIR, 2, 0, 2, "..", NULL }, +/* { PROC_PID_FD, DIR, 2, 2, 2, "fd", data_proc_pid_fd },*/ + { PROC_PID_CMDLINE, REG, 1, 1, 7, "cmdline", data_proc_pid_cmdline }, + { PROC_PID_CWD, LNKPID, 1, 1, 3, "cwd", data_proc_pid_cwd }, + { PROC_PID_ENVIRON, REGUSR, 1, 1, 7, "environ", data_proc_pid_environ }, + { PROC_PID_EXE, LNKPID, 1, 1, 3, "exe", data_proc_pid_exe }, + { PROC_PID_MAPS, REG, 1, 1, 4, "maps", data_proc_pid_maps }, + { PROC_PID_MOUNTINFO,REG, 1, 1, 9, "mountinfo",data_proc_pid_mountinfo }, + { PROC_PID_ROOT, LNKPID, 1, 1, 4, "root", data_proc_pid_root }, + { PROC_PID_STAT, REG, 1, 1, 4, "stat", data_proc_pid_stat }, + { PROC_PID_STATUS, REG, 1, 1, 6, "status", data_proc_pid_status }, + { 0, 0, 0, 0, 0, NULL, NULL } + }, + + { + }, + + { /* [3] /sys/ */ + { 3, DIR, 2, 3, 1, ".", NULL }, + { 1, DIR, 2, 0, 2, "..", NULL }, + { 2001, DIR, 2, 4, 6, "kernel", NULL }, + { 0, 0, 0, 0, 0, NULL, NULL } + }, + { /* [4] /sys/kernel/ */ + { 2001, DIR, 2, 4, 1, ".", NULL }, + { 3, DIR, 2, 3, 2, "..", NULL }, + { 3001, REG, 1, 4, 10, "domainname", data_proc_domainname }, + { 3002, REG, 1, 4, 8, "file-max", data_proc_filemax }, + { 3003, REG, 1, 4, 7, "file-nr", data_proc_filenr }, + { 3004, REG, 1, 4, 8, "hostname", data_proc_hostname }, + { 3005, REG, 1, 4, 9, "inode-max", data_proc_inodemax }, + { 3006, REG, 1, 4, 8, "inode-nr", data_proc_inodenr }, + { 3007, REG, 1, 4, 9, "osrelease", data_proc_osrelease }, + { 3008, REG, 1, 4, 6, "ostype", data_proc_ostype }, + { 3009, REG, 1, 4, 7, "version", data_proc_version }, + { 0, 0, 0, 0, 0, NULL, NULL } + } +}; + +struct procfs_dir_entry * get_procfs_by_inode(struct inode *i) +{ + __ino_t inode; + int n, lev; + struct procfs_dir_entry *d; + + inode = i->inode; + for(lev = 0; procfs_array[lev]; lev++) { + if(lev == PROC_PID_LEV) { /* PID entries */ + if((i->inode & 0xF0000000) == PROC_PID_INO) { + inode = i->inode & 0xF0000FFF; + } + } + d = procfs_array[lev]; + for(n = 0; n < PROC_ARRAY_ENTRIES && d->inode; n++) { + if(d->inode == inode) { + return d; + } + d++; + } + } + + return NULL; +} diff --git a/fs/super.c b/fs/super.c new file mode 100644 index 00000000..8c77f96e --- /dev/null +++ b/fs/super.c @@ -0,0 +1,223 @@ +/* + * fiwix/fs/super.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct mount *mount_table; +static struct resource sync_resource = { NULL, NULL }; + +void superblock_lock(struct superblock *sb) +{ + unsigned long int flags; + + for(;;) { + SAVE_FLAGS(flags); CLI(); + if(sb->locked) { + sb->wanted = 1; + RESTORE_FLAGS(flags); + sleep(&superblock_lock, PROC_UNINTERRUPTIBLE); + } else { + break; + } + } + sb->locked = 1; + RESTORE_FLAGS(flags); +} + +void superblock_unlock(struct superblock *sb) +{ + unsigned long int flags; + + SAVE_FLAGS(flags); CLI(); + sb->locked = 0; + if(sb->wanted) { + sb->wanted = 0; + wakeup(&superblock_lock); + } + RESTORE_FLAGS(flags); +} + +struct mount * get_free_mount_point(__dev_t dev) +{ + unsigned long int flags; + int n; + + if(!dev) { + printk("%s(): invalid device %d,%d.\n", __FUNCTION__, MAJOR(dev), MINOR(dev)); + return NULL; + } + + for(n = 0; n < NR_MOUNT_POINTS; n++) { + if(mount_table[n].dev == dev) { + printk("%s(): device %d,%d already mounted.\n", __FUNCTION__, MAJOR(dev), MINOR(dev)); + return NULL; + } + } + + SAVE_FLAGS(flags); CLI(); + for(n = 0; n < NR_MOUNT_POINTS; n++) { + if(!mount_table[n].used) { + /* 'dev' is saved here now for get_superblock() (which + * in turn is called by read_inode(), which in turn is + * called by iget(), which in turn is called by + * read_superblock) to be able to find the device. + */ + mount_table[n].dev = dev; + mount_table[n].used = 1; + RESTORE_FLAGS(flags); + return &mount_table[n]; + } + } + RESTORE_FLAGS(flags); + + printk("WARNING: %s(): mount-point table is full.\n", __FUNCTION__); + return NULL; +} + +void release_mount_point(struct mount *mt) +{ + memset_b(mt, NULL, sizeof(struct mount)); +} + +struct mount * get_mount_point(struct inode *i_target) +{ + int n; + + for(n = 0; n < NR_MOUNT_POINTS; n++) { + if(mount_table[n].used) { + if(S_ISDIR(i_target->i_mode)) { + if(mount_table[n].sb.root == i_target) { + return &mount_table[n]; + } + } + if(S_ISBLK(i_target->i_mode)) { + if(mount_table[n].dev == i_target->rdev) { + return &mount_table[n]; + } + } + } + } + return NULL; +} + +struct superblock * get_superblock(__dev_t dev) +{ + int n; + + for(n = 0; n < NR_MOUNT_POINTS; n++) { + if(mount_table[n].used && mount_table[n].dev == dev) { + return &mount_table[n].sb; + } + } + return NULL; +} + +void sync_superblocks(__dev_t dev) +{ + struct superblock *sb; + int n, errno; + + lock_resource(&sync_resource); + for(n = 0; n < NR_MOUNT_POINTS; n++) { + if(mount_table[n].used && (!dev || mount_table[n].dev == dev)) { + sb = &mount_table[n].sb; + if(sb->dirty && !(sb->flags & MS_RDONLY)) { + if(sb->fsop && sb->fsop->write_superblock) { + errno = sb->fsop->write_superblock(sb); + if(errno) { + printk("WARNING: %s(): I/O error on device %d,%d while syncing superblock.\n", __FUNCTION__, MAJOR(sb->dev), MINOR(sb->dev)); + } + } + } + } + } + unlock_resource(&sync_resource); +} + +/* pseudo-filesystems are only mountable by the kernel */ +int kern_mount(__dev_t dev, struct filesystems *fs) +{ + struct mount *mt; + + if(!(mt = get_free_mount_point(dev))) { + return -EBUSY; + } + + if(fs->fsop->read_superblock(dev, &mt->sb)) { + release_mount_point(mt); + return -EINVAL; + } + + mt->dev = dev; + strcpy(mt->devname, "none"); + strcpy(mt->dirname, "none"); + mt->sb.dir = NULL; + mt->fs = fs; + fs->mt = mt; + return 0; +} + +int mount_root(void) +{ + struct filesystems *fs; + struct mount *mt; + + /* FIXME: before trying to mount the filesystem, we should first + * check if '_rootdev' is a device successfully registered. + */ + + if(!(fs = get_filesystem(_rootfstype))) { + printk("WARNING: %s(): '%s' is not a registered filesystem. Defaulting to 'minix'.\n", __FUNCTION__, _rootfstype); + if(!(fs = get_filesystem("minix"))) { + PANIC("minix filesystem is not registered!\n"); + } + } + + if(!(mt = get_free_mount_point(_rootdev))) { + PANIC("unable to get a free mount point.\n"); + } + + mt->sb.flags = MS_RDONLY; + if(fs->fsop && fs->fsop->read_superblock) { + if(fs->fsop->read_superblock(_rootdev, &mt->sb)) { + PANIC("unable to mount root filesystem on %s.\n", _rootdevname); + } + } + + strcpy(mt->devname, "/dev/root"); + strcpy(mt->dirname, "/"); + mt->dev = _rootdev; + mt->sb.root->mount_point = mt->sb.root; + mt->sb.root->count++; + mt->sb.dir = mt->sb.root; + mt->sb.dir->count++; + mt->fs = fs; + + current->root = mt->sb.root; + current->root->count++; + current->pwd = mt->sb.root; + current->pwd->count++; + iput(mt->sb.root); + + printk("mounted root device (%s filesystem) in readonly mode.\n", fs->name); + return 0; +} + +void mount_init(void) +{ + memset_b(mount_table, NULL, mount_table_size); +} diff --git a/include/fiwix/asm.h b/include/fiwix/asm.h new file mode 100644 index 00000000..65f2796c --- /dev/null +++ b/include/fiwix/asm.h @@ -0,0 +1,136 @@ +/* + * fiwix/include/fiwix/asm.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_ASM_H +#define _FIWIX_ASM_H + +extern void except0(void); +extern void except1(void); +extern void except2(void); +extern void except3(void); +extern void except4(void); +extern void except5(void); +extern void except6(void); +extern void except7(void); +extern void except8(void); +extern void except9(void); +extern void exceptA(void); +extern void exceptB(void); +extern void exceptC(void); +extern void exceptD(void); +extern void exceptE(void); +extern void exceptF(void); +extern void except10(void); +extern void except11(void); +extern void except12(void); +extern void except13(void); +extern void except14(void); +extern void except15(void); +extern void except16(void); +extern void except17(void); +extern void except18(void); +extern void except19(void); +extern void except1A(void); +extern void except1B(void); +extern void except1C(void); +extern void except1D(void); +extern void except1E(void); +extern void except1F(void); + +extern void irq0(void); +extern void irq1(void); +extern void irq2(void); +extern void irq3(void); +extern void irq4(void); +extern void irq5(void); +extern void irq6(void); +extern void irq7(void); +extern void irq8(void); +extern void irq9(void); +extern void irq10(void); +extern void irq11(void); +extern void irq12(void); +extern void irq13(void); +extern void irq14(void); +extern void irq15(void); +extern void unknown_irq(void); + +extern void switch_to_user_mode(void); +extern void sighandler_trampoline(void); +extern void end_sighandler_trampoline(void); +extern void syscall(void); +extern void return_from_syscall(void); +extern void do_switch(unsigned int *, unsigned int *, unsigned int, unsigned int, unsigned int, unsigned short int); + +int cpuid(void); +int getfpu(void); +int vendor_id(void); +int signature_flags(void); +int brand_str(void); +int tlbinfo(void); + +unsigned char inport_b(unsigned int); +short int inport_w(unsigned int); +void inport_sw(unsigned int, void *, unsigned int); +void outport_b(unsigned int, unsigned char); +void outport_w(unsigned int, unsigned int); +void outport_sw(unsigned int, void *, unsigned int); + +void load_gdt(unsigned int); +void load_idt(unsigned int); +void activate_kpage_dir(void); +void load_tr(unsigned int); +unsigned long long int get_rdtsc(void); +void invalidate_tlb(void); + +#define CLI() __asm__ __volatile__ ("cli":::"memory") +#define STI() __asm__ __volatile__ ("sti":::"memory") +#define NOP() __asm__ __volatile__ ("nop":::"memory") +#define HLT() __asm__ __volatile__ ("hlt":::"memory") + +#define GET_CR2(cr2) __asm__ __volatile__ ("movl %%cr2, %0" : "=r" (cr2)); +#define GET_ESP(esp) __asm__ __volatile__ ("movl %%esp, %0" : "=r" (esp)); +#define SET_ESP(esp) __asm__ __volatile__ ("movl %0, %%esp" :: "r" (esp)); + +#define SAVE_FLAGS(flags) \ + __asm__ __volatile__( \ + "pushfl ; popl %0\n\t" \ + : "=r" (flags) \ + : /* no input */ \ + : "memory" \ + ); + +#define RESTORE_FLAGS(x) \ + __asm__ __volatile__( \ + "pushl %0 ; popfl\n\t" \ + : /* no output */ \ + : "r" (flags) \ + : "memory" \ + ); + +#define USER_SYSCALL(num, arg1, arg2, arg3) \ + __asm__ __volatile__( \ + "movl %0, %%eax\n\t" \ + "movl %1, %%ebx\n\t" \ + "movl %2, %%ecx\n\t" \ + "movl %3, %%edx\n\t" \ + "int $0x80\n\t" \ + : /* no output */ \ + : "eax"((unsigned int)num), "ebx"((unsigned int)arg1), "ecx"((unsigned int)arg2), "edx"((unsigned int)arg3) \ + ); + +/* +static inline unsigned long long int get_rdtsc(void) +{ + unsigned int eax, edx; + + __asm__ __volatile__("rdtsc" : "=a" (eax), "=d" (edx)); + return ((unsigned long long int)eax) | (((unsigned long long int)edx) << 32); +} +*/ + +#endif /* _FIWIX_ASM_H */ diff --git a/include/fiwix/bios.h b/include/fiwix/bios.h new file mode 100644 index 00000000..e68e527a --- /dev/null +++ b/include/fiwix/bios.h @@ -0,0 +1,29 @@ +/* + * fiwix/include/fiwix/bios.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_BIOS_H +#define _FIWIX_BIOS_H + +#include + +#define BIOS_MEM_AVAIL 1 /* BIOS memory available */ +#define BIOS_MEM_RES 2 /* BIOS memory reserved */ +#define BIOS_MEM_ACPI_REC 3 /* BIOS memory ACPI reclaim */ +#define BIOS_MEM_ACPI_NVS 4 /* BIOS memory ACPI NVS */ +#define NR_BIOS_MM_ENT 25 /* entries in BIOS memory map */ + +struct bios_mem_map { + unsigned long int from; + unsigned long int to; + unsigned long int type; +}; +struct bios_mem_map bios_mem_map[NR_BIOS_MM_ENT]; + +int addr_in_bios_map(unsigned int); +void bios_map_init(memory_map_t *, unsigned long int); + +#endif /* _FIWIX_BIOS_H */ diff --git a/include/fiwix/buffer.h b/include/fiwix/buffer.h new file mode 100644 index 00000000..ace638af --- /dev/null +++ b/include/fiwix/buffer.h @@ -0,0 +1,43 @@ +/* + * fiwix/include/fiwix/buffer.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_BUFFER_H +#define _FIWIX_BUFFER_H + +#include +#include + +struct buffer { + __dev_t dev; /* device number */ + __blk_t block; /* block number */ + int size; /* block size (in bytes) */ + char valid; /* 1 = valid */ + unsigned char locked; /* 1 = locked */ + unsigned char dirty; /* 1 = delayed write */ + char *data; /* block contents */ + struct buffer *prev_hash; + struct buffer *next_hash; + struct buffer *prev_free; + struct buffer *next_free; +}; +extern struct buffer *buffer_table; +extern struct buffer **buffer_hash_table; + +/* values to be determined during system startup */ +extern unsigned int buffer_table_size; /* size in bytes */ +extern unsigned int buffer_hash_table_size; /* size in bytes */ + +struct buffer * get_dirty_buffer(__dev_t, __blk_t, int); +struct buffer * bread(__dev_t, __blk_t, int); +void bwrite(struct buffer *); +void brelse(struct buffer *); +void sync_buffers(__dev_t); +void invalidate_buffers(__dev_t); +int reclaim_buffers(void); +void buffer_init(void); + +#endif /* _FIWIX_BUFFER_H */ diff --git a/include/fiwix/cmos.h b/include/fiwix/cmos.h new file mode 100644 index 00000000..18ec4dd8 --- /dev/null +++ b/include/fiwix/cmos.h @@ -0,0 +1,58 @@ +/* + * fiwix/include/fiwix/cmos.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_CMOS_H +#define _FIWIX_CMOS_H + +#define CMOS_INDEX 0x70 +#define CMOS_DATA 0x71 + +#define CMOS_STATA_IRQF 0x0F /* periodic interrupt frequency */ +#define CMOS_STATA_UIP 0x80 /* time update in progress */ + +#define CMOS_STATB_DSE 0x01 /* enable daylight savings */ +#define CMOS_STATB_24H 0x02 /* 24-hour mode (0=12h, 1=24h) */ +#define CMOS_STATB_DM 0x04 /* time/date in binary mode (0=BCD, 1=binary) */ +#define CMOS_STATB_SQWE 0x08 /* enable square wave frequency */ +#define CMOS_STATB_UIE 0x10 /* enable update-ended interrupt */ +#define CMOS_STATB_AIE 0x20 /* enable alarm interrupt */ +#define CMOS_STATB_PIE 0x40 /* enable periodic interrupt */ +#define CMOS_STATB_SET 0x80 /* abort clock update */ + +#define CMOS_STATD_VRT 0x80 /* valid RAM and time */ + +/* CMOS RAM data registers */ +#define CMOS_SEC 0x00 /* second */ +#define CMOS_ASEC 0x01 /* alarm second */ +#define CMOS_MIN 0x02 /* minute */ +#define CMOS_AMIN 0x03 /* alarm minute */ +#define CMOS_HOUR 0x04 /* hour */ +#define CMOS_AHOUR 0x05 /* alarm hour */ +#define CMOS_DOW 0x06 /* day of week */ +#define CMOS_DAY 0x07 /* day */ +#define CMOS_MONTH 0x08 /* month */ +#define CMOS_YEAR 0x09 /* last two digits of year */ +#define CMOS_STATA 0x0A /* status register A */ +#define CMOS_STATB 0x0B /* status register B */ +#define CMOS_STATC 0x0C /* status register C */ +#define CMOS_STATD 0x0D /* status register D */ +#define CMOS_DIAG 0x0E /* diagnostics status */ +#define CMOS_FDDTYPE 0x10 /* floppy disk drive type */ +#define CMOS_HDDTYPE 0x12 /* hard disk drive type */ +#define CMOS_CENTURY 0x32 /* century */ + +/* conversions */ +#define BCD2BIN(bcd) (((bcd) >> 4) * 10) + ((bcd) & 0x0F) +#define BIN2BCD(bin) ((bin) % 10) | (((bin) / 10) << 4) + +int cmos_update_in_progress(void); +unsigned char cmos_read_date(unsigned char); +void cmos_write_date(unsigned char, unsigned char); +unsigned char cmos_read(unsigned char); +void cmos_write(unsigned char, unsigned char); + +#endif /* _FIWIX_CMOS_H */ diff --git a/include/fiwix/config.h b/include/fiwix/config.h new file mode 100644 index 00000000..b6f9a740 --- /dev/null +++ b/include/fiwix/config.h @@ -0,0 +1,55 @@ +/* + * fiwix/include/fiwix/config.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_CONFIG_H +#define _FIWIX_CONFIG_H + +/* maximum number of processes */ +#define NR_PROCS 64 + +/* maximum number of callout functions (timer) */ +#define NR_CALLOUTS NR_PROCS + +/* maximum number of bottom halves in pool */ +#define NR_BH NR_PROCS + +/* maximum number of mounted filesystems */ +#define NR_MOUNT_POINTS 8 + +/* maximum number of opened files in system */ +#define NR_OPENS 1024 + +/* maximum number of flocks in system */ +#define NR_FLOCKS (NR_PROCS * 5) + + + +/* percentage of memory that buffer cache will borrow from available memory */ +#define BUFFER_PERCENTAGE 100 + +/* percentage of hash buckets relative to the size of the buffer table */ +#define BUFFER_HASH_PERCENTAGE 10 + +/* buffers reclaimed in a single call */ +#define NR_BUF_RECLAIM 150 + + +/* percentage of memory assigned to the inode table and hash table */ +#define INODE_PERCENTAGE 5 +#define INODE_HASH_PERCENTAGE 10 + +/* percentage of memory assigned to the page hash table */ +#define PAGE_HASH_PERCENTAGE 10 + + +/* maximum value for PID */ +#define MAX_PID_VALUE 32767 + +/* number of screens in console' scroll back */ +#define SCREENS_LOG 6 + +#endif /* _FIWIX_CONFIG_H */ diff --git a/include/fiwix/console.h b/include/fiwix/console.h new file mode 100644 index 00000000..c8fe5d40 --- /dev/null +++ b/include/fiwix/console.h @@ -0,0 +1,130 @@ +/* + * fiwix/include/fiwix/console.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_CONSOLE_H +#define _FIWIX_CONSOLE_H + +#include +#include +#include + +#define NR_VCONSOLES 12 /* number of virtual consoles */ + +#define VCONSOLES_MAJOR 4 /* virtual consoles major number */ +#define SYSCON_MAJOR 5 /* system console major number */ + +#define MONO_ADDR 0xB0000L +#define COLOR_ADDR 0xB8000L + +#define MONO_6845_ADDR 0x3B4 /* i/o address (+1 for data register) */ +#define COLOR_6845_ADDR 0x3D4 /* i/o address (+1 for data register) */ + +#define ATTR_CONTROLLER 0x3C0 /* attribute controller registrer */ +#define ATTR_CONTROLLER_PAS 0x20 /* palette address source */ +#define INPUT_STAT1 0x3DA /* input status #1 register */ +#define BLANK_INTERVAL (600 * HZ) /* 600 seconds (10 minutes) */ + +#define CRT_INDEX 0 +#define CRT_DATA 1 +#define CRT_CURSOR_STR 0xA +#define CRT_CURSOR_END 0xB +#define CRT_START_ADDR_HI 0xC +#define CRT_START_ADDR_LO 0xD +#define CRT_CURSOR_POS_HI 0xE +#define CRT_CURSOR_POS_LO 0xF + +#define CURSOR_MASK 0x1F +#define CURSOR_DISABLE 0x20 + +#define COLOR_NORMAL 0 +#define COLOR_BOLD 1 +#define COLOR_BOLD_OFF 2 +#define COLOR_UNDERLINE 4 +#define COLOR_BLINK 5 +#define COLOR_REVERSE 7 + +#define COLOR_BLACK 0x0000 +#define COLOR_BLUE 0x0100 +#define COLOR_GREEN 0x0200 +#define COLOR_CYAN 0x0300 +#define COLOR_RED 0x0400 +#define COLOR_MAGENTA 0x0500 +#define COLOR_BROWN 0x0600 +#define COLOR_WHITE 0x0700 +#define BG_BLACK 0x0000 +#define BG_BLUE 0x1000 +#define BG_GREEN 0x2000 +#define BG_CYAN 0x3000 +#define BG_RED 0x4000 +#define BG_MAGENTA 0x5000 +#define BG_BROWN 0x6000 +#define BG_WHITE 0x7000 + +#define DEF_MODE (COLOR_WHITE | BG_BLACK) +#define BLANK_MEM (DEF_MODE | ' ') + +#define SCREEN_COLS 80 +#define SCREEN_LINES 25 +#define SCREEN_SIZE (SCREEN_COLS * SCREEN_LINES * 2) + +#define TAB_SIZE 8 +#define BS 127 /* backspace */ + +#define MAX_TAB_COLS 132 /* maximum number of tab stops */ + +#define VC_BUF_LINES (SCREEN_LINES * SCREENS_LOG) +#define VC_BUF_SIZE (SCREEN_COLS * VC_BUF_LINES * 2) +#define VC_BUF_UP 1 +#define VC_BUF_DOWN 2 + +unsigned int video_port; +extern short int current_cons; /* current console (/dev/tty1 ... /dev/tty12) */ + +struct vconsole { + int x; /* current column */ + int y; /* current line */ + int lines, columns; + short int check_x; + unsigned char led_status; + unsigned char scrlock, numlock, capslock; + unsigned char esc, sbracket, semicolon, question; + int parmv1, parmv2; + unsigned short int color_attr; + unsigned char bold, underline, blink, reverse; + int insert_mode; + unsigned short int *vidmem; + short int has_focus; + unsigned short int scrbuf[SCREEN_SIZE / 2]; + int saved_x; + int saved_y; + char tab_stop[MAX_TAB_COLS]; + struct vt_mode vt_mode; + unsigned char vc_mode; + unsigned char blanked; + int switchto_tty; + struct tty *tty; +}; + +void vconsole_reset(struct tty *); +void vconsole_write(struct tty *); +void vconsole_select(int); +void vconsole_select_final(int); +void vconsole_save(struct vconsole *); +void vconsole_restore(struct vconsole *); +void vconsole_buffer_scrl(int); +void blank_screen(struct vconsole *); +void unblank_screen(struct vconsole *); +void screen_on(void); +void screen_off(unsigned int); +void vconsole_start(struct tty *); +void vconsole_stop(struct tty *); +void vconsole_beep(void); +void vconsole_deltab(struct tty *); +void console_flush_log_buf(char *, unsigned int); +void vconsole_init(void); + +#endif /* _FIWIX_CONSOLE_H */ diff --git a/include/fiwix/const.h b/include/fiwix/const.h new file mode 100644 index 00000000..e8d1d833 --- /dev/null +++ b/include/fiwix/const.h @@ -0,0 +1,20 @@ +/* + * fiwix/include/fiwix/const.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_CONST_H +#define _FIWIX_CONST_H + +#define KERNEL_BASE_ADDR 0xC0000000 +#define KERNEL_ENTRY_ADDR 0x100000 + +#define KERNEL_CS 0x08 /* kernel code segment */ +#define KERNEL_DS 0x10 /* kernel data segment */ +#define USER_CS 0x18 /* user code segment */ +#define USER_DS 0x20 /* user data segment */ +#define TSS 0x28 /* TSS segment */ + +#endif /* _FIWIX_CONST_H */ diff --git a/include/fiwix/cpu.h b/include/fiwix/cpu.h new file mode 100644 index 00000000..d53a8f9b --- /dev/null +++ b/include/fiwix/cpu.h @@ -0,0 +1,68 @@ +/* + * fiwix/include/fiwix/cpu.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_CPU_H +#define _FIWIX_CPU_H + +#define CPU_FPU 0x00000001 /* Floating-Point Unit on chip */ +#define CPU_VME 0x00000002 /* Virtual 8086 Mode Enhancements */ +#define CPU_DE 0x00000004 /* Debugging Extensions */ +#define CPU_PSE 0x00000008 /* Page Size Extension */ +#define CPU_TSC 0x00000010 /* Time Stamp Counter */ +#define CPU_MSR 0x00000020 /* Model Specific Registers */ +#define CPU_PAE 0x00000040 /* Physical Address Extension */ +#define CPU_MCE 0x00000080 /* Machine Check Exception */ +#define CPU_CX8 0x00000100 /* CMPXCHG8B instruction supported */ +#define CPU_APIC 0x00000200 /* On-chip APIC hardware supported */ +#define CPU_RES10 0x00000400 /* Reserved */ +#define CPU_SEP 0x00000800 /* Fast System Call */ +#define CPU_MTRR 0x00001000 /* Memory Type Range Registers */ +#define CPU_PGE 0x00002000 /* Page Global Enable */ +#define CPU_MCA 0x00004000 /* Machine Check Architecture */ +#define CPU_CMOV 0x00008000 /* Conditional Move Instruction */ +#define CPU_PAT 0x00010000 /* Page Attribute Table */ +#define CPU_PSE36 0x00020000 /* 36-bit Page Size Extension */ +#define CPU_PSN 0x00040000 /* Processor Serial Number */ +#define CPU_CLFSH 0x00080000 /* CLFLUSH instruction supported */ +#define CPU_RES20 0x00100000 /* Reserved */ +#define CPU_DS 0x00200000 /* Debug Store */ +#define CPU_ACPI 0x00400000 /* Thermal Monitor and others */ +#define CPU_MMX 0x00800000 /* Intel Architecture MMX Technology */ +#define CPU_FXSR 0x01000000 /* Fast Floating Point Save and Rest. */ +#define CPU_SSE 0x02000000 /* Streaming SIMD Extensions */ +#define CPU_SSE2 0x04000000 /* Streaming SIMD Extensions 2 */ +#define CPU_SS 0x08000000 /* Self-Snoop */ +#define CPU_HTT 0x10000000 /* Hyper-Threading Technology */ +#define CPU_TM 0x20000000 /* Thermal Monitor */ +#define CPU_RES30 0x40000000 /* Reserved */ +#define CPU_PBE 0x80000000 /* Pending Break Enable */ + +#define RESERVED_DESC 0x80000000 /* TLB descriptor reserved */ + +struct cpu { + char *vendor_id; + char family; + char model; + char *model_name; + char stepping; + unsigned long int hz; + char *cache; + char has_cpuid; + char has_fpu; + int flags; +}; +struct cpu cpu_table; + +struct cpu_type { + int cpu; + char *name[20]; +}; + +int get_cpu_flags(char *, int); +void cpu_init(void); + +#endif /* _FIWIX_CPU_H */ diff --git a/include/fiwix/ctype.h b/include/fiwix/ctype.h new file mode 100644 index 00000000..42c3b3b7 --- /dev/null +++ b/include/fiwix/ctype.h @@ -0,0 +1,38 @@ +/* + * fiwix/include/fiwix/ctype.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_CTYPE_H +#define _FIWIX_CTYPE_H + +#define _U 0x01 /* upper case */ +#define _L 0x02 /* lower case */ +#define _N 0x04 /* numeral (digit) */ +#define _S 0x08 /* spacing character */ +#define _P 0x10 /* punctuation */ +#define _C 0x20 /* control character */ +#define _X 0x40 /* hexadecimal */ +#define _B 0x80 /* blank */ + +extern unsigned char _ctype[]; + +#define ISALPHA(ch) ((_ctype + 1)[ch] & (_U | _L)) +#define ISUPPER(ch) ((_ctype + 1)[ch] & _U) +#define ISLOWER(ch) ((_ctype + 1)[ch] & _L) +#define ISDIGIT(ch) ((_ctype + 1)[ch] & _N) +#define ISALNUM(ch) ((_ctype + 1)[ch] & (_U | _L | _N)) +#define ISSPACE(ch) ((_ctype + 1)[ch] & _S) +#define ISPUNCT(ch) ((_ctype + 1)[ch] & _P) +#define ISCNTRL(ch) ((_ctype + 1)[ch] & _C) +#define ISXDIGIT(ch) ((_ctype + 1)[ch] & (_N | _X)) + +#define ISASCII(ch) ((unsigned) ch <= 0x7F) +#define TOASCII(ch) ((unsigned) ch & 0x7F) + +#define TOUPPER(ch) ((ch) & ~32) +#define TOLOWER(ch) ((ch) | 32) + +#endif /* _FIWIX_CTYPE_H */ diff --git a/include/fiwix/devices.h b/include/fiwix/devices.h new file mode 100644 index 00000000..d9557a8f --- /dev/null +++ b/include/fiwix/devices.h @@ -0,0 +1,49 @@ +/* + * fiwix/include/fiwix/devices.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_DEVICES_H +#define _FIWIX_DEVICES_H + +#include +#include + +#define NR_BLKDEV 128 /* maximum number of block devices */ +#define NR_CHRDEV 128 /* maximum number of char devices */ + +#define BLK_DEV 1 /* block device */ +#define CHR_DEV 2 /* character device */ + +#define SET_MINOR(minors, bit) ((minors[(bit) / 32]) |= (1 << ((bit) % 32))) +#define CLEAR_MINOR(minors, bit) ((minors[(bit) / 32]) &= ~(1 << ((bit) % 32))) +#define TEST_MINOR(minors, bit) ((minors[(bit) / 32]) & (1 << ((bit) % 32))) + +struct device { + char *name; + int irq; + unsigned char major; + unsigned int minors[8]; /* bitmap of 256 bits */ + int blksize; + void *device_data; /* mostly used for minor sizes in KB */ + struct fs_operations *fsop; +}; + +extern struct device chr_device_table[NR_CHRDEV]; +extern struct device blk_device_table[NR_BLKDEV]; + +int register_device(int, struct device *); +struct device * get_device(int, unsigned char); +int chr_dev_open(struct inode *, struct fd *); +int blk_dev_open(struct inode *, struct fd *); +int blk_dev_close(struct inode *, struct fd *); +int blk_dev_read(struct inode *, struct fd *, char *, __size_t); +int blk_dev_write(struct inode *, struct fd *, const char *, __size_t); +int blk_dev_ioctl(struct inode *, int, unsigned long int); +int blk_dev_lseek(struct inode *, __off_t); + +void dev_init(void); + +#endif /* _FIWIX_DEVICES_H */ diff --git a/include/fiwix/dirent.h b/include/fiwix/dirent.h new file mode 100644 index 00000000..a4010dee --- /dev/null +++ b/include/fiwix/dirent.h @@ -0,0 +1,21 @@ +/* + * fiwix/include/fiwix/dirent.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_DIRENT_H +#define _FIWIX_DIRENT_H + +#include +#include + +struct dirent { + __ino_t d_ino; /* inode number */ + __off_t d_off; /* offset to next dirent */ + unsigned short int d_reclen; /* length of this dirent */ + char d_name[NAME_MAX + 1]; /* file name (null-terminated) */ +}; + +#endif /* _FIWIX_DIRENT_H */ diff --git a/include/fiwix/dma.h b/include/fiwix/dma.h new file mode 100644 index 00000000..3728ec33 --- /dev/null +++ b/include/fiwix/dma.h @@ -0,0 +1,33 @@ +/* + * fiwix/include/fiwix/dma.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_DMA_H +#define _FIWIX_DMA_H + +#define DMA_CHANNELS 8 /* max. number of DMA channels */ + +#define DMA_MASK_CHANNEL 0x04 +#define DMA_UNMASK_CHANNEL 0x00 + +#define DMA_MODE_VERIFY 0x00 +#define DMA_MODE_WRITE 0x04 /* read device -> write memory */ +#define DMA_MODE_READ 0x08 /* read memory -> write device */ +#define DMA_MODE_AUTOINIT 0x10 +#define DMA_MODE_ADDRES_DEC 0x20 +#define DMA_MODE_DEMAND 0x00 +#define DMA_MODE_SINGLE 0x40 +#define DMA_MODE_BLOCK 0x80 +#define DMA_MODE_CASCADE 0xC0 + +char *dma_resources[DMA_CHANNELS]; + +void start_dma(int, void *, unsigned int, int); +int dma_register(int, char *); +int dma_unregister(int); +void dma_init(void); + +#endif /* _FIWIX_DMA_H */ diff --git a/include/fiwix/errno.h b/include/fiwix/errno.h new file mode 100644 index 00000000..49c8d8b7 --- /dev/null +++ b/include/fiwix/errno.h @@ -0,0 +1,132 @@ +#ifndef _FIWIX_ERRNO_H +#define _FIWIX_ERRNO_H + +#define EPERM 1 /* Operation not permitted - Not owner */ +#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 /* Arg 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 - No more processes */ +#define ENOMEM 12 /* Out of memory - No enough space */ +#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 EDEADLOCK EDEADLK + +#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 */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale NFS file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ + +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ + +#endif /* _FIWIX_ERRNO_H */ diff --git a/include/fiwix/fcntl.h b/include/fiwix/fcntl.h new file mode 100644 index 00000000..a1217be8 --- /dev/null +++ b/include/fiwix/fcntl.h @@ -0,0 +1,67 @@ +/* + * fiwix/include/fiwix/fcntl.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_FCNTL_H +#define _FIWIX_FCNTL_H + +#include + +#define O_ACCMODE 0003 +#define O_RDONLY 00 +#define O_WRONLY 01 +#define O_RDWR 02 + +/* for open() only */ +#define O_CREAT 0100 /* create file if it does not exist */ +#define O_EXCL 0200 /* exclusive use flag */ +#define O_NOCTTY 0400 /* do not assign controlling terminal */ +#define O_TRUNC 01000 /* truncate flag */ +#define O_NOFOLLOW 0400000 /* do not follow symbolic links */ + +#define O_APPEND 02000 +#define O_NONBLOCK 04000 +#define O_NDELAY O_NONBLOCK +#define O_SYNC 010000 + +#define F_DUPFD 0 /* duplicate file descriptor */ +#define F_GETFD 1 /* get file descriptor flags */ +#define F_SETFD 2 /* set file descriptor flags */ +#define F_GETFL 3 /* get status flags and file access modes */ +#define F_SETFL 4 /* set file status flags */ +#define F_GETLK 5 /* get record locking information */ +#define F_SETLK 6 /* set record locking information */ +#define F_SETLKW 7 /* same as F_SETLK; wait if blocked */ + +/* get/set process or process group ID to receive SIGURG signals */ +#define F_SETOWN 8 /* for sockets only */ +#define F_GETOWN 9 /* for sockets only */ + +/* for F_[GET|SET]FL */ +#define FD_CLOEXEC 1 /* close the file descriptor upon exec() */ + +/* for POSIX fcntl() */ +#define F_RDLCK 0 /* shared or read lock */ +#define F_WRLCK 1 /* exclusive or write lock */ +#define F_UNLCK 2 /* unlock */ + +/* for BSD flock() */ +#define LOCK_SH 1 /* shared lock */ +#define LOCK_EX 2 /* exclusive lock */ +#define LOCK_NB 4 /* or'd with one of the above to prevent + blocking */ +#define LOCK_UN 8 /* unlock */ + +/* IEEE Std 1003.1, 2004 Edition */ +struct flock { + short int l_type; /* type of lock: F_RDLCK, F_WRLCK, F_UNLCK */ + short int l_whence; /* flag for 'l_start': SEEK_SET, SEEK_CUR, ...*/ + __off_t l_start; /* relative offset in bytes */ + __off_t l_len; /* size; if 0 then until EOF */ + __pid_t l_pid; /* PID holding the lock; returned in F_GETLK */ +}; + +#endif /* _FIWIX_FCNTL_H */ diff --git a/include/fiwix/filesystems.h b/include/fiwix/filesystems.h new file mode 100644 index 00000000..c59b4996 --- /dev/null +++ b/include/fiwix/filesystems.h @@ -0,0 +1,151 @@ +/* + * fiwix/include/fiwix/filesystems.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_FILESYSTEMS_H +#define _FIWIX_FILESYSTEMS_H + +#include +#include + +#define NR_FILESYSTEMS 5 /* supported filesystems */ + +/* value to be determined during system startup */ +extern unsigned int mount_table_size; /* size in bytes */ + +struct filesystems { + const char *name; /* filesystem name */ + struct fs_operations *fsop; /* filesystem operations */ + struct mount *mt; /* mount-table entry (only for nodev) */ +}; +struct filesystems filesystems_table[NR_FILESYSTEMS]; + +struct mount { + __dev_t dev; /* device number */ + char devname[DEVNAME_MAX + 1]; /* device name */ + char dirname[NAME_MAX + 1]; /* mount point directory name */ + unsigned char used; /* 1=busy, 0=free */ + struct superblock sb; /* superblock */ + struct filesystems *fs; /* pointer to filesystem structure */ +}; +extern struct mount *mount_table; + +int register_filesystem(const char *, struct fs_operations *); +struct filesystems * get_filesystem(const char *); +void fs_init(void); + +struct superblock * get_superblock(__dev_t); +void sync_superblocks(__dev_t); +int kern_mount(__dev_t, struct filesystems *); +int mount_root(void); +void mount_init(void); + + +/* minix prototypes */ +int minix_file_open(struct inode *, struct fd *); +int minix_file_close(struct inode *, struct fd *); +int minix_file_write(struct inode *, struct fd *, const char *, __size_t); +int minix_file_lseek(struct inode *, __off_t); +int minix_dir_open(struct inode *, struct fd *); +int minix_dir_close(struct inode *, struct fd *); +int minix_dir_read(struct inode *, struct fd *, char *, __size_t); +int minix_dir_write(struct inode *, struct fd *, const char *, __size_t); +int minix_dir_readdir(struct inode *, struct fd *, struct dirent *, unsigned int); +int minix_readlink(struct inode *, char *, __size_t); +int minix_followlink(struct inode *, struct inode *, struct inode **); +int minix_bmap(struct inode *, __off_t, int); +int minix_lookup(const char *, struct inode *, struct inode **); +int minix_rmdir(struct inode *, struct inode *); +int minix_link(struct inode *, struct inode *, char *); +int minix_unlink(struct inode *, struct inode *, char *); +int minix_symlink(struct inode *, char *, char *); +int minix_mkdir(struct inode *, char *, __mode_t); +int minix_mknod(struct inode *, char *, __mode_t, __dev_t); +int minix_truncate(struct inode *, __off_t); +int minix_create(struct inode *, char *, __mode_t, struct inode **); +int minix_rename(struct inode *, struct inode *, struct inode *, struct inode *, char *, char *); +int minix_read_inode(struct inode *); +int minix_write_inode(struct inode *); +int minix_ialloc(struct inode *); +void minix_ifree(struct inode *); +void minix_statfs(struct superblock *, struct statfs *); +int minix_read_superblock(__dev_t, struct superblock *); +int minix_remount_fs(struct superblock *, int); +int minix_write_superblock(struct superblock *); +void minix_release_superblock(struct superblock *); +int minix_init(void); + + +/* ext2 prototypes */ +int ext2_file_open(struct inode *, struct fd *); +int ext2_file_close(struct inode *, struct fd *); +int ext2_file_lseek(struct inode *, __off_t); +int ext2_dir_open(struct inode *, struct fd *); +int ext2_dir_close(struct inode *, struct fd *); +int ext2_dir_read(struct inode *, struct fd *, char *, __size_t); +int ext2_dir_readdir(struct inode *, struct fd *, struct dirent *, unsigned int); +int ext2_readlink(struct inode *, char *, __size_t); +int ext2_followlink(struct inode *, struct inode *, struct inode **); +int ext2_bmap(struct inode *, __off_t, int); +int ext2_lookup(const char *, struct inode *, struct inode **); +int ext2_read_inode(struct inode *); +void ext2_statfs(struct superblock *, struct statfs *); +int ext2_read_superblock(__dev_t, struct superblock *); +int ext2_init(void); + + +/* pipefs prototypes */ +int fifo_open(struct inode *, struct fd *); +int pipefs_close(struct inode *, struct fd *); +int pipefs_read(struct inode *, struct fd *, char *, __size_t); +int pipefs_write(struct inode *, struct fd *, const char *, __size_t); +int pipefs_ioctl(struct inode *, int, unsigned long int); +int pipefs_lseek(struct inode *, __off_t); +int pipefs_select(struct inode *, int); +int pipefs_ialloc(struct inode *); +void pipefs_ifree(struct inode *); +int pipefs_read_superblock(__dev_t, struct superblock *); +int pipefs_init(void); + + +/* iso9660 prototypes */ +int iso9660_file_open(struct inode *, struct fd *); +int iso9660_file_close(struct inode *, struct fd *); +int iso9660_file_lseek(struct inode *, __off_t); +int iso9660_dir_open(struct inode *, struct fd *); +int iso9660_dir_close(struct inode *, struct fd *); +int iso9660_dir_read(struct inode *, struct fd *, char *, __size_t); +int iso9660_dir_readdir(struct inode *, struct fd *, struct dirent *, unsigned int); +int iso9660_readlink(struct inode *, char *, __size_t); +int iso9660_followlink(struct inode *, struct inode *, struct inode **); +int iso9660_bmap(struct inode *, __off_t, int); +int iso9660_lookup(const char *, struct inode *, struct inode **); +int iso9660_read_inode(struct inode *); +void iso9660_statfs(struct superblock *, struct statfs *); +int iso9660_read_superblock(__dev_t, struct superblock *); +void iso9660_release_superblock(struct superblock *); +int iso9660_init(void); + + +/* procfs prototypes */ +int procfs_file_open(struct inode *, struct fd *); +int procfs_file_close(struct inode *, struct fd *); +int procfs_file_read(struct inode *, struct fd *, char *, __size_t); +int procfs_file_lseek(struct inode *, __off_t); +int procfs_dir_open(struct inode *, struct fd *); +int procfs_dir_close(struct inode *, struct fd *); +int procfs_dir_read(struct inode *, struct fd *, char *, __size_t); +int procfs_dir_readdir(struct inode *, struct fd *, struct dirent *, unsigned int); +int procfs_readlink(struct inode *, char *, __size_t); +int procfs_followlink(struct inode *, struct inode *, struct inode **); +int procfs_bmap(struct inode *, __off_t, int); +int procfs_lookup(const char *, struct inode *, struct inode **); +int procfs_read_inode(struct inode *); +void procfs_statfs(struct superblock *, struct statfs *); +int procfs_read_superblock(__dev_t, struct superblock *); +int procfs_init(void); + +#endif /* _FIWIX_FILESYSTEMS_H */ diff --git a/include/fiwix/floppy.h b/include/fiwix/floppy.h new file mode 100644 index 00000000..1ac32190 --- /dev/null +++ b/include/fiwix/floppy.h @@ -0,0 +1,100 @@ +/* + * fiwix/include/fiwix/floppy.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_FLOPPY_H +#define _FIWIX_FLOPPY_H + +#include + +#define FLOPPY_IRQ 6 +#define FLOPPY_DMA 2 /* DMA channel */ + +#define FDC_MAJOR 2 /* fdd major number */ + +#define FDC_SECTSIZE 512 /* sector size (in bytes) */ +#define FDC_TR_DEFAULT 0 /* timer reason is IRQ */ +#define FDC_TR_MOTOR 1 /* timer reason is motor on */ + +#define FDC_SRA 0x3F0 /* Status Register A */ +#define FDC_SRB 0x3F1 /* Status Register B */ +#define FDC_DOR 0x3F2 /* Digital Output Register */ +#define FDC_MSR 0x3F4 /* Main Status Register */ +#define FDC_DATA 0x3F5 /* command/data register */ +#define FDC_DIR 0x3F7 /* Digital Input Register */ +#define FDC_CCR 0x3F7 /* Configuration Control Register */ + +#define FDC_ENABLE 0x04 /* bit #2 FDC enabled (normal op) */ +#define FDC_DMA_ENABLE 0x08 /* bit #3 DMA enabled */ +#define FDC_DRIVE0 0x10 /* motor on for the first drive, the rest will + * be calculated by left-shifting this value + * with 'current_fdd'. + */ + +#define FDC_DIO 0x40 /* bit #6 DIO I/O direction */ +#define FDC_RQM 0x80 /* bit #7 RQM is ready for I/O */ + +#define MAX_FDC_RESULTS 7 +#define MAX_FDC_ERR 5 + +#define FDC_RESET 0xFF /* reset indicador */ +#define FDC_READ 0xE6 +#define FDC_WRITE 0xC5 +#define FDC_VERSION 0x10 +#define FDC_FORMAT_TRK 0x4D +#define FDC_RECALIBRATE 0x07 +#define FDC_SENSEI 0x08 +#define FDC_SPECIFY 0x03 +#define FDC_SEEK 0x0F +#define FDC_LOCK 0x14 +#define FDC_PARTID 0x18 + +#define ST0 0x00 /* Status Register 0 */ +#define ST1 0x01 /* Status Register 1 */ +#define ST2 0x02 /* Status Register 2 */ + +#define ST0_IC 0xC0 /* bits #7 and #6 interrupt code */ +#define ST0_SE 0x20 /* bit #5 successful implied seek */ +#define ST0_RECALIBRATE ST0_SE /* bit #5 successful FDC_RECALIBRATE */ +#define ST0_SEEK ST0_SE /* bit #5 successful FDC_SEEK */ +#define ST0_UC 0x10 /* bit #4 unit needs check (fault) */ +#define ST0_NR 0x8 /* bit #3 drive not ready */ + +#define ST1_NW 0x02 /* bit #1 not writable */ + +#define ST_PCN 0x01 /* present cylinder */ +#define ST_CYL 0x03 /* cylinder returned */ +#define ST_HEAD 0x04 /* head returned */ +#define ST_SECTOR 0x05 /* sector returned */ + +/* floppy disk drive type */ +struct fddt { + short int size; /* number of sectors */ + short int sizekb; /* size in KB */ + char tracks; /* number of tracks */ + char spt; /* number of sectors per track */ + char heads; /* number of heads */ + char gpl1; /* GAP in READ/WRITE operations */ + char gpl2; /* GAP in FORMAT TRACK operations */ + char rate; /* data rate value */ + char spec; /* SRT+HUT (StepRate + HeadUnload) Time */ + char hlt; /* HLT (Head Load Time) */ + char *name; /* unit name */ +}; + +void irq_floppy(void); +void fdc_timer(unsigned int); + +int fdc_open(struct inode *, struct fd *); +int fdc_close(struct inode *, struct fd *); +int fdc_read(__dev_t, __blk_t, char *, int); +int fdc_write(__dev_t, __blk_t, char *, int); +int fdc_ioctl(struct inode *, int, unsigned long int); +int fdc_lseek(struct inode *, __off_t); + +void floppy_init(void); + +#endif /* _FIWIX_FLOPPY_H */ diff --git a/include/fiwix/fs.h b/include/fiwix/fs.h new file mode 100644 index 00000000..531b0f30 --- /dev/null +++ b/include/fiwix/fs.h @@ -0,0 +1,264 @@ +/* + * fiwix/include/fiwix/fs.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_FS_H +#define _FIWIX_FS_H + +#include +#include + +#define CHECK_UFD(ufd) \ +{ \ + if((ufd) > OPEN_MAX || current->fd[(ufd)] == 0) { \ + return -EBADF; \ + } \ +} \ + +struct fd { + struct inode *inode; /* file inode */ + unsigned short int flags; /* flags */ + unsigned short int count; /* number of opened instances */ + __off_t offset; /* r/w pointer position */ +}; + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BPS 512 /* bytes per sector */ +#define BLKSIZE_1K 1024 /* 1KB block size */ +#define BLKSIZE_2K 2048 /* 2KB block size */ +#define SUPERBLOCK 1 /* block 1 is for superblock */ + +#define MAJOR(dev) (((__dev_t) (dev)) >> 8) +#define MINOR(dev) (((__dev_t) (dev)) & 0xFF) +#define MKDEV(major, minor) (((major) << 8) | (minor)) + +/* filesystem independent mount-flags */ +#define MS_RDONLY 1 /* mount read-only */ +#define MS_REMOUNT 32 /* alter flags of a mounted FS */ + +/* old magic mount flag and mask */ +#define MS_MGC_VAL 0xC0ED0000 +#define MS_MGC_MSK 0xFFFF0000 + +#define IS_RDONLY_FS(inode) (((inode)->sb) && ((inode)->sb->flags & MS_RDONLY)) + +#define FOLLOW_LINKS 1 +#define MAX_SYMLINKS 8 /* this prevents infinite loops in symlinks */ + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +#define FOR_READING 0 +#define FOR_WRITING 1 + +#define VERIFY_READ 1 +#define VERIFY_WRITE 2 + +#define SEL_R 1 +#define SEL_W 2 +#define SEL_E 4 + +struct inode { + __mode_t i_mode; /* file mode */ + __uid_t i_uid; /* owner uid */ + __size_t i_size; /* size in bytes */ + __u32 i_atime; /* access time */ + __u32 i_ctime; /* creation time */ + __u32 i_mtime; /* modification time */ + __gid_t i_gid; /* group id */ + __nlink_t i_nlink; /* links count */ + __blk_t i_blocks; /* blocks count */ + __u32 i_flags; /* file flags */ + unsigned char locked; + unsigned char dirty; /* 1 = delayed write */ + struct inode *mount_point; + __dev_t dev; + __ino_t inode; + __s16 count; + __dev_t rdev; + struct fs_operations *fsop; + struct superblock *sb; + struct inode *prev_hash; + struct inode *next_hash; + struct inode *prev_free; + struct inode *next_free; + union { + struct minix_i_info minix; + struct ext2_i_info ext2; + struct pipefs_inode pipefs; + struct iso9660_inode iso9660; + struct procfs_inode procfs; + } u; +}; +extern struct inode *inode_table; +extern struct inode **inode_hash_table; +extern int inodes_on_free_list; + +/* values to be determined during system startup */ +extern unsigned int inode_table_size; /* size in bytes */ +extern unsigned int inode_hash_table_size; /* size in bytes */ +extern unsigned int fd_table_size; /* size in bytes */ + +extern struct fd *fd_table; + +struct superblock { + __dev_t dev; + unsigned char locked; + unsigned char wanted; + struct inode *root; /* root inode of mounted fs */ + struct inode *dir; /* inode on which the fs was mounted */ + unsigned int flags; + unsigned char dirty; /* 1 = delayed write */ + struct fs_operations *fsop; + __u32 s_blocksize; + union { + struct minix_sb_info minix; + struct ext2_super_block ext2; + struct iso9660_sb_info iso9660; + } u; +}; + + +#define FSOP_REQUIRES_DEV 1 /* requires a block device */ +#define FSOP_KERN_MOUNT 2 /* mounted by kernel */ + +struct fs_operations { + int flags; + int fsdev; /* internal filesystem (nodev) */ + +/* file operations */ + int (*open)(struct inode *, struct fd *); + int (*close)(struct inode *, struct fd *); + int (*read)(struct inode *, struct fd *, char *, __size_t); + int (*write)(struct inode *, struct fd *, const char *, __size_t); + int (*ioctl)(struct inode *, int, unsigned long int); + int (*lseek)(struct inode *, __off_t); + int (*readdir)(struct inode *, struct fd *, struct dirent *, unsigned int); + int (*mmap)(struct inode *, struct vma *); + int (*select)(struct inode *, int); + +/* inode operations */ + int (*readlink)(struct inode *, char *, __size_t); + int (*followlink)(struct inode *, struct inode *, struct inode **); + int (*bmap)(struct inode *, __off_t, int); + int (*lookup)(const char *, struct inode *, struct inode **); + int (*rmdir)(struct inode *, struct inode *); + int (*link)(struct inode *, struct inode *, char *); + int (*unlink)(struct inode *, struct inode *, char *); + int (*symlink)(struct inode *, char *, char *); + int (*mkdir)(struct inode *, char *, __mode_t); + int (*mknod)(struct inode *, char *, __mode_t, __dev_t); + int (*truncate)(struct inode *, __off_t); + int (*create)(struct inode *, char *, __mode_t, struct inode **); + int (*rename)(struct inode *, struct inode *, struct inode *, struct inode *, char *, char *); + +/* block device I/O operations */ + int (*read_block)(__dev_t, __blk_t, char *, int); + int (*write_block)(__dev_t, __blk_t, char *, int); + +/* superblock operations */ + int (*read_inode)(struct inode *); + int (*write_inode)(struct inode *); + int (*ialloc)(struct inode *); + void (*ifree)(struct inode *); + void (*statfs)(struct superblock *, struct statfs *); + int (*read_superblock)(__dev_t, struct superblock *); + int (*remount_fs)(struct superblock *, int); + int (*write_superblock)(struct superblock *); + void (*release_superblock)(struct superblock *); +}; + +extern struct fs_operations def_chr_fsop; +extern struct fs_operations def_blk_fsop; + +/* fs_minix.h prototypes */ +extern struct fs_operations minix_fsop; +extern struct fs_operations minix_file_fsop; +extern struct fs_operations minix_dir_fsop; +extern struct fs_operations minix_symlink_fsop; +extern int minix_count_free_inodes(struct superblock *); +extern int minix_count_free_blocks(struct superblock *); +extern int minix_find_first_zero(struct superblock *, __blk_t, int, int); +extern int minix_change_bit(int, struct superblock *, int, int); +extern void minix_bfree(struct superblock *, int); +extern int minix_balloc(struct superblock *); + +/* fs_ext2.h prototypes */ +extern struct fs_operations ext2_fsop; +extern struct fs_operations ext2_file_fsop; +extern struct fs_operations ext2_dir_fsop; +extern struct fs_operations ext2_symlink_fsop; + +/* fs_proc.h prototypes */ +extern struct fs_operations procfs_fsop; +extern struct fs_operations procfs_file_fsop; +extern struct fs_operations procfs_dir_fsop; +extern struct fs_operations procfs_symlink_fsop; +struct procfs_dir_entry * get_procfs_by_inode(struct inode *); + +/* fs_iso9660.h prototypes */ +extern int isonum_711(char *); +extern int isonum_723(char *); +extern int isonum_731(char *); +extern int isonum_733(char *); +extern unsigned long int isodate(char *); +extern int iso9660_cleanfilename(char *, int); +extern struct fs_operations iso9660_fsop; +extern struct fs_operations iso9660_file_fsop; +extern struct fs_operations iso9660_dir_fsop; +extern struct fs_operations iso9660_symlink_fsop; +void check_rrip_inode(struct iso9660_directory_record *, struct inode *); +int get_rrip_filename(struct iso9660_directory_record *, struct inode *, char *); +int get_rrip_symlink(struct inode *, char *); + + +/* generic VFS function prototypes */ +void inode_lock(struct inode *); +void inode_unlock(struct inode *); +struct inode * ialloc(struct superblock *); +struct inode * iget(struct superblock *, __ino_t); +int bmap(struct inode *, __off_t, int); +int check_fs_busy(__dev_t, struct inode *); +void iput(struct inode *); +void sync_inodes(__dev_t); +void invalidate_inodes(__dev_t); +void inode_init(void); + +int parse_namei(char *, struct inode *, struct inode **, struct inode **, int); +int namei(char *, struct inode **, struct inode **, int); + +void superblock_lock(struct superblock *); +void superblock_unlock(struct superblock *); +struct mount * get_free_mount_point(__dev_t); +void release_mount_point(struct mount *); +struct mount * get_mount_point(struct inode *); + +int elf_load(struct inode *, char **, char **, struct sigcontext *); + +int get_new_fd(struct inode *); +void release_fd(unsigned int); +void fd_init(void); + +void free_name(const char *); +int malloc_name(const char *, char **); +int check_user_permission(struct inode *); +int check_group(struct inode *); +int check_user_area(int, const void *, unsigned int); +int check_permission(int, struct inode *); + +int do_select(int, fd_set *, fd_set *, fd_set *, fd_set *, fd_set *, fd_set *); + +#endif /* _FIWIX_FS_H */ diff --git a/include/fiwix/fs_ext2.h b/include/fiwix/fs_ext2.h new file mode 100644 index 00000000..6be31877 --- /dev/null +++ b/include/fiwix/fs_ext2.h @@ -0,0 +1,232 @@ +/* + * fiwix/include/fiwix/fs_ext2.h + * + * This file from: Linux 2.0.40 + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#ifndef _FIWIX_FS_EXT2_H +#define _FIWIX_FS_EXT2_H + +#include + +#define EXT2_ROOT_INO 2 /* Root inode */ +#define EXT2_SUPER_MAGIC 0xEF53 + +/* + * Macro-instructions used to manage several block sizes + */ +#define EXT2_MIN_BLOCK_SIZE 1024 +#define EXT2_MAX_BLOCK_SIZE 4096 +#define EXT2_MIN_BLOCK_LOG_SIZE 10 +# define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize) +# define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits) + +/* + * Structure of a blocks group descriptor + */ +struct ext2_group_desc +{ + __u32 bg_block_bitmap; /* Blocks bitmap block */ + __u32 bg_inode_bitmap; /* Inodes bitmap block */ + __u32 bg_inode_table; /* Inodes table block */ + __u16 bg_free_blocks_count; /* Free blocks count */ + __u16 bg_free_inodes_count; /* Free inodes count */ + __u16 bg_used_dirs_count; /* Directories count */ + __u16 bg_pad; + __u32 bg_reserved[3]; +}; + +/* + * Macro-instructions used to manage group descriptors + */ +# define EXT2_BLOCKS_PER_GROUP(s) ((s)->u.ext2_sb.s_blocks_per_group) +# define EXT2_DESC_PER_BLOCK(s) ((s)->u.ext2_sb.s_desc_per_block) +# define EXT2_INODES_PER_GROUP(s) ((s)->u.ext2_sb.s_inodes_per_group) +# define EXT2_DESC_PER_BLOCK_BITS(s) ((s)->u.ext2_sb.s_desc_per_block_bits) + +/* + * Constants relative to the data blocks + */ +#define EXT2_NDIR_BLOCKS 12 +#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS +#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) +#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) +#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) + +/* + * Structure of an inode on the disk + */ +struct ext2_inode { + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Low 16 bits of Owner Uid */ + __u32 i_size; /* Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* Creation time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* Deletion Time */ + __u16 i_gid; /* Low 16 bits of Group Id */ + __u16 i_links_count; /* Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* File flags */ + union { + struct { + __u32 l_i_reserved1; + } linux1; + struct { + __u32 h_i_translator; + } hurd1; + struct { + __u32 m_i_reserved1; + } masix1; + } osd1; /* OS dependent 1 */ + __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ + __u32 i_generation; /* File version (for NFS) */ + __u32 i_file_acl; /* File ACL */ + __u32 i_dir_acl; /* Directory ACL */ + __u32 i_faddr; /* Fragment address */ + union { + struct { + __u8 l_i_frag; /* Fragment number */ + __u8 l_i_fsize; /* Fragment size */ + __u16 i_pad1; + __u16 l_i_uid_high; /* these 2 fields */ + __u16 l_i_gid_high; /* were reserved2[0] */ + __u32 l_i_reserved2; + } linux2; + struct { + __u8 h_i_frag; /* Fragment number */ + __u8 h_i_fsize; /* Fragment size */ + __u16 h_i_mode_high; + __u16 h_i_uid_high; + __u16 h_i_gid_high; + __u32 h_i_author; + } hurd2; + struct { + __u8 m_i_frag; /* Fragment number */ + __u8 m_i_fsize; /* Fragment size */ + __u16 m_pad1; + __u32 m_i_reserved2[2]; + } masix2; + } osd2; /* OS dependent 2 */ +}; + +/* + * File system states + */ +#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */ +#define EXT2_ERROR_FS 0x0002 /* Errors detected */ + +/* + * Structure of the super block + */ +struct ext2_super_block { + __u32 s_inodes_count; /* Inodes count */ + __u32 s_blocks_count; /* Blocks count */ + __u32 s_r_blocks_count; /* Reserved blocks count */ + __u32 s_free_blocks_count; /* Free blocks count */ + __u32 s_free_inodes_count; /* Free inodes count */ + __u32 s_first_data_block; /* First Data Block */ + __u32 s_log_block_size; /* Block size */ + __s32 s_log_frag_size; /* Fragment size */ + __u32 s_blocks_per_group; /* # Blocks per group */ + __u32 s_frags_per_group; /* # Fragments per group */ + __u32 s_inodes_per_group; /* # Inodes per group */ + __u32 s_mtime; /* Mount time */ + __u32 s_wtime; /* Write time */ + __u16 s_mnt_count; /* Mount count */ + __s16 s_max_mnt_count; /* Maximal mount count */ + __u16 s_magic; /* Magic signature */ + __u16 s_state; /* File system state */ + __u16 s_errors; /* Behaviour when detecting errors */ + __u16 s_minor_rev_level; /* minor revision level */ + __u32 s_lastcheck; /* time of last check */ + __u32 s_checkinterval; /* max. time between checks */ + __u32 s_creator_os; /* OS */ + __u32 s_rev_level; /* Revision level */ + __u16 s_def_resuid; /* Default uid for reserved blocks */ + __u16 s_def_resgid; /* Default gid for reserved blocks */ + /* + * These fields are for EXT2_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the filesystem. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + __u32 s_first_ino; /* First non-reserved inode */ + __u16 s_inode_size; /* size of inode structure */ + __u16 s_block_group_nr; /* block group # of this superblock */ + __u32 s_feature_compat; /* compatible feature set */ + __u32 s_feature_incompat; /* incompatible feature set */ + __u32 s_feature_ro_compat; /* readonly-compatible feature set */ + __u8 s_uuid[16]; /* 128-bit uuid for volume */ + char s_volume_name[16]; /* volume name */ + char s_last_mounted[64]; /* directory where last mounted */ + __u32 s_algorithm_usage_bitmap; /* For compression */ + /* + * Performance hints. Directory preallocation should only + * happen if the EXT2_COMPAT_PREALLOC flag is on. + */ + __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ + __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ + __u16 s_padding1; + /* + * Journaling support valid if EXT3_FEATURE_COMPAT_HAS_JOURNAL set. + */ + __u8 s_journal_uuid[16]; /* uuid of journal superblock */ + __u32 s_journal_inum; /* inode number of journal file */ + __u32 s_journal_dev; /* device number of journal file */ + __u32 s_last_orphan; /* start of list of inodes to delete */ + __u32 s_hash_seed[4]; /* HTREE hash seed */ + __u8 s_def_hash_version; /* Default hash version to use */ + __u8 s_reserved_char_pad; + __u16 s_reserved_word_pad; + __u32 s_default_mount_opts; + __u32 s_first_meta_bg; /* First metablock block group */ + __u32 s_reserved[190]; /* Padding to the end of the block */ +}; + +#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 + +/* + * Structure of a directory entry + */ +#define EXT2_NAME_LEN 255 + +struct ext2_dir_entry { + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u16 name_len; /* Name length */ + char name[EXT2_NAME_LEN]; /* File name */ +}; + +/* + * The new version of the directory entry. Since EXT2 structures are + * stored in intel byte order, and the name_len field could never be + * bigger than 255 chars, it's safe to reclaim the extra byte for the + * file_type field. + */ +struct ext2_dir_entry_2 { + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u8 name_len; /* Name length */ + __u8 file_type; + char name[EXT2_NAME_LEN]; /* File name */ +}; + +/* inode in memory */ +struct ext2_i_info { + __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ +}; + +#endif /* _FIWIX_FS_EXT2_H */ diff --git a/include/fiwix/fs_iso9660.h b/include/fiwix/fs_iso9660.h new file mode 100644 index 00000000..e284fdd2 --- /dev/null +++ b/include/fiwix/fs_iso9660.h @@ -0,0 +1,216 @@ +/* + * fiwix/include/fiwix/fs_iso9660.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_FS_ISO9660_H +#define _FIWIX_FS_ISO9660_H + +#include +#include + +#define ISO9660_SUPERBLOCK 16 /* ISO9660 superblock is in block 16 */ +#define ISO9660_STANDARD_ID "CD001" /* standard identification */ +#define ISO9660_SUPER_MAGIC 0x9660 + +#define ISO9660_VD_BOOT 0 +#define ISO9660_VD_PRIMARY 1 +#define ISO9660_VD_SUPPLEMENTARY 2 +#define ISO9660_VD_PARTITION 3 +#define ISO9660_VD_END 255 + +#define ISODCL(from, to) ((to - from) + 1) /* descriptor length */ + +#define ISO9660_MAX_VD 10 /* maximum number of VD per CDROM */ + +/* inodes will have their directory block and their offset packed as follows: + * 7FF7FF + * \-/\-/ + * ^ ^ + * | +----- offset value (11bit entries) + * +-------- directory block where to find it (11bit entries) + */ +#define ISO9660_INODE_BITS 11 /* FIXME: it could be greater (16bit) */ +#define ISO9660_INODE_MASK 0x7FF + +#define ISO9660_FILE_NOTEXIST 0x01 /* file shouldn't exists for the user */ +#define ISO9660_FILE_ISDIR 0x02 /* is a directory */ +#define ISO9660_FILE_ISASSOC 0x04 /* associated file */ +#define ISO9660_FILE_HASRECFMT 0x08 /* has a record format */ +#define ISO9660_FILE_HASOWNER 0x10 /* has owner and group defined */ +#define ISO9660_FILE_RESERVED5 0x20 /* reserved */ +#define ISO9660_FILE_RESERVED6 0x40 /* reserved */ +#define ISO9660_FILE_ISMULTIEXT 0x80 /* has more directory records */ + +#define SP_MAGIC1 0xBE +#define SP_MAGIC2 0xEF +#define GET_SIG(s1, s2) ((s1 << 8) | s2) + +#define SL_CURRENT 0x02 +#define SL_PARENT 0x04 +#define SL_ROOT 0x08 + +#define TF_CREATION 0x01 +#define TF_MODIFY 0x02 +#define TF_ACCESS 0x04 +#define TF_ATTRIBUTES 0x08 +#define TF_BACKUP 0x10 +#define TF_EXPIRATION 0x20 +#define TF_EFFECTIVE 0x40 +#define TF_LONG_FORM 0x80 + +#define NM_CONTINUE 0 +#define NM_CURRENT 1 +#define NM_PARENT 2 + +/* formerly Primary Volume Descriptor */ +struct iso9660_super_block { + char type [ISODCL( 1, 1)]; /* 7.1.1 */ + char id [ISODCL( 2, 6)]; + char version [ISODCL( 7, 7)]; /* 7.1.1 */ + char unused1 [ISODCL( 8, 8)]; + char system_id [ISODCL( 9, 40)]; /* a-chars */ + char volume_id [ISODCL( 41, 72)]; /* d-chars */ + char unused2 [ISODCL( 73, 80)]; + char volume_space_size [ISODCL( 81, 88)]; /* 7.3.3 */ + char unused3 [ISODCL( 89, 120)]; + char volume_set_size [ISODCL(121, 124)]; /* 7.2.3 */ + char volume_sequence_number [ISODCL(125, 128)]; /* 7.2.3 */ + char logical_block_size [ISODCL(129, 132)]; /* 7.2.3 */ + char path_table_size [ISODCL(133, 140)]; /* 7.3.3 */ + char type_l_path_table [ISODCL(141, 144)]; /* 7.3.1 */ + char opt_type_l_path_table [ISODCL(145, 148)]; /* 7.3.1 */ + char type_m_path_table [ISODCL(149, 152)]; /* 7.3.2 */ + char opt_type_m_path_table [ISODCL(153, 156)]; /* 7.3.2 */ + char root_directory_record [ISODCL(157, 190)]; /* 9.1 */ + char volume_set_id [ISODCL(191, 318)]; /* d-chars */ + char publisher_id [ISODCL(319, 446)]; /* a-chars */ + char preparer_id [ISODCL(447, 574)]; /* a-chars */ + char application_id [ISODCL(575, 702)]; /* a-chars */ + char copyright_file_id [ISODCL(703, 739)]; /* 7.5 d-chars */ + char abstract_file_id [ISODCL(740, 776)]; /* 7.5 d-chars */ + char bibliographic_file_id [ISODCL(777, 813)]; /* 7.5 d-chars */ + char creation_date [ISODCL(814, 830)]; /* 8.4.26.1 */ + char modification_date [ISODCL(831, 847)]; /* 8.4.26.1 */ + char expiration_date [ISODCL(848, 864)]; /* 8.4.26.1 */ + char effective_date [ISODCL(865, 881)]; /* 8.4.26.1 */ + char file_structure_version [ISODCL(882, 882)]; + char unused4 [ISODCL(883, 883)]; + char application_data [ISODCL(884, 1395)]; + char unused5 [ISODCL(1396, 2048)]; +}; + +struct iso9660_directory_record +{ + char length [ISODCL( 1, 1)]; /* 7.1.1 */ + char ext_attr_length [ISODCL( 2, 2)]; /* 7.1.1 */ + char extent [ISODCL( 3, 10)]; /* 7.3.3 */ + char size [ISODCL(11, 18)]; /* 7.3.3 */ + char date [ISODCL(19, 25)]; /* 7 by 7.1.1 */ + char flags [ISODCL(26, 26)]; + char file_unit_size [ISODCL(27, 27)]; /* 7.1.1 */ + char interleave [ISODCL(28, 28)]; /* 7.1.1 */ + char volume_sequence_number [ISODCL(29, 32)]; /* 7.2.3 */ + char name_len [ISODCL(33, 33)]; /* 7.1.1 */ + char name[0]; +}; + +struct iso9660_pathtable_record +{ + char length [ISODCL( 1, 1)]; /* 7.1.1 */ + char ext_attr_length [ISODCL( 2, 2)]; /* 7.1.1 */ + char extent [ISODCL( 3, 6)]; /* 7.3 */ + char parent [ISODCL( 7, 8)]; /* 7.2 */ + char name[0]; +}; + +struct susp_sp { + unsigned char magic[2]; + char len_skip; +}; + +struct susp_ce { + char block[8]; + char offset[8]; + char size[8]; +}; + +struct susp_er { + char len_id; + char len_des; + char len_src; + char ext_ver; + char data[0]; +}; + +struct rrip_px { + char mode[8]; + char nlink[8]; + char uid[8]; + char gid[8]; + char sn[8]; +}; + +struct rrip_pn { + char dev_h[8]; + char dev_l[8]; +}; + +struct rrip_sl_component { + unsigned char flags; + unsigned char len; + char name[0]; +}; + +struct rrip_sl { + unsigned char flags; + struct rrip_sl_component area; +}; + +struct rrip_nm { + unsigned char flags; + char name[0]; +}; + +struct rrip_tf_timestamp { + char time[7]; /* assumes LONG_FORM bit always set to zero */ +}; + +struct rrip_tf { + char flags; + struct rrip_tf_timestamp times[0]; +}; + +struct susp_rrip { + char signature[2]; + unsigned char len; + unsigned char version; + union { + struct susp_sp sp; + struct susp_ce ce; + struct susp_er er; + struct rrip_px px; + struct rrip_pn pn; + struct rrip_sl sl; + struct rrip_nm nm; + struct rrip_tf tf; + } u; +}; + +struct iso9660_inode { + __blk_t i_extent; + struct inode *i_parent; /* inode of its parent directory */ +}; + +struct iso9660_sb_info { + __u32 s_root_inode; + char *pathtable_raw; + struct iso9660_pathtable_record **pathtable; + int paths; + unsigned char rrip; + struct iso9660_super_block *sb; +}; + +#endif /* _FIWIX_FS_ISO9660_H */ diff --git a/include/fiwix/fs_minix.h b/include/fiwix/fs_minix.h new file mode 100644 index 00000000..9c0c28d2 --- /dev/null +++ b/include/fiwix/fs_minix.h @@ -0,0 +1,131 @@ +/* + * fiwix/include/fiwix/fs_minix.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_FS_MINIX_H +#define _FIWIX_FS_MINIX_H + +#include +#include + +#define MINIX_ROOT_INO 1 /* root inode */ + +#define MINIX_SUPER_MAGIC 0x137F /* Minix v1, 14 char names */ +#define MINIX_SUPER_MAGIC2 0x138F /* Minix v1, 30 char names */ +#define MINIX2_SUPER_MAGIC 0x2468 /* Minix v2, 14 char names */ +#define MINIX2_SUPER_MAGIC2 0x2478 /* Minix v2, 30 char names */ + +#define MINIX_VALID_FS 1 /* clean filesystem */ +#define MINIX_ERROR_FS 2 /* needs fsck */ + +#define CLEAR_BIT 0 +#define SET_BIT 1 + +#define V1_MAX_BITMAP_BLOCKS 8 /* 64MB filesystem size */ +#define V2_MAX_BITMAP_BLOCKS 128 /* 1GB filesystem size */ + +/* + * Minix (v1 and v2) file system physical layout: + * + * +----------------------------------------------- + * | size in blocks of BLKSIZE_1K (1024 bytes) | + * +-------------+----------------------------------------------+ + * | block 0 | 1 | + * +-------------+----------------------------------------------+ + * | superblock | 1 | + * +-------------+----------------------------------------------+ + * | inode map | number of inodes / (BLKSIZE_1K * 8) | + * +-------------+----------------------------------------------+ + * | zone map | number of zones / (BLKSIZE_1K * 8) | + * +-------------+----------------------------------------------+ + * | inode table | ((32 or 64) * number of inodes) / BLKSIZE_1K | + * +-------------+----------------------------------------------+ + * | data zones | ... | + * +-------------+----------------------------------------------+ + * + * The implementation of this filesystem in Fiwix might have slow disk writes + * because I don't keep in memory the superblock, nor the blocks of the inode + * map nor the blocks of the zone map. Keeping them in memory would be a waste + * of 137KB per each mounted v2 filesystem (1GB of size). + * + * - superblock -> 1KB + * - inode map -> 8KB (1KB (8192 bits) x 8 = 65536 inodes) + * - zone map -> 128KB (1KB (8192 bits) x 128 = 1048576 1k-blocks) + * + */ + +struct minix_super_block { + __u16 s_ninodes; /* number of inodes */ + __u16 s_nzones; /* number of data zones */ + __u16 s_imap_blocks; /* blocks used by inode bitmap */ + __u16 s_zmap_blocks; /* blocks used by zone bitmap */ + __u16 s_firstdatazone; /* number of first data zone */ + __u16 s_log_zone_size; /* 1024 << s_log_zone_size */ + __u32 s_max_size; /* maximum file size (in bytes) */ + __u16 s_magic; /* magic number */ + __u16 s_state; /* filesystem state */ + __u32 s_zones; /* number of data zones (for v2 only) */ +}; + +struct minix_inode { + __u16 i_mode; + __u16 i_uid; + __u32 i_size; + __u32 i_time; + __u8 i_gid; + __u8 i_nlinks; + __u16 i_zone[9]; +}; + +struct minix2_inode { + __u16 i_mode; + __u16 i_nlink; + __u16 i_uid; + __u16 i_gid; + __u32 i_size; + __u32 i_atime; + __u32 i_mtime; + __u32 i_ctime; + __u32 i_zone[10]; +}; + +struct minix_dir_entry { + __u16 inode; + char name[0]; +}; + +/* super block in memory */ +struct minix_sb_info { + unsigned char namelen; + unsigned char dirsize; + unsigned short int version; + unsigned int nzones; + struct minix_super_block sb; +}; + +/* inode in memory */ +struct minix_i_info { + union { + __u16 i1_zone[9]; + __u32 i2_zone[10]; + } u; +}; + +int v1_minix_read_inode(struct inode *); +int v1_minix_write_inode(struct inode *); +int v1_minix_ialloc(struct inode *); +void v1_minix_ifree(struct inode *); +int v1_minix_bmap(struct inode *, __off_t, int); +int v1_minix_truncate(struct inode *, __off_t); + +int v2_minix_read_inode(struct inode *); +int v2_minix_write_inode(struct inode *); +int v2_minix_ialloc(struct inode *); +void v2_minix_ifree(struct inode *); +int v2_minix_bmap(struct inode *, __off_t, int); +int v2_minix_truncate(struct inode *, __off_t); + +#endif /* _FIWIX_FS_MINIX_H */ diff --git a/include/fiwix/fs_pipe.h b/include/fiwix/fs_pipe.h new file mode 100644 index 00000000..8bee3f48 --- /dev/null +++ b/include/fiwix/fs_pipe.h @@ -0,0 +1,23 @@ +/* + * fiwix/include/fiwix/fs_pipe.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_FS_PIPE_H +#define _FIWIX_FS_PIPE_H + +#define PIPE_DEV 0xFFF0 /* special device number for nodev fs */ + +extern struct fs_operations pipefs_fsop; + +struct pipefs_inode { + char *i_data; /* buffer */ + unsigned int i_readoff; /* offset for reads */ + unsigned int i_writeoff; /* offset for writes */ + unsigned int i_readers; /* number of readers */ + unsigned int i_writers; /* number of writers */ +}; + +#endif /* _FIWIX_FS_PIPE_H */ diff --git a/include/fiwix/fs_proc.h b/include/fiwix/fs_proc.h new file mode 100644 index 00000000..1fdcaada --- /dev/null +++ b/include/fiwix/fs_proc.h @@ -0,0 +1,89 @@ +/* + * fiwix/include/fiwix/fs_proc.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_FS_PROC_H +#define _FIWIX_FS_PROC_H + +#include + +#define PROC_DEV 0xFFF1 /* special device number for nodev fs */ +#define PROC_ROOT_INO 1 /* root inode */ +#define PROC_SUPER_MAGIC 0x9FA0 /* same as in Linux */ + +#define PROC_PID_INO 0x40000000 /* base for PID inodes */ +#define PROC_PID_LEV 1 /* array level for PID */ + +#define PROC_ARRAY_ENTRIES 19 + +enum pid_dir_inodes { + PROC_PID_FD = PROC_PID_INO + 1001, + PROC_PID_CMDLINE, + PROC_PID_CWD, + PROC_PID_ENVIRON, + PROC_PID_EXE, + PROC_PID_MAPS, + PROC_PID_MOUNTINFO, + PROC_PID_ROOT, + PROC_PID_STAT, + PROC_PID_STATUS +}; + +struct procfs_inode { + unsigned int i_lev; /* array level (directory depth) */ +}; + +struct procfs_dir_entry { + __ino_t inode; + __mode_t mode; + __nlink_t nlink; + int lev; /* array level (directory depth) */ + unsigned short int name_len; + char *name; + int (*data_fn)(char *, __pid_t); +}; + +extern struct procfs_dir_entry procfs_array[][PROC_ARRAY_ENTRIES + 1]; + +int data_proc_cmdline(char *, __pid_t); +int data_proc_cpuinfo(char *, __pid_t); +int data_proc_devices(char *, __pid_t); +int data_proc_dma(char *, __pid_t); +int data_proc_filesystems(char *, __pid_t); +int data_proc_interrupts(char *, __pid_t); +int data_proc_loadavg(char *, __pid_t); +int data_proc_locks(char *, __pid_t); +int data_proc_meminfo(char *, __pid_t); +int data_proc_mounts(char *, __pid_t); +int data_proc_partitions(char *, __pid_t); +int data_proc_rtc(char *, __pid_t); +int data_proc_self(char *, __pid_t); +int data_proc_stat(char *, __pid_t); +int data_proc_uptime(char *, __pid_t); +int data_proc_fullversion(char *, __pid_t); +int data_proc_domainname(char *, __pid_t); +int data_proc_filemax(char *, __pid_t); +int data_proc_filenr(char *, __pid_t); +int data_proc_hostname(char *, __pid_t); +int data_proc_inodemax(char *, __pid_t); +int data_proc_inodenr(char *, __pid_t); +int data_proc_osrelease(char *, __pid_t); +int data_proc_ostype(char *, __pid_t); +int data_proc_version(char *, __pid_t); + +/* PID related functions */ +int data_proc_pid_fd(char *, __pid_t); +int data_proc_pid_cmdline(char *, __pid_t); +int data_proc_pid_cwd(char *, __pid_t); +int data_proc_pid_environ(char *, __pid_t); +int data_proc_pid_exe(char *, __pid_t); +int data_proc_pid_maps(char *, __pid_t); +int data_proc_pid_mountinfo(char *, __pid_t); +int data_proc_pid_root(char *, __pid_t); +int data_proc_pid_stat(char *, __pid_t); +int data_proc_pid_status(char *, __pid_t); + +#endif /* _FIWIX_FS_PROC_H */ diff --git a/include/fiwix/i386elf.h b/include/fiwix/i386elf.h new file mode 100644 index 00000000..bc64f3ce --- /dev/null +++ b/include/fiwix/i386elf.h @@ -0,0 +1,279 @@ +/* + * fiwix/include/fiwix/i386elf.h + */ + +#ifndef _FIWIX_ELF_H +#define _FIWIX_ELF_H + +typedef unsigned long Elf32_Addr; +typedef unsigned short Elf32_Half; +typedef unsigned long Elf32_Off; +typedef long Elf32_Sword; +typedef unsigned long Elf32_Word; + +#define ELFMAG0 0x7f /* EI_MAG */ +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define EI_NIDENT 16 + +typedef struct elf32_hdr{ + unsigned char e_ident[EI_NIDENT]; /* ELF "magic number" */ + Elf32_Half e_type; /* File type */ + Elf32_Half e_machine; /* Target machine */ + Elf32_Word e_version; /* File version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* File flags */ + Elf32_Half e_ehsize; /* Sizeof Ehdr (ELF header) */ + Elf32_Half e_phentsize; /* Sizeof Phdr (Program header) */ + Elf32_Half e_phnum; /* Number Phdrs (Program header) */ + Elf32_Half e_shentsize; /* Sizeof Shdr (Section header) */ + Elf32_Half e_shnum; /* Number Shdrs (Section header) */ + Elf32_Half e_shstrndx; /* Shdr string index */ +} Elf32_Ehdr; + +#define EI_MAG0 0 /* e_ident[] indexes */ +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_PAD 7 + +#define ELFCLASSNONE 0 /* EI_CLASS */ +#define ELFCLASS32 1 +#define ELFCLASS64 2 +#define ELFCLASSNUM 3 + +#define ELFDATANONE 0 /* e_ident[EI_DATA] */ +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 +#define ELFDATANUM 3 + +/* ELF file types */ +#define ET_NONE 0 +#define ET_REL 1 +#define ET_EXEC 2 +#define ET_DYN 3 +#define ET_CORE 4 +#define ET_LOPROC 5 +#define ET_HIPROC 6 + +#define EM_386 3 + +#define EV_NONE 0 /* e_version, EI_VERSION */ +#define EV_CURRENT 1 +#define EV_NUM 2 + +typedef struct elf32_phdr{ + Elf32_Word p_type; /* Entry type */ + Elf32_Off p_offset; /* File offset */ + Elf32_Addr p_vaddr; /* Virtual address */ + Elf32_Addr p_paddr; /* Physical address */ + Elf32_Word p_filesz; /* File size */ + Elf32_Word p_memsz; /* Memory size */ + Elf32_Word p_flags; /* Entry flags */ + Elf32_Word p_align; /* Memory & file alignment */ +} Elf32_Phdr; + +/* segment types stored in the image headers */ +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 +#define PT_NUM 7 +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff + +/* permission types on sections in the program header, p_flags. */ +#define PF_R 0x4 +#define PF_W 0x2 +#define PF_X 0x1 + +#define PF_MASKPROC 0xf0000000 + +typedef struct { + Elf32_Word sh_name; /* Section name, index in string tbl */ + Elf32_Word sh_type; /* Type of section */ + Elf32_Word sh_flags; /* Miscellaneous section attributes */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Size of section in bytes */ + Elf32_Word sh_link; /* Index of another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +} Elf32_Shdr; + +/* sh_type */ +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_NUM 12 + +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff + +/* sh_flags */ +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 +#define SHF_EXECINSTR 0x4 + +/* special section indexes */ +#define SHN_UNDEF 0 +#define SHN_LORESERVE 0xff00 +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#define SHN_HIRESERVE 0xffff +#define SHN_LOPROC 0xff00 +#define SHN_HIPROC 0xff1f + +typedef struct elf32_sym{ + Elf32_Word st_name; /* Symbol name, index in string tbl */ + Elf32_Addr st_value; /* Value of the symbol */ + Elf32_Word st_size; /* Associated symbol size */ + unsigned char st_info; /* Type and binding attributes */ + unsigned char st_other; /* No defined meaning, 0 */ + Elf32_Half st_shndx; /* Associated section index */ +} Elf32_Sym; + +#define ELF32_ST_BIND(info) ((info) >> 4) +#define ELF32_ST_TYPE(info) (((unsigned int) info) & 0xf) + +/* This info is needed when parsing the symbol table */ +#define STB_LOCAL 0 +#define STB_GLOBAL 1 +#define STB_WEAK 2 +#define STB_NUM 3 + +#define STT_NOTYPE 0 +#define STT_OBJECT 1 +#define STT_FUNC 2 +#define STT_SECTION 3 +#define STT_FILE 4 +#define STT_NUM 5 + +typedef struct elf32_rel { + Elf32_Addr r_offset; /* Location at which to apply the action */ + Elf32_Word r_info; /* Index and type of relocation */ +} Elf32_Rel; + +typedef struct elf32_rela{ + Elf32_Addr r_offset; /* Location at which to apply the action */ + Elf32_Word r_info; /* Index and type of relocation */ + Elf32_Sword r_addend; /* Constant addend used to compute value */ +} Elf32_Rela; + +/* The following are used with relocations */ +#define ELF32_R_SYM(info) ((info) >> 8) +#define ELF32_R_TYPE(info) ((info) & 0xff) + +/* This is the info that is needed to parse the dynamic section of the file */ +#define DT_NULL 0 +#define DT_NEEDED 1 +#define DT_PLTRELSZ 2 +#define DT_PLTGOT 3 +#define DT_HASH 4 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_RELA 7 +#define DT_RELASZ 8 +#define DT_RELAENT 9 +#define DT_STRSZ 10 +#define DT_SYMENT 11 +#define DT_INIT 12 +#define DT_FINI 13 +#define DT_SONAME 14 +#define DT_RPATH 15 +#define DT_SYMBOLIC 16 +#define DT_REL 17 +#define DT_RELSZ 18 +#define DT_RELENT 19 +#define DT_PLTREL 20 +#define DT_DEBUG 21 +#define DT_TEXTREL 22 +#define DT_JMPREL 23 +#define DT_LOPROC 0x70000000 +#define DT_HIPROC 0x7fffffff + +/* Symbolic values for the entries in the auxiliary table + put on the initial stack */ +#define AT_NULL 0 /* end of vector */ +#define AT_IGNORE 1 /* entry should be ignored */ +#define AT_EXECFD 2 /* file descriptor of program */ +#define AT_PHDR 3 /* program headers for program */ +#define AT_PHENT 4 /* size of program header entry */ +#define AT_PHNUM 5 /* number of program headers */ +#define AT_PAGESZ 6 /* system page size */ +#define AT_BASE 7 /* base address of interpreter */ +#define AT_FLAGS 8 /* flags */ +#define AT_ENTRY 9 /* entry point of program */ +#define AT_NOTELF 10 /* program is not ELF */ +#define AT_UID 11 /* real uid */ +#define AT_EUID 12 /* effective uid */ +#define AT_GID 13 /* real gid */ +#define AT_EGID 14 /* effective gid */ + + +typedef struct dynamic{ + Elf32_Sword d_tag; /* entry tabg value */ + union{ + Elf32_Sword d_val; + Elf32_Addr d_ptr; + } d_un; +} Elf32_Dyn; + +#define R_386_NONE 0 +#define R_386_32 1 +#define R_386_PC32 2 +#define R_386_GOT32 3 +#define R_386_PLT32 4 +#define R_386_COPY 5 +#define R_386_GLOB_DAT 6 +#define R_386_JMP_SLOT 7 +#define R_386_RELATIVE 8 +#define R_386_GOTOFF 9 +#define R_386_GOTPC 10 +#define R_386_NUM 11 + +/* Notes used in ET_CORE */ +#define NT_PRSTATUS 1 +#define NT_PRFPREG 2 +#define NT_PRPSINFO 3 +#define NT_TASKSTRUCT 4 + +/* Note header in a PT_NOTE section */ +typedef struct elf32_note { + Elf32_Word n_namesz; /* Name size */ + Elf32_Word n_descsz; /* Content size */ + Elf32_Word n_type; /* Content type */ +} Elf32_Nhdr; + +#define ELF_START_MMAP 0x80000000 + +extern Elf32_Dyn _DYNAMIC []; +#define elfhdr elf32_hdr +#define elf_phdr elf32_phdr +#define elf_note elf32_note + +#endif /* _FIWIX_ELF_H */ diff --git a/include/fiwix/ide.h b/include/fiwix/ide.h new file mode 100644 index 00000000..e6eb587c --- /dev/null +++ b/include/fiwix/ide.h @@ -0,0 +1,276 @@ +/* + * fiwix/include/fiwix/ide.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_IDE_H +#define _FIWIX_IDE_H + +#include +#include + +#define IDE0_IRQ 14 /* primary controller interrupt */ +#define IDE1_IRQ 15 /* secondary controller interrupt */ + +#define IDE0_MAJOR 3 /* 1st controller major number */ +#define IDE1_MAJOR 22 /* 2nd controller major number */ +#define IDE_MINORS 4 /* max. minors/partitions per unit */ +#define IDE_MASTER_MSF 0 /* IDE master minor shift factor */ +#define IDE_SLAVE_MSF 6 /* IDE slave minor shift factor */ + +#define IDE_PRIMARY 0 +#define IDE_SECONDARY 1 +#define IDE_MASTER 0 +#define IDE_SLAVE 1 +#define IDE_ATA 0 +#define IDE_ATAPI 1 + +#define NR_IDE_CTRLS 2 /* IDE controllers */ +#define NR_IDE_DRVS 2 /* max. drives per IDE controller */ + +/* controller addresses */ +#define IDE0_BASE 0x1F0 /* primary controller base addr */ +#define IDE0_CTRL 0x3F4 /* primary controller control port */ +#define IDE1_BASE 0x170 /* secondary controller base addr */ +#define IDE1_CTRL 0x374 /* secondary controller control port */ + +#define IDE_BASE_LEN 7 /* controller address length */ + +#define IDE_RDY_RETR_LONG 50000 /* long delay for fast CPUs */ +#define IDE_RDY_RETR_SHORT 500 /* short delay for slow CPUs */ +#define MAX_IDE_ERR 10 /* number of retries */ +#define MAX_CD_ERR 5 /* number of retries in CDROMs */ + +#define SET_IDE_RDY_RETR(retries) \ + if((cpu_table.hz / 1000000) <= 100) { \ + retries = IDE_RDY_RETR_SHORT; \ + } else { \ + retries = IDE_RDY_RETR_LONG; \ + } + +#define WAIT_FOR_IDE (1 * HZ) /* timeout for hard disk */ +#define WAIT_FOR_CD (3 * HZ) /* timeout for cdrom */ + +/* controller registers */ +#define IDE_DATA 0x0 /* Data Port Register (R/W) */ +#define IDE_ERROR 0x1 /* Error Register (R) */ +#define IDE_FEATURES 0x1 /* Features Register (W) */ +#define IDE_SECCNT 0x2 /* Sector Count Register (R/W) */ +#define IDE_SECNUM 0x3 /* Sector Number Register (R/W) */ +#define IDE_LCYL 0x4 /* Cylinder Low Register (R/W) */ +#define IDE_HCYL 0x5 /* Cylinder High Register (R/W) */ +#define IDE_DRVHD 0x6 /* Drive/Head Register (R/W) */ +#define IDE_STATUS 0x7 /* Status Register (R) */ +#define IDE_COMMAND 0x7 /* Command Register (W) */ + +#define IDE_ALT_STATUS 0x2 /* Alternate Register (R) */ +#define IDE_DEV_CTRL 0x2 /* Device Control Register (W) */ + +/* error register bits */ +#define IDE_ERR_AMNF 0x01 /* Address Mark Not Found */ +#define IDE_ERR_TK0NF 0x02 /* Track 0 Not Found */ +#define IDE_ERR_ABRT 0x04 /* Aborted Command */ +#define IDE_ERR_MCR 0x08 /* Media Change Registered */ +#define IDE_ERR_IDNF 0x10 /* Sector ID Field Not Found */ +#define IDE_ERR_MC 0x20 /* Media Changed */ +#define IDE_ERR_UNC 0x40 /* Uncorrectable Data Error */ +#define IDE_ERR_BBK 0x80 /* Bad Block */ + +/* status register bits */ +#define IDE_STAT_ERR 0x01 /* an error ocurred */ +#define IDE_STAT_SENS 0x02 /* sense data available */ +#define IDE_STAT_CORR 0x04 /* a correctable error ocurred */ +#define IDE_STAT_DRQ 0x08 /* device is ready to transfer */ +#define IDE_STAT_DSC 0x10 /* device requests service o intr. */ +#define IDE_STAT_DWF 0x20 /* drive write fault */ +#define IDE_STAT_RDY 0x40 /* drive is ready */ +#define IDE_STAT_BSY 0x80 /* drive is busy */ + +#define IDE_CHS_MODE 0xA0 /* select CHS mode */ +#define IDE_LBA_MODE 0xE0 /* select LBA mode */ + +/* alternate & device control register bits */ +#define IDE_DEVCTR_DRQ 0x08 /* Data Request */ +#define IDE_DEVCTR_NIEN 0x02 /* Disable Interrupt */ +#define IDE_DEVCTR_SRST 0x04 /* Software Reset */ + +/* ATA commands */ +#define ATA_READ_PIO 0x20 /* read sector(s) with retries */ +#define ATA_READ_MULTIPLE_PIO 0xC4 /* read multiple sectors */ +#define ATA_WRITE_PIO 0x30 /* write sector(s) with retries */ +#define ATA_WRITE_MULTIPLE_PIO 0xC5 /* write multiple sectors */ +#define ATA_SET_MULTIPLE_MODE 0xC6 +#define ATA_PACKET 0xA0 +#define ATA_IDENTIFY_PACKET 0xA1 /* identify ATAPI device */ +#define ATA_IDENTIFY 0xEC /* identify ATA device */ + +/* ATAPI commands */ +#define ATAPI_TEST_UNIT 0x00 +#define ATAPI_REQUEST_SENSE 0x03 +#define ATAPI_START_STOP 0x1B +#define ATAPI_MEDIUM_REMOVAL 0x1E +#define ATAPI_READ10 0x28 + +#define CD_UNLOCK_MEDIUM 0x00 /* allow medium removal */ +#define CD_LOCK_MEDIUM 0x01 /* prevent medium removal */ +#define CD_EJECT 0x02 /* eject the CD if possible */ +#define CD_LOAD 0x03 /* load the CD (closes tray) */ + +/* ATAPI CD additional sense code */ +#define ASC_NOT_READY 0x04 +#define ASC_NO_MEDIUM 0x3A + +/* capabilities */ +#define IDE_SUPPORTS_CFA 0x848A +#define IDE_HAS_DMA 0x100 +#define IDE_HAS_LBA 0x200 +#define IDE_MIN_LBA 16514064/* sectors limit for using CHS */ + +/* general configuration bits */ +#define IDE_HAS_UDMA 0x04 /* device supports UDMA */ +#define IDE_REMOVABLE 0x80 /* removable media device */ + +/* ATAPI types */ +#define ATAPI_IS_SEQ_ACCESS 0x01 /* sequential-access device */ +#define ATAPI_IS_PRINTER 0x02 +#define ATAPI_IS_PROCESSOR 0x03 +#define ATAPI_IS_WRITE_ONCE 0x04 +#define ATAPI_IS_CDROM 0x05 +#define ATAPI_IS_SCANNER 0x06 + +/* IDE drive flags */ +#define DEVICE_IS_ATAPI 0x01 +#define DEVICE_IS_CFA 0x02 +#define DEVICE_IS_DISK 0x04 +#define DEVICE_IS_CDROM 0x08 +#define DEVICE_REQUIRES_LBA 0x10 +#define DEVICE_HAS_RW_MULTIPLE 0x20 + +/* ATA/ATAPI-4 based */ +struct ide_drv_ident { + unsigned short int gen_config; /* general configuration bits */ + unsigned short int logic_cyls; /* logical cylinders */ + unsigned short int reserved2; + unsigned short int logic_heads; /* logical heads */ + unsigned short int retired4; + unsigned short int retired5; + unsigned short int logic_spt; /* logical sectors/track */ + unsigned short int retired7; + unsigned short int retired8; + unsigned short int retired9; + char serial_number[20]; /* serial number */ + unsigned short int vendor_spec20; + unsigned short int buffer_cache; + unsigned short int vendor_spec22; /* reserved */ + char firmware_rev[8]; /* firmware version */ + char model_number[40]; /* model number */ + unsigned short int rw_multiple; + unsigned short int reserved48; + unsigned short int capabilities; /* capabilities */ + unsigned short int reserved50; + unsigned short int pio_mode; /* PIO data transfer mode*/ + unsigned short int dma_mode; + unsigned short int fields_validity; /* fields validity */ + unsigned short int cur_log_cyls; /* current logical cylinders */ + unsigned short int cur_log_heads; /* current logical heads */ + unsigned short int cur_log_spt; /* current logical sectors/track */ + unsigned short int cur_capacity; /* current capacity in sectors */ + unsigned short int cur_capacity2; /* 32bit number */ + unsigned short int mult_sect_set; /* multiple sector settings */ + unsigned short int tot_sectors; /* sectors (LBA only) */ + unsigned short int tot_sectors2; /* 32bit number */ + unsigned short int singleword_dma; + unsigned short int multiword_dma; /* multiword DMA settings */ + unsigned short int adv_pio_modes; /* advanced PIO modes */ + unsigned short int min_multiword; /* min. Multiword DMA transfer */ + unsigned short int rec_multiword; /* recommended Multiword DMS transfer */ + unsigned short int min_pio_wo_fc; /* min. PIO w/o flow control */ + unsigned short int min_pio_w_fc; /* min. PIO with flow control */ + unsigned short int reserved69; + unsigned short int reserved70; + unsigned short int reserved71; + unsigned short int reserved72; + unsigned short int reserved73; + unsigned short int reserved74; + unsigned short int queue_depth; /* queue depth */ + unsigned short int reserved76; + unsigned short int reserved77; + unsigned short int reserved78; + unsigned short int reserved79; + unsigned short int majorver; /* major version number */ + unsigned short int minorver; /* minor version number */ + unsigned short int cmdset1; /* command set supported */ + unsigned short int cmdset2; /* command set supported */ + unsigned short int cmdsf_ext; /* command set/feature sup.ext. */ + unsigned short int cmdsf_enable1; /* command s/f enabled */ + unsigned short int cmdsf_enable2; /* command s/f enabled */ + unsigned short int cmdsf_default; /* command s/f default */ + unsigned short int ultradma; /* ultra DMA mode */ + unsigned short int reserved89; + unsigned short int reserved90; + unsigned short int curapm; /* current APM values */ + unsigned short int reserved92_126[35]; + unsigned short int r_status_notif; /* removable media status notif. */ + unsigned short int security_status; /* security status */ + unsigned short int vendor_spec129_159[31]; + unsigned short int reserved160_255[96]; +}; + +struct ide_drv { + int drive; /* master or slave */ + char *dev_name; + unsigned char major; /* major number */ + unsigned int flags; + int minor_shift; /* shift factor to get the real minor */ + int lba_cyls; + int lba_heads; + short int lba_factor; + unsigned int nr_sects; /* total sectors (LBA) */ + struct fs_operations *fsop; + struct ide_drv_ident ident; + struct partition part_table[NR_PARTITIONS]; +}; + +struct ide { + int channel; /* primary or secondary */ + int base; /* base address */ + int ctrl; /* control port address */ + short int irq; + struct ide_drv drive[NR_IDE_DRVS]; +}; + +extern struct ide ide_table[NR_IDE_CTRLS]; + +extern int ide0_need_reset; +extern int ide0_wait_interrupt; +extern int ide0_timeout; +extern int ide1_need_reset; +extern int ide1_wait_interrupt; +extern int ide1_timeout; + +void irq_ide0(void); +void ide0_timer(unsigned int); +void irq_ide1(void); +void ide1_timer(unsigned int); + +void ide_error(struct ide *, int); +void ide_delay(void); +void ide_wait400ns(struct ide *); +int ide_ready(struct ide *); +int ide_drvsel(struct ide *, int, int, unsigned char); +int ide_softreset(struct ide *); + +struct ide * get_ide_controller(__dev_t); +int get_ide_drive(__dev_t); + +int ide_open(struct inode *, struct fd *); +int ide_close(struct inode *, struct fd *); +int ide_read(__dev_t, __blk_t, char *, int); +int ide_write(__dev_t, __blk_t, char *, int); +int ide_ioctl(struct inode *, int, unsigned long int); + +void ide_init(void); + +#endif /* _FIWIX_IDE_H */ diff --git a/include/fiwix/ide_cd.h b/include/fiwix/ide_cd.h new file mode 100644 index 00000000..1becfa8f --- /dev/null +++ b/include/fiwix/ide_cd.h @@ -0,0 +1,24 @@ +/* + * fiwix/include/fiwix/ide_cd.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_IDE_CD_H +#define _FIWIX_IDE_CD_H + +#include + +#define IDE_CD_SECTSIZE BLKSIZE_2K /* sector size (in bytes) */ + +void ide_cd_timer(unsigned int); + +int ide_cd_open(struct inode *, struct fd *); +int ide_cd_close(struct inode *, struct fd *); +int ide_cd_read(__dev_t, __blk_t, char *, int); +int ide_cd_ioctl(struct inode *, int, unsigned long int); + +int ide_cd_init(struct ide *, int); + +#endif /* _FIWIX_IDE_CD_H */ diff --git a/include/fiwix/ide_hd.h b/include/fiwix/ide_hd.h new file mode 100644 index 00000000..b0ba6744 --- /dev/null +++ b/include/fiwix/ide_hd.h @@ -0,0 +1,23 @@ +/* + * fiwix/include/fiwix/ide_hd.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_IDE_HD_H +#define _FIWIX_IDE_HD_H + +#include + +#define IDE_HD_SECTSIZE 512 /* sector size (in bytes) */ + +int ide_hd_open(struct inode *, struct fd *); +int ide_hd_close(struct inode *, struct fd *); +int ide_hd_read(__dev_t, __blk_t, char *, int); +int ide_hd_write(__dev_t, __blk_t, char *, int); +int ide_hd_ioctl(struct inode *, int, unsigned long int); + +int ide_hd_init(struct ide *, int); + +#endif /* _FIWIX_IDE_HD_H */ diff --git a/include/fiwix/ioctl.h b/include/fiwix/ioctl.h new file mode 100644 index 00000000..5c67565a --- /dev/null +++ b/include/fiwix/ioctl.h @@ -0,0 +1,89 @@ +/* + * fiwix/include/fiwix/ioctl.h + */ + +#ifndef _FIWIX_IOCTL_H +#define _FIWIX_IOCTL_H + +#define HDIO_GETGEO 0x0301 /* get device geometry */ + +#define BLKRRPART 0x125F /* re-read partition table */ +#define BLKGETSIZE 0x1260 /* return device size */ +#define BLKFLSBUF 0x1261 /* flush buffer cache */ + +/* 0x54 is just a magic number to make these relatively unique ('T') */ +#define TCGETS 0x5401 +#define TCSETS 0x5402 +#define TCSETSW 0x5403 +#define TCSETSF 0x5404 +#define TCGETA 0x5405 +#define TCSETA 0x5406 +#define TCSETAW 0x5407 +#define TCSETAF 0x5408 +#define TCSBRK 0x5409 +#define TCXONC 0x540A +#define TCFLSH 0x540B +#define TIOCEXCL 0x540C +#define TIOCNXCL 0x540D +#define TIOCSCTTY 0x540E +#define TIOCGPGRP 0x540F +#define TIOCSPGRP 0x5410 +#define TIOCOUTQ 0x5411 +#define TIOCSTI 0x5412 +#define TIOCGWINSZ 0x5413 +#define TIOCSWINSZ 0x5414 +#define TIOCMGET 0x5415 +#define TIOCMBIS 0x5416 +#define TIOCMBIC 0x5417 +#define TIOCMSET 0x5418 +#define TIOCGSOFTCAR 0x5419 +#define TIOCSSOFTCAR 0x541A +#define FIONREAD 0x541B +#define TIOCINQ FIONREAD +#define TIOCLINUX 0x541C +#define TIOCCONS 0x541D +#define TIOCGSERIAL 0x541E +#define TIOCSSERIAL 0x541F +#define TIOCPKT 0x5420 +#define FIONBIO 0x5421 +#define TIOCNOTTY 0x5422 +#define TIOCSETD 0x5423 +#define TIOCGETD 0x5424 +#define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ +#define TIOCTTYGSTRUCT 0x5426 /* For debugging only */ +#define TIOCSBRK 0x5427 /* BSD compatibility */ +#define TIOCCBRK 0x5428 /* BSD compatibility */ +#define TIOCGSID 0x5429 /* Return the session ID of FD */ +#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ +#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ + +#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ +#define FIOCLEX 0x5451 +#define FIOASYNC 0x5452 +#define TIOCSERCONFIG 0x5453 +#define TIOCSERGWILD 0x5454 +#define TIOCSERSWILD 0x5455 +#define TIOCGLCKTRMIOS 0x5456 +#define TIOCSLCKTRMIOS 0x5457 +#define TIOCSERGSTRUCT 0x5458 /* For debugging only */ +#define TIOCSERGETLSR 0x5459 /* Get line status register */ +#define TIOCSERGETMULTI 0x545A /* Get multiport config */ +#define TIOCSERSETMULTI 0x545B /* Set multiport config */ + +#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */ +#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ +#define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */ +#define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */ + +/* Used for packet mode */ +#define TIOCPKT_DATA 0 +#define TIOCPKT_FLUSHREAD 1 +#define TIOCPKT_FLUSHWRITE 2 +#define TIOCPKT_STOP 4 +#define TIOCPKT_START 8 +#define TIOCPKT_NOSTOP 16 +#define TIOCPKT_DOSTOP 32 + +#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ + +#endif /* _FIWIX_IOCTL_H */ diff --git a/include/fiwix/kd.h b/include/fiwix/kd.h new file mode 100644 index 00000000..c4e82185 --- /dev/null +++ b/include/fiwix/kd.h @@ -0,0 +1,167 @@ +/* + * fiwix/include/fiwix/kd.h + */ + +#ifndef _LINUX_KD_H +#define _LINUX_KD_H + +/* Prefix 0x4B is 'K', to avoid collision with termios and vt */ + +#define GIO_FONT 0x4B60 /* gets font in expanded form */ +#define PIO_FONT 0x4B61 /* use font in expanded form */ + +#define GIO_FONTX 0x4B6B /* get font using struct consolefontdesc */ +#define PIO_FONTX 0x4B6C /* set font using struct consolefontdesc */ +struct consolefontdesc { + unsigned short int charcount; /* characters in font (256 or 512) */ + unsigned short int charheight; /* scan lines per character (1-32) */ + char *chardata; /* font data in expanded form */ +}; + +#define PIO_FONTRESET 0x4B6D /* reset to default font */ + +#define GIO_CMAP 0x4B70 /* gets colour palette on VGA+ */ +#define PIO_CMAP 0x4B71 /* sets colour palette on VGA+ */ + +#define KIOCSOUND 0x4B2F /* start sound generation (0 for off) */ +#define KDMKTONE 0x4B30 /* generate tone */ + +#define KDGETLED 0x4B31 /* return current led state */ +#define KDSETLED 0x4B32 /* set led state [lights, not flags] */ +#define LED_SCR 0x01 /* scroll lock led */ +#define LED_NUM 0x02 /* num lock led */ +#define LED_CAP 0x04 /* caps lock led */ + +#define KDGKBTYPE 0x4B33 /* get keyboard type */ +#define KB_84 0x01 +#define KB_101 0x02 /* this is what we always answer */ +#define KB_OTHER 0x03 + +#define KDADDIO 0x4B34 /* add i/o port as valid */ +#define KDDELIO 0x4B35 /* del i/o port as valid */ +#define KDENABIO 0x4B36 /* enable i/o to video board */ +#define KDDISABIO 0x4B37 /* disable i/o to video board */ + +#define KDSETMODE 0x4B3A /* set text/graphics mode */ +#define KD_TEXT 0x00 +#define KD_GRAPHICS 0x01 +#define KD_TEXT0 0x02 /* obsolete */ +#define KD_TEXT1 0x03 /* obsolete */ +#define KDGETMODE 0x4B3B /* get current mode */ + +#define KDMAPDISP 0x4B3C /* map display into address space */ +#define KDUNMAPDISP 0x4B3D /* unmap display from address space */ + +typedef char scrnmap_t; +#define E_TABSZ 256 +#define GIO_SCRNMAP 0x4B40 /* get screen mapping from kernel */ +#define PIO_SCRNMAP 0x4B41 /* put screen mapping table in kernel */ +#define GIO_UNISCRNMAP 0x4B69 /* get full Unicode screen mapping */ +#define PIO_UNISCRNMAP 0x4B6A /* set full Unicode screen mapping */ + +#define GIO_UNIMAP 0x4B66 /* get unicode-to-font mapping from kernel */ +struct unipair { + unsigned short int unicode; + unsigned short int fontpos; +}; +struct unimapdesc { + unsigned short int entry_ct; + struct unipair *entries; +}; +#define PIO_UNIMAP 0x4B67 /* put unicode-to-font mapping in kernel */ +#define PIO_UNIMAPCLR 0x4B68 /* clear table, possibly advise hash algorithm */ +struct unimapinit { + unsigned short int advised_hashsize; /* 0 if no opinion */ + unsigned short int advised_hashstep; /* 0 if no opinion */ + unsigned short int advised_hashlevel; /* 0 if no opinion */ +}; + +#define UNI_DIRECT_BASE 0xF000 /* start of Direct Font Region */ +#define UNI_DIRECT_MASK 0x01FF /* Direct Font Region bitmask */ + +#define K_RAW 0x00 +#define K_XLATE 0x01 +#define K_MEDIUMRAW 0x02 +#define K_UNICODE 0x03 +#define KDGKBMODE 0x4B44 /* gets current keyboard mode */ +#define KDSKBMODE 0x4B45 /* sets current keyboard mode */ + +#define K_METABIT 0x03 +#define K_ESCPREFIX 0x04 +#define KDGKBMETA 0x4B62 /* gets meta key handling mode */ +#define KDSKBMETA 0x4B63 /* sets meta key handling mode */ + +#define K_SCROLLLOCK 0x01 +#define K_NUMLOCK 0x02 +#define K_CAPSLOCK 0x04 +#define KDGKBLED 0x4B64 /* get led flags (not lights) */ +#define KDSKBLED 0x4B65 /* set led flags (not lights) */ + +struct kbentry { + unsigned char kb_table; + unsigned char kb_index; + unsigned short int kb_value; +}; +#define K_NORMTAB 0x00 +#define K_SHIFTTAB 0x01 +#define K_ALTTAB 0x02 +#define K_ALTSHIFTTAB 0x03 + +#define KDGKBENT 0x4B46 /* gets one entry in translation table */ +#define KDSKBENT 0x4B47 /* sets one entry in translation table */ + +struct kbsentry { + unsigned char kb_func; + unsigned char kb_string[512]; +}; +#define KDGKBSENT 0x4B48 /* gets one function key string entry */ +#define KDSKBSENT 0x4B49 /* sets one function key string entry */ + +struct kbdiacr { + unsigned char diacr, base, result; +}; +struct kbdiacrs { + unsigned int kb_cnt; /* number of entries in following array */ + struct kbdiacr kbdiacr[256]; /* MAX_DIACR from keyboard.h */ +}; +#define KDGKBDIACR 0x4B4A /* read kernel accent table */ +#define KDSKBDIACR 0x4B4B /* write kernel accent table */ + +struct kbkeycode { + unsigned int scancode, keycode; +}; +#define KDGETKEYCODE 0x4B4C /* read kernel keycode table entry */ +#define KDSETKEYCODE 0x4B4D /* write kernel keycode table entry */ + +#define KDSIGACCEPT 0x4B4E /* accept kbd generated signals */ + +struct kbd_repeat { + int delay; /* in msec; <= 0: don't change */ + int rate; /* in msec; <= 0: don't change */ +}; + +#define KDKBDREP 0x4B52 /* set keyboard delay/repeat rate; + * actually used values are returned */ + +#define KDFONTOP 0x4B72 /* font operations */ + +struct console_font_op { + unsigned int op; /* operation code KD_FONT_OP_* */ + unsigned int flags; /* KD_FONT_FLAG_* */ + unsigned int width, height; /* font size */ + unsigned int charcount; + unsigned char *data; /* font data with height fixed to 32 */ +}; + +#define KD_FONT_OP_SET 0 /* Set font */ +#define KD_FONT_OP_GET 1 /* Get font */ +#define KD_FONT_OP_SET_DEFAULT 2 /* Set font to default, data points to name / NULL */ +#define KD_FONT_OP_COPY 3 /* Copy from another console */ + +#define KD_FONT_FLAG_DONT_RECALC 1 /* Don't recalculate hw charcell size [compat] */ + +/* note: 0x4B00-0x4B4E all have had a value at some time; + don't reuse for the time being */ +/* note: 0x4B60-0x4B6D, 0x4B70-0x4B72 used above */ + +#endif /* _LINUX_KD_H */ diff --git a/include/fiwix/kernel.h b/include/fiwix/kernel.h new file mode 100644 index 00000000..99b499bc --- /dev/null +++ b/include/fiwix/kernel.h @@ -0,0 +1,85 @@ +/* + * fiwix/include/fiwix/kernel.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_KERNEL_H +#define _FIWIX_KERNEL_H + +#include +#include + +#define PANIC(format, args...) \ +{ \ + printk("\nPANIC: in %s()", __FUNCTION__); \ + printk("\n"); \ + printk(format, ## args); \ + stop_kernel(); \ +} + +#define CURRENT_TIME (kstat.system_time) +#define INIT_PROGRAM "/sbin/init" + +extern char *init_argv[]; +extern char *init_envp[]; + +extern Elf32_Shdr *symtab, *strtab; +extern unsigned int _last_data_addr; + +extern int _memsize; +extern int _extmemsize; +extern int _rootdev; +extern int _noramdisk; +extern int _ramdisksize; +extern char _rootfstype[10]; +extern char _rootdevname[DEVNAME_MAX + 1]; +extern int _syscondev; + +extern int _cputype; +extern int _cpusignature; +extern int _cpuflags; +extern int _brandid; +extern char _vendorid[12]; +extern char _brandstr[48]; +extern unsigned int _tlbinfo_eax; +extern unsigned int _tlbinfo_ebx; +extern unsigned int _tlbinfo_ecx; +extern unsigned int _tlbinfo_edx; +extern char _etext[], _edata[], _end[]; + +extern char cmdline[NAME_MAX + 1]; + +struct kernel_stat { + unsigned int cpu_user; /* ticks in user-mode */ + unsigned int cpu_nice; /* ticks in user-mode (with priority) */ + unsigned int cpu_system; /* ticks in kernel-mode */ + unsigned int irqs; /* irq counter */ + unsigned int sirqs; /* spurious irq counter */ + unsigned int ctxt; /* context switches */ + unsigned int ticks; /* ticks (1/HZths of sec) since boot */ + unsigned int system_time; /* current system time (since the Epoch) */ + unsigned int boot_time; /* boot time (since the Epoch) */ + int tz_minuteswest; /* minutes west of GMT */ + int tz_dsttime; /* type of DST correction */ + unsigned int uptime; /* seconds since boot */ + unsigned int processes; /* number of forks since boot */ + unsigned int physical_pages; /* physical memory in pages */ + unsigned int kernel_reserved; /* kernel memory reserved in KB */ + unsigned int physical_reserved; /* physical memory reserved in KB */ + unsigned int total_mem_pages; /* total memory in pages */ + unsigned int free_pages; /* pages on free list (available) */ + unsigned int buffers; /* memory used by buffers in KB */ + unsigned int cached; /* memory used to cache file pages */ + unsigned int shared; /* pages with count > 1 */ +}; +extern struct kernel_stat kstat; + +unsigned int get_last_elf_addr(unsigned int); +void start_kernel(unsigned long, unsigned long, unsigned int); +void stop_kernel(void); +void init_init(void); +void cpu_idle(void); + +#endif /* _FIWIX_KERNEL_H */ diff --git a/include/fiwix/keyboard.h b/include/fiwix/keyboard.h new file mode 100644 index 00000000..4931aae0 --- /dev/null +++ b/include/fiwix/keyboard.h @@ -0,0 +1,134 @@ +/* + * fiwix/include/fiwix/keyboard.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include + +#ifndef _FIWIX_KEYBOARD_H +#define _FIWIX_KEYBOARD_H + +#define KEYBOARD_IRQ 1 + +#define NR_MODIFIERS 16 /* max. number of modifiers per keymap */ +#define NR_SCODES 128 /* max. number of scancodes */ +#define NR_DIACR 10 + +#define SCRLBIT 0x01 /* scroll lock led */ +#define NUMSBIT 0x02 /* num lock led */ +#define CAPSBIT 0x04 /* caps lock led */ + +#define C(ch) ((ch) & 0x1F) +#define A(ch) ((ch) | META_KEYS) +#define L(ch) ((ch) | LETTER_KEYS) + +#define SLASH_NPAD 53 + +#define MOD_BASE 0 +#define MOD_SHIFT 1 +#define MOD_ALTGR 2 +#define MOD_CTRL 3 +#define MOD_ALT 4 +#define MOD_SHIFTL 5 +#define MOD_SHIFTR 6 +#define MOD_CTRLL 7 +#define MOD_CTRLR 8 + +#define FN_KEYS 0x100 +#define SPEC_KEYS 0x200 +#define PAD_KEYS 0x300 +#define DEAD_KEYS 0x400 +#define CONS_KEYS 0x500 +#define SHIFT_KEYS 0x700 +#define META_KEYS 0x800 +#define LETTER_KEYS 0xB00 + +#define CR (0x01 + SPEC_KEYS) +#define SCRL2 (0x02 + SPEC_KEYS) /* SH_REGS (show registers) */ +#define SCRL3 (0x03 + SPEC_KEYS) /* SH_MEM (show memory) */ +#define SCRL4 (0x04 + SPEC_KEYS) /* SH_STAT (show status) */ +#define CAPS (0x07 + SPEC_KEYS) +#define NUMS (0x08 + SPEC_KEYS) +#define SCRL (0x09 + SPEC_KEYS) + +#define INS (0x00 + PAD_KEYS) +#define END (0x01 + PAD_KEYS) +#define DOWN (0x02 + PAD_KEYS) +#define PGDN (0x03 + PAD_KEYS) +#define LEFT (0x04 + PAD_KEYS) +#define MID (0x05 + PAD_KEYS) +#define RIGHT (0x06 + PAD_KEYS) +#define HOME (0x07 + PAD_KEYS) +#define UP (0x08 + PAD_KEYS) +#define PGUP (0x09 + PAD_KEYS) +#define PLUS (0x0A + PAD_KEYS) +#define MINUS (0x0B + PAD_KEYS) +#define ASTSK (0x0C + PAD_KEYS) +#define SLASH (0x0D + PAD_KEYS) +#define ENTER (0x0E + PAD_KEYS) +#define DEL (0x10 + PAD_KEYS) + +#define GRAVE (0x00 + DEAD_KEYS) +#define ACUTE (0x01 + DEAD_KEYS) +#define CIRCM (0x02 + DEAD_KEYS) +#define DIERE (0x04 + DEAD_KEYS) + +#define SHIFT (0x00 + SHIFT_KEYS) +#define ALTGR (0x01 + SHIFT_KEYS) +#define CTRL (0x02 + SHIFT_KEYS) +#define ALT (0x03 + SHIFT_KEYS) + +#define F1 (0x00 + FN_KEYS) +#define F2 (0x01 + FN_KEYS) +#define F3 (0x02 + FN_KEYS) +#define F4 (0x03 + FN_KEYS) +#define F5 (0x04 + FN_KEYS) +#define F6 (0x05 + FN_KEYS) +#define F7 (0x06 + FN_KEYS) +#define F8 (0x07 + FN_KEYS) +#define F9 (0x08 + FN_KEYS) +#define F10 (0x09 + FN_KEYS) +#define F11 (0x0A + FN_KEYS) +#define F12 (0x0B + FN_KEYS) + +#define SF1 (0x0A + FN_KEYS) +#define SF2 (0x0B + FN_KEYS) +#define SF3 (0x0C + FN_KEYS) +#define SF4 (0x0D + FN_KEYS) +#define SF5 (0x0E + FN_KEYS) +#define SF6 (0x0F + FN_KEYS) +#define SF7 (0x10 + FN_KEYS) +#define SF8 (0x11 + FN_KEYS) +#define SF9 (0x12 + FN_KEYS) +#define SF10 (0x13 + FN_KEYS) +#define SF11 (0x0A + SHIFT) +#define SF12 (0x0B + SHIFT) + +#define AF1 (0x00 + CONS_KEYS) +#define AF2 (0x01 + CONS_KEYS) +#define AF3 (0x02 + CONS_KEYS) +#define AF4 (0x03 + CONS_KEYS) +#define AF5 (0x04 + CONS_KEYS) +#define AF6 (0x05 + CONS_KEYS) +#define AF7 (0x06 + CONS_KEYS) +#define AF8 (0x07 + CONS_KEYS) +#define AF9 (0x08 + CONS_KEYS) +#define AF10 (0x09 + CONS_KEYS) +#define AF11 (0x0A + CONS_KEYS) +#define AF12 (0x0B + CONS_KEYS) + +struct diacritic { + unsigned char letter; + unsigned char code; +}; + +extern __key_t keymap[NR_MODIFIERS * NR_SCODES]; + +void set_leds(unsigned char); +void irq_keyboard(void); +void keyboard_bh(void); +void keyboard_init(void); + +#endif /* _FIWIX_KEYBOARD_H */ diff --git a/include/fiwix/kparms.h b/include/fiwix/kparms.h new file mode 100644 index 00000000..b75aca2d --- /dev/null +++ b/include/fiwix/kparms.h @@ -0,0 +1,57 @@ +/* + * fiwix/include/fiwix/kparms.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_KPARMS_H +#define _FIWIX_KPARMS_H + +#define CMDL_ARG_LEN 25 /* max length of cmdline argument */ +#define CMDL_NUM_VALUES 30 /* max values of cmdline parameter */ + +struct kparms { + char *name; + char *value[CMDL_NUM_VALUES]; + unsigned int sysval[CMDL_NUM_VALUES]; +}; + +static struct kparms parm_table[] = { + { "root=", + { "/dev/fd0", "/dev/fd1", + "/dev/hda", "/dev/hda1", "/dev/hda2", "/dev/hda3", "/dev/hda4", + "/dev/hdb", "/dev/hdb1", "/dev/hdb2", "/dev/hdb3", "/dev/hdb4", + "/dev/hdc", "/dev/hdc1", "/dev/hdc2", "/dev/hdc3", "/dev/hdc4", + "/dev/hdd", "/dev/hdd1", "/dev/hdd2", "/dev/hdd3", "/dev/hdd4" }, + { 0x200, 0x201, + 0x300, 0x301, 0x302, 0x303, 0x304, + 0x340, 0x341, 0x342, 0x343, 0x344, + 0x1600, 0x1601, 0x1602, 0x1603, 0x1604, + 0x1640, 0x1641, 0x1642, 0x1643, 0x1644 } + }, + { "noramdisk", + { NULL }, + { NULL }, + }, + { "ramdisksize=", + { NULL }, + { NULL }, + }, + { "rootfstype=", + { "minix", "ext2", "iso9660" }, + { 0, 0 } + }, + { "console=", + { "/dev/tty1", "/dev/tty2", "/dev/tty3", "/dev/tty4", "/dev/tty5", + "/dev/tty6", "/dev/tty7", "/dev/tty8", "/dev/tty9", "/dev/tty10", + "/dev/tty11", "/dev/tty12" }, + { 0x401, 0x402, 0x403, 0x404, 0x405, + 0x406, 0x407, 0x408, 0x409, 0x410, + 0x411, 0x412 } + }, + + { NULL } +}; + +#endif /* _FIWIX_KPARMS_H */ diff --git a/include/fiwix/limits.h b/include/fiwix/limits.h new file mode 100644 index 00000000..9a815c22 --- /dev/null +++ b/include/fiwix/limits.h @@ -0,0 +1,27 @@ +/* + * fiwix/include/fiwix/limits.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_LIMITS_H +#define _FIWIX_LIMITS_H + +#define DEVNAME_MAX 25 /* device name length in mount table */ +#define ARG_MAX 32 /* length (in pages) of argv+env in 'execve' */ +#define CHILD_MAX 64 /* simultaneous processes per real user ID */ +#define LINK_MAX 255 /* maximum number of links to a file */ +#define MAX_CANON 255 /* bytes in a terminal canonical input queue */ +#define MAX_INPUT 255 /* bytes for which space will be available in a + terminal input queue */ +#define NGROUPS_MAX 32 /* simultaneous supplementary group IDs */ +#define OPEN_MAX 256 /* files one process can have opened at once */ +#define FD_SETSIZE OPEN_MAX /* descriptors that a process may examine with + 'pselect' or 'select' */ +#define NAME_MAX 255 /* bytes in a filename */ +#define PATH_MAX 1024 /* bytes in a pathname */ +#define PIPE_BUF 4096 /* bytes than can be written atomically to a + pipe */ + +#endif /* _FIWIX_LIMITS_H */ diff --git a/include/fiwix/locks.h b/include/fiwix/locks.h new file mode 100644 index 00000000..072c78cc --- /dev/null +++ b/include/fiwix/locks.h @@ -0,0 +1,29 @@ +/* + * fiwix/include/fiwix/locks.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_LOCKS_H +#define _FIWIX_LOCKS_H + +#include +#include +#include + +struct flock_file { + struct inode *inode; /* file */ + unsigned char type; /* type of lock */ + struct proc *proc; /* owner */ +}; + +struct flock_file flock_file_table[NR_FLOCKS]; + +int posix_lock(int, int, struct flock *); + +void flock_release_inode(struct inode *); +int flock_inode(struct inode *, int); +void flock_init(void); + +#endif /* _FIWIX_LOCKS_H */ diff --git a/include/fiwix/lp.h b/include/fiwix/lp.h new file mode 100644 index 00000000..23662fd4 --- /dev/null +++ b/include/fiwix/lp.h @@ -0,0 +1,48 @@ +/* + * fiwix/include/fiwix/lp.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_LP_H +#define _FIWIX_LP_H + +#include + +#define LP_MAJOR 6 /* major number for /dev/lp[n] */ +#define LP_MINORS 1 + +/*#define LP0_ADDR 0x3BC */ +#define LP0_ADDR 0x378 +/*#define LP2_ADDR 0x278 */ + +#define LP_STAT_ERR 0x08 /* printer error */ +#define LP_STAT_SEL 0x10 /* select in */ +#define LP_STAT_PE 0x20 /* paper empty or no paper */ +#define LP_STAT_ACK 0x40 /* ack */ +#define LP_STAT_BUS 0x80 /* printer busy */ + +#define LP_CTRL_STR 0x01 /* strobe */ +#define LP_CTRL_AUT 0x02 /* auto line feed */ +#define LP_CTRL_INI 0x04 /* initialize printer (reset) */ +#define LP_CTRL_SEL 0x08 /* select printer */ +#define LP_CTRL_IRQ 0x10 /* enable IRQ */ +#define LP_CTRL_BID 0x20 /* bidireccional (on PS/2 ports) */ + +#define LP_RDY_RETR 100 /* retries before timeout */ + +struct lp { + int data; /* data port address */ + int stat; /* status port address */ + int ctrl; /* control port address */ + char flags; /* flags */ +}; + +int lp_open(struct inode *, struct fd *); +int lp_close(struct inode *, struct fd *); +int lp_write(struct inode *, struct fd *, const char *, __size_t); + +void lp_init(void); + +#endif /* _FIWIX_LP_H */ diff --git a/include/fiwix/memdev.h b/include/fiwix/memdev.h new file mode 100644 index 00000000..a45529a5 --- /dev/null +++ b/include/fiwix/memdev.h @@ -0,0 +1,49 @@ +/* + * fiwix/include/fiwix/memdev.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_MEMDEV_H +#define _FIWIX_MEMDEV_H + +#include + +#define MEMDEV_MAJOR 1 /* major number */ +#define MEMDEV_MINORS 5 /* number of supported minors */ + +#define MEMDEV_MEM 1 /* minor for /dev/mem */ +#define MEMDEV_KMEM 2 /* minor for /dev/kmem */ +#define MEMDEV_NULL 3 /* minor for /dev/null */ +#define MEMDEV_ZERO 5 /* minor for /dev/zero */ + +int mem_open(struct inode *, struct fd *); +int mem_close(struct inode *, struct fd *); +int mem_read(struct inode *, struct fd *, char *, __size_t); +int mem_write(struct inode *, struct fd *, const char *, __size_t); +int mem_lseek(struct inode *, __off_t); + +int kmem_open(struct inode *, struct fd *); +int kmem_close(struct inode *, struct fd *); +int kmem_read(struct inode *, struct fd *, char *, __size_t); +int kmem_write(struct inode *, struct fd *, const char *, __size_t); +int kmem_lseek(struct inode *, __off_t); + +int null_open(struct inode *, struct fd *); +int null_close(struct inode *, struct fd *); +int null_read(struct inode *, struct fd *, char *, __size_t); +int null_write(struct inode *, struct fd *, const char *, __size_t); +int null_lseek(struct inode *, __off_t); + +int zero_open(struct inode *, struct fd *); +int zero_close(struct inode *, struct fd *); +int zero_read(struct inode *, struct fd *, char *, __size_t); +int zero_write(struct inode *, struct fd *, const char *, __size_t); +int zero_lseek(struct inode *, __off_t); + +int memdev_open(struct inode *, struct fd *); +int mem_mmap(struct inode *, struct vma *); +void memdev_init(void); + +#endif /* _FIWIX_MEMDEV_H */ diff --git a/include/fiwix/mm.h b/include/fiwix/mm.h new file mode 100644 index 00000000..15d51689 --- /dev/null +++ b/include/fiwix/mm.h @@ -0,0 +1,101 @@ +/* + * fiwix/include/fiwix/mm.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_MEMORY_H +#define _FIWIX_MEMORY_H + +#include +#include +#include + +#define P2V(addr) (addr + KERNEL_BASE_ADDR) +#define V2P(addr) (addr - KERNEL_BASE_ADDR) + +#define PAGE_SIZE 4096 +#define PAGE_SHIFT 0x0C +#define PAGE_MASK ~(PAGE_SIZE - 1) /* 0xFFFFF000 */ +#define PAGE_ALIGN(addr) (((addr) + (PAGE_SIZE - 1)) & PAGE_MASK) +#define PT_ENTRIES (PAGE_SIZE / sizeof(unsigned int)) +#define PD_ENTRIES (PAGE_SIZE / sizeof(unsigned int)) + +#define PAGE_PRESENT 0x001 /* Present */ +#define PAGE_RW 0x002 /* Read/Write */ +#define PAGE_USER 0x004 /* User */ + +#define PAGE_RESERVED 0x100 /* kernel, BIOS address, ... */ +#define PAGE_COW 0x200 /* marked for Copy-On-Write */ + +#define PFAULT_V 0x01 /* protection violation */ +#define PFAULT_W 0x02 /* during write */ +#define PFAULT_U 0x04 /* in user mode */ + +#define GET_PGDIR(address) ((unsigned int)((address) >> 22) & 0x3FF) +#define GET_PGTBL(address) ((unsigned int)((address) >> 12) & 0x3FF) + +#define P_TEXT 1 /* text section */ +#define P_DATA 2 /* data section */ +#define P_BSS 3 /* BSS section */ +#define P_HEAP 4 /* heap section (sys_brk()) */ +#define P_STACK 5 /* stack section */ +#define P_MMAP 6 /* mmap() section */ + +struct page { + unsigned int page; /* page number */ + unsigned int count; /* usage counter */ + unsigned int flags; + unsigned char locked; /* 1 = locked */ + struct inode *inode; + __off_t offset; /* file offset */ + char *data; /* page contents */ + struct page *prev_hash; + struct page *next_hash; + struct page *prev_free; + struct page *next_free; +}; + +extern struct page *page_table; +extern struct page **page_hash_table; + +/* values to be determined during system startup */ +extern unsigned int page_table_size; /* size in bytes */ +extern unsigned int page_hash_table_size; /* size in bytes */ + +extern unsigned int *kpage_dir; +extern unsigned int *kpage_table; + +/* alloc.c */ +unsigned int kmalloc(void); +void kfree(unsigned int); + +/* page.c */ +void page_lock(struct page *); +void page_unlock(struct page *); +struct page * get_free_page(void); +struct page * search_page_hash(struct inode *, __off_t); +void release_page(unsigned int); +int valid_page(unsigned int); +void update_page_cache(struct inode *, __off_t, const char *, int); +int write_page(struct page *, struct inode *, __off_t, unsigned int); +int bread_page(struct page *, struct inode *, __off_t, char, char); +int file_read(struct inode *, struct fd *, char *, __size_t); +void page_init(unsigned int); + +/* memory.c */ +void bss_init(void); +unsigned int setup_minmem(void); +unsigned int get_mapped_addr(struct proc *, unsigned int); +int clone_pages(struct proc *); +int free_page_tables(struct proc *); +unsigned int map_page(struct proc *, unsigned int, unsigned int, unsigned int); +int unmap_page(unsigned int); +void mem_init(void); +void mem_stats(void); + +/* swapper.c */ +int kswapd(void); + +#endif /* _FIWIX_MEMORY_H */ diff --git a/include/fiwix/mman.h b/include/fiwix/mman.h new file mode 100644 index 00000000..5b8b4f53 --- /dev/null +++ b/include/fiwix/mman.h @@ -0,0 +1,59 @@ +/* + * fiwix/include/fiwix/mman.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_MMAN_H +#define _FIWIX_MMAN_H + +#include + +#define PROT_READ 0x1 /* page can be read */ +#define PROT_WRITE 0x2 /* page can be written */ +#define PROT_EXEC 0x4 /* page can be executed */ +#define PROT_NONE 0x0 /* page cannot be accessed */ + +#define MAP_SHARED 0x01 /* share changes */ +#define MAP_PRIVATE 0x02 /* changes are private */ +#define MAP_TYPE 0x0f /* mask for type of mapping */ +#define MAP_FIXED 0x10 /* interpret address exactly */ +#define MAP_ANONYMOUS 0x20 /* don't use the file descriptor */ + +#define MAP_GROWSDOWN 0x0100 /* stack-like segment */ +#define MAP_DENYWRITE 0x0800 /* -ETXTBSY */ +#define MAP_EXECUTABLE 0x1000 /* mark it as a executable */ +#define MAP_LOCKED 0x2000 /* pages are locked */ + +#define ZERO_PAGE 0x80000000 /* this page must be zero-filled */ + +#define MS_ASYNC 1 /* sync memory asynchronously */ +#define MS_INVALIDATE 2 /* invalidate the caches */ +#define MS_SYNC 4 /* synchronous memory sync */ + +#define MCL_CURRENT 1 /* lock all current mappings */ +#define MCL_FUTURE 2 /* lock all future mappings */ + +/* compatibility flags */ +#define MAP_ANON MAP_ANONYMOUS +#define MAP_FILE 0 + +struct mmap { + unsigned int start; + unsigned int length; + unsigned int prot; + unsigned int flags; + int fd; + unsigned int offset; +}; + +void show_vma_regions(struct proc *); +void release_binary(void); +struct vma * find_vma_region(unsigned int); +int expand_heap(unsigned int); +int do_mmap(struct inode *, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, char, char); +int do_munmap(unsigned int, __size_t); +int do_mprotect(struct vma *, unsigned int, __size_t, int); + +#endif /* _FIWIX_MMAN_H */ diff --git a/include/fiwix/multiboot.h b/include/fiwix/multiboot.h new file mode 100644 index 00000000..4a43f8de --- /dev/null +++ b/include/fiwix/multiboot.h @@ -0,0 +1,124 @@ +/* multiboot.h - the header for Multiboot */ +/* Copyright (C) 1999, 2001 Free Software Foundation, Inc. + + 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; either version 2 of the License, or + (at your option) any later version. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Macros. */ + +#ifndef _FIWIX_MULTIBOOT_H +#define _FIWIX_MULTIBOOT_H + +/* The magic number for the Multiboot header. */ +#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 + +/* The flags for the Multiboot header. */ +#ifdef __ELF__ +# define MULTIBOOT_HEADER_FLAGS 0x00000003 +#else +# define MULTIBOOT_HEADER_FLAGS 0x00010003 +#endif + +/* The magic number passed by a Multiboot-compliant boot loader. */ +#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 + +/* The size of our stack (16KB). */ +#define STACK_SIZE 0x4000 + +/* C symbol format. HAVE_ASM_USCORE is defined by configure. */ +#ifdef HAVE_ASM_USCORE +# define EXT_C(sym) _ ## sym +#else +# define EXT_C(sym) sym +#endif + +#ifndef ASM +/* Do not include here in boot.S. */ + +/* Types. */ + +/* The Multiboot header. */ +typedef struct multiboot_header +{ + unsigned long magic; + unsigned long flags; + unsigned long checksum; + unsigned long header_addr; + unsigned long load_addr; + unsigned long load_end_addr; + unsigned long bss_end_addr; + unsigned long entry_addr; +} multiboot_header_t; + +/* The symbol table for a.out. */ +typedef struct aout_symbol_table +{ + unsigned long tabsize; + unsigned long strsize; + unsigned long addr; + unsigned long reserved; +} aout_symbol_table_t; + +/* The section header table for ELF. */ +typedef struct elf_section_header_table +{ + unsigned long num; + unsigned long size; + unsigned long addr; + unsigned long shndx; +} elf_section_header_table_t; + +/* The Multiboot information. */ +typedef struct multiboot_info +{ + unsigned long flags; + unsigned long mem_lower; + unsigned long mem_upper; + unsigned long boot_device; + unsigned long cmdline; + unsigned long mods_count; + unsigned long mods_addr; + union + { + aout_symbol_table_t aout_sym; + elf_section_header_table_t elf_sec; + } u; + unsigned long mmap_length; + unsigned long mmap_addr; +} multiboot_info_t; + +/* The module structure. */ +typedef struct module +{ + unsigned long mod_start; + unsigned long mod_end; + unsigned long string; + unsigned long reserved; +} module_t; + +/* The memory map. Be careful that the offset 0 is base_addr_low + but no size. */ +typedef struct memory_map +{ + unsigned long size; + unsigned long base_addr_low; + unsigned long base_addr_high; + unsigned long length_low; + unsigned long length_high; + unsigned long type; +} memory_map_t; + +#endif /* ! ASM */ + +#endif /* _FIWIX_MULTIBOOT_H */ diff --git a/include/fiwix/part.h b/include/fiwix/part.h new file mode 100644 index 00000000..eb9eac12 --- /dev/null +++ b/include/fiwix/part.h @@ -0,0 +1,38 @@ +/* + * fiwix/include/fiwix/part.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_PART_H +#define _FIWIX_PART_H + +#define PARTITION_BLOCK 0 +#define NR_PARTITIONS 4 /* partitions in the MBR */ +#define MBR_CODE_SIZE 446 +#define ACTIVE_PART 0x80 + +struct hd_geometry { + unsigned char heads; + unsigned char sectors; + unsigned short int cylinders; + unsigned long int start; +}; + +struct partition { + unsigned char status; + unsigned char head; + unsigned char sector; + unsigned char cyl; + unsigned char type; + unsigned char endhead; + unsigned char endsector; + unsigned char endcyl; + unsigned int startsect; + unsigned int nr_sects; +}; + +int read_msdos_partition(__dev_t, struct partition *); + +#endif /* _FIWIX_PART_H */ diff --git a/include/fiwix/pic.h b/include/fiwix/pic.h new file mode 100644 index 00000000..0d35cac2 --- /dev/null +++ b/include/fiwix/pic.h @@ -0,0 +1,54 @@ +/* + * fiwix/include/fiwix/pic.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_PIC_H +#define _FIWIX_PIC_H + +#include + +#define NR_IRQS 16 /* hardware interrupts */ +#define PIC_MASTER 0x20 /* I/O base address for master PIC */ +#define PIC_SLAVE 0xA0 /* I/O base address for slave PIC */ + +#define DATA 0x01 /* offset to data port */ +#define EOI 0x20 /* End-Of-Interrupt command code */ + +/* Inicialization Command Words */ +#define ICW1_RESET 0x11 /* ICW1_INIT + ICW1_ICW4 */ +#define CASCADE_IRQ 0x02 +#define ICW4_8086EOI 0x01 + +#define PIC_READ_IRR 0x0A /* OCW3 irq ready */ +#define PIC_READ_ISR 0x0B /* OCW3 irq service */ + +/* Operational Command Words */ +#define OCW1 0xFF /* mask (disable) all IRQs */ + +struct interrupts { + unsigned int ticks; + char *name; + char registered; + void (*handler)(void *); +}; +struct interrupts irq_table[NR_IRQS]; + +struct bh { + void (*fn)(void); + struct bh *next; +}; + +void add_bh(void (*fn)(void)); +void del_bh(void); +void enable_irq(int); +void disable_irq(int); +int register_irq(int, char *, void *); +int unregister_irq(int); +void irq_handler(int, struct sigcontext); +void do_bh(void); +void pic_init(void); + +#endif /* _FIWIX_PIC_H */ diff --git a/include/fiwix/pit.h b/include/fiwix/pit.h new file mode 100644 index 00000000..03b6ac85 --- /dev/null +++ b/include/fiwix/pit.h @@ -0,0 +1,51 @@ +/* + * fiwix/include/fiwix/pit.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_PIT_H +#define _FIWIX_PIT_H + +/* Intel 8253/82c54 Programmable Interval Timer */ + +#define OSCIL 1193182 /* oscillator frequency */ + +#define MODEREG 0x43 /* mode/command register (w) */ +#define CHANNEL0 0x40 /* channel 0 data port (rw) */ +#define CHANNEL1 0x41 /* channel 1 data port (rw) */ +#define CHANNEL2 0x42 /* channel 2 data port (rw) */ + +#define BINARY_CTR 0x00 /* 16bit binary mode counter */ +#define TERM_COUNT 0x00 /* mode 0 (Terminal Count) */ +#define RATE_GEN 0x04 /* mode 2 (Rate Generator) */ +#define SQUARE_WAVE 0x06 /* mode 3 (Square Wave) */ +#define LSB_MSB 0x30 /* LSB then MSB */ +#define SEL_CHAN0 0x00 /* select channel 0 */ +#define SEL_CHAN2 0x80 /* select channel 2 */ + +/* + * PS/2 System Control Port B + * --------------------------------------- + * bit 7 -> IRQ=1, 0=reset + * bit 6 -> reserved + * bit 5 -> reserved + * bit 4 -> reserved + * bit 3 -> channel check enable + * bit 2 -> parity check enable + * bit 1 -> speaker data enable + * bit 0 -> timer 2 gate to speaker enable + */ +#define PS2_SYSCTRL_B 0x61 /* PS/2 system control port B (write) */ + +#define ENABLE_TMR2G 0x01 /* timer 2 gate to speaker enable */ +#define ENABLE_SDATA 0x02 /* speaker data enable */ + +#define BEEP_FREQ 900 /* 900Hz */ + +void pit_beep_on(void); +void pit_beep_off(unsigned int); +void pit_init(unsigned short int); + +#endif /* _FIWIX_PIT_H */ diff --git a/include/fiwix/process.h b/include/fiwix/process.h new file mode 100644 index 00000000..150d8904 --- /dev/null +++ b/include/fiwix/process.h @@ -0,0 +1,181 @@ +/* + * fiwix/include/fiwix/process.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_PROCESS_H +#define _FIWIX_PROCESS_H + +struct vma { + unsigned int start; + unsigned int end; + char prot; /* PROT_READ, PROT_WRITE, ... */ + unsigned int flags; /* MAP_SHARED, MAP_PRIVATE, ... */ + unsigned int offset; + char s_type; /* section type (P_TEXT, P_DATA, ...) */ + struct inode *inode; /* file inode */ + char o_mode; /* open mode (O_RDONLY, O_RDWR, ...) */ +}; + +#include +#include +#include +#include +#include +#include +#include + +#define IDLE 0 /* PID of idle */ +#define INIT 1 /* PID of /sbin/init */ +#define SAFE_SLOTS 2 /* process slots reserved for root */ +#define SLOT(p) ((p) - (&proc_table[0])) + +/* bits in flags */ +#define PF_KPROC 0x00000001 /* kernel internal process */ +#define PF_PEXEC 0x00000002 /* has performed a sys_execve() */ +#define PF_USEREAL 0x00000004 /* use real UID in permission checks */ + +#define MMAP_START 0x40000000 /* mmap()s start at 1GB */ +#define IS_SUPERUSER (current->euid == 0) + +#define IO_BITMAP_SIZE 32 /* 32 * 32bit = 1024 = 0x3FF */ + /* 2048 * 32bit = 65536 = 0xFFFF */ + +#define VMA_REGIONS (PAGE_SIZE / sizeof(struct vma)) + +#define PG_LEADER(p) ((p)->pid == (p)->pgid) +#define SESS_LEADER(p) ((p)->pid == (p)->pgid && (p)->pid == (p)->sid) + +#define FOR_EACH_PROCESS(p) for(p = &proc_table[INIT]; p; p = p->next) + +/* value to be determined during system startup */ +extern unsigned int proc_table_size; /* size in bytes */ + +extern char any_key_to_reboot; +extern int nr_processes; +extern __pid_t lastpid; + +struct binargs { + unsigned int page[ARG_MAX]; + int argc; + int argv_len; + int envc; + int envp_len; + int offset; +}; + +/* Intel 386 Task Switch State */ +struct i386tss { + unsigned int prev_tss; + unsigned int esp0; + unsigned int ss0; + unsigned int esp1; + unsigned int ss1; + unsigned int esp2; + unsigned int ss2; + unsigned int cr3; + unsigned int eip; + unsigned int eflags; + unsigned int eax; + unsigned int ecx; + unsigned int edx; + unsigned int ebx; + unsigned int esp; + unsigned int ebp; + unsigned int esi; + unsigned int edi; + unsigned int es; + unsigned int cs; + unsigned int ss; + unsigned int ds; + unsigned int fs; + unsigned int gs; + unsigned int ldt; + unsigned short int debug_trap; + unsigned short int io_bitmap_addr; +}; + +struct proc { + struct i386tss tss; + unsigned int io_bitmap[IO_BITMAP_SIZE + 1]; + __pid_t pid; /* process ID */ + __pid_t ppid; /* parent process ID */ + __pid_t pgid; /* process group ID */ + __pid_t sid; /* session ID */ + int flags; + int groups[NGROUPS_MAX]; + int children; /* number of children */ + struct tty *ctty; /* controlling terminal */ + int state; /* process state */ + int priority; + int cpu_count; /* time of process running */ + __time_t start_time; + int exit_code; + void *sleep_address; + unsigned short int uid; /* real user ID */ + unsigned short int gid; /* real group ID */ + unsigned short int euid; /* effective user ID */ + unsigned short int egid; /* effective group ID */ + unsigned short int suid; /* saved user ID */ + unsigned short int sgid; /* saved group ID */ + unsigned short int fd[OPEN_MAX]; + unsigned char fd_flags[OPEN_MAX]; + struct inode *root; + struct inode *pwd; /* process working directory */ + unsigned int entry_address; + char argv0[NAME_MAX + 1]; + char **argv; + char **envp; + char pidstr[5]; /* pid number converted to string */ + struct vma *vma; /* virtual memory-map addresses */ + unsigned int brk_lower; /* lower limit of the heap section */ + unsigned int brk; /* current limit of the heap */ + __sigset_t sigpending; + __sigset_t sigblocked; + __sigset_t sigexecuting; + struct sigaction sigaction[NSIG]; + struct sigcontext sc[NSIG]; /* each signal has its own context */ + unsigned int sp; /* current process' stack frame */ + struct rusage usage; /* process resource usage */ + struct rusage cusage; /* children resource usage */ + unsigned long int it_real_interval, it_real_value; + unsigned long int it_virt_interval, it_virt_value; + unsigned long int it_prof_interval, it_prof_value; + unsigned long int timeout; + struct rlimit rlim[RLIM_NLIMITS]; + unsigned long int rss; + __mode_t umask; + unsigned char loopcnt; /* nested symlinks counter */ + struct proc *prev; + struct proc *next; + struct proc *sleep_prev; + struct proc *sleep_next; +}; + +extern struct proc *current; +extern struct proc *proc_table; + +int send_sig(struct proc *, __sigset_t); +int kill_pid(__pid_t, __sigset_t); +int kill_pgrp(__pid_t, __sigset_t); +void add_crusage(struct proc *, struct rusage *); +void get_rusage(struct proc *, struct rusage *); +void add_rusage(struct proc *); +struct proc * get_next_zombie(struct proc *); +__pid_t remove_zombie(struct proc *); +int is_orphaned_pgrp(__pid_t); +struct proc * get_proc_free(void); +void release_proc(struct proc *); +int get_unused_pid(void); +struct proc * get_proc_by_pid(__pid_t); + +int get_new_user_fd(int); +void release_user_fd(int); + +struct proc * kernel_process(int (*fn)(void)); +void proc_slot_init(struct proc *); +void proc_init(void); + +#endif /* _FIWIX_PROCESS_H */ diff --git a/include/fiwix/ramdisk.h b/include/fiwix/ramdisk.h new file mode 100644 index 00000000..cbd24e6a --- /dev/null +++ b/include/fiwix/ramdisk.h @@ -0,0 +1,33 @@ +/* + * fiwix/include/fiwix/ramdisk.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_RAMDISK_H +#define _FIWIX_RAMDISK_H + +#include + +#define RAMDISK_MAJOR 1 /* ramdisk device major number */ +#define RAMDISK_MINORS 1 /* number of minors */ +#define RAMDISK_SIZE 4096 /* default ramdisk size in KBs */ +#define RAMDISK_MAXSIZE 131072 /* maximum ramdisk size in KBs */ + +struct ramdisk { + char *addr; /* ramdisk memory address */ +}; + +extern struct ramdisk ramdisk_table[RAMDISK_MINORS]; + +int ramdisk_open(struct inode *, struct fd *); +int ramdisk_close(struct inode *, struct fd *); +int ramdisk_read(__dev_t, __blk_t, char *, int); +int ramdisk_write(__dev_t, __blk_t, char *, int); +int ramdisk_ioctl(struct inode *, int, unsigned long int); +int ramdisk_lseek(struct inode *, __off_t); + +void ramdisk_init(void); + +#endif /* _FIWIX_RAMDISK_H */ diff --git a/include/fiwix/reboot.h b/include/fiwix/reboot.h new file mode 100644 index 00000000..1a01023d --- /dev/null +++ b/include/fiwix/reboot.h @@ -0,0 +1,23 @@ +/* + * fiwix/include/fiwix/reboot.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_REBOOT_H +#define _FIWIX_REBOOT_H + +#define BMAGIC_HARD 0x89ABCDEF +#define BMAGIC_SOFT 0 +#define BMAGIC_REBOOT 0x01234567 +#define BMAGIC_HALT 0xCDEF0123 +#define BMAGIC_POWEROFF 0x4321FEDC + +#define BMAGIC_1 0xFEE1DEAD +#define BMAGIC_2 672274793 + +extern char ctrl_alt_del; +void reboot(void); + +#endif /* _FIWIX_REBOOT_H */ diff --git a/include/fiwix/resource.h b/include/fiwix/resource.h new file mode 100644 index 00000000..3f267be9 --- /dev/null +++ b/include/fiwix/resource.h @@ -0,0 +1,81 @@ +/* + * fiwix/include/fiwix/resource.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_RESOURCE_H +#define _FIWIX_RESOURCE_H + +#include + +#define RLIMIT_INFINITY 0x7FFFFFFF /* value to indicate "no limit" */ +#define RLIM_INFINITY RLIMIT_INFINITY /* traditional name */ + +#define RUSAGE_SELF 0 /* the calling process */ +#define RUSAGE_CHILDREN (-1) /* all of its termin. child processes */ + +#define RLIMIT_CPU 0 /* per-process CPU limit (secs) */ +#define RLIMIT_FSIZE 1 /* largest file that can be created + (bytes */ +#define RLIMIT_DATA 2 /* maximum size of data segment + (bytes) */ +#define RLIMIT_STACK 3 /* maximum size of stack segment + (bytes) */ +#define RLIMIT_CORE 4 /* largest core file that can be created + (bytes) */ +#define RLIMIT_RSS 5 /* largest resident set size (bytes) */ +#define RLIMIT_NPROC 6 /* number of processes */ +#define RLIMIT_NOFILE 7 /* number of open files */ +#define RLIMIT_MEMLOCK 8 /* locked-in-memory address space */ +#define RLIMIT_AS 9 /* address space limit */ + +#define RLIM_NLIMITS 10 + +struct rusage { + struct timeval ru_utime; /* total amount of user time used */ + struct timeval ru_stime; /* total amount of system time used */ + long ru_maxrss; /* maximum resident set size (KB) */ + long ru_ixrss; /* amount of sharing of text segment + memory with other processes + (KB-secs) */ + long ru_idrss; /* amount of data segment memory used + (KB-secs) */ + long ru_isrss; /* amount of stack memory used + (KB-secs) */ + long ru_minflt; /* number of soft page faults (i.e. + those serviced by reclaiming a page + from the list of pages awaiting + rellocation) */ + long ru_majflt; /* number of hard page faults (i.e. + those that required I/O) */ + long ru_nswap; /* number of times a process was swapped + out of physical memory */ + long ru_inblock; /* number of input operations via the + file system. Note this and + 'ru_outblock' do not include + operations with the cache */ + long ru_oublock; /* number of output operations via the + file system */ + long ru_msgsnd; /* number of IPC messages sent */ + long ru_msgrcv; /* number of IPC messages received */ + long ru_nsignals; /* number of signals delivered */ + long ru_nvcsw; /* number of voluntary context switches, + i.e. because the process gave up the + process before it had to (usually to + wait for some resouce to be + availabe */ + long ru_nivcsw; /* number of involuntary context + switches. i.e. a higher priority + process became runnable or the + current process used up its time + slice */ +}; + +struct rlimit { + int rlim_cur; /* the current (soft) limit */ + int rlim_max; /* the maximum (hard) limit */ +}; + +#endif /* _FIWIX_RESOURCE_H */ diff --git a/include/fiwix/sched.h b/include/fiwix/sched.h new file mode 100644 index 00000000..cb4d6380 --- /dev/null +++ b/include/fiwix/sched.h @@ -0,0 +1,56 @@ +/* + * fiwix/include/fiwix/sched.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_SCHED_H +#define _FIWIX_SCHED_H + +#include + +#define PRIO_PROCESS 0 +#define PRIO_PGRP 1 +#define PRIO_USER 2 + +#define PROC_UNUSED 0 +#define PROC_RUNNING 1 +#define PROC_SLEEPING 2 +#define PROC_ZOMBIE 3 +#define PROC_STOPPED 4 +#define PROC_IDLE 5 + +#define PROC_INTERRUPTIBLE 1 +#define PROC_UNINTERRUPTIBLE 2 + +#define DEF_PRIORITY (20 * HZ / 100) /* 200ms of time slice */ + +extern int need_resched; + +#define SI_LOAD_SHIFT 16 + +/* + * This was brougth from Linux 2.0.30 (sched.h). + * Copyright Linus Torvalds et al. + */ +extern unsigned int avenrun[3]; /* Load averages */ +#define FSHIFT 11 /* nr of bits of precision */ +#define FIXED_1 (1<>= FSHIFT; +/* ------------------------------------------------------------------------ */ + + +void do_sched(void); +void set_tss(struct proc *); +void sched_init(void); + +#endif /* _FIWIX_SCHED_H */ diff --git a/include/fiwix/segments.h b/include/fiwix/segments.h new file mode 100644 index 00000000..bcc144c8 --- /dev/null +++ b/include/fiwix/segments.h @@ -0,0 +1,69 @@ +/* + * fiwix/include/fiwix/segments.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_SEGMENTS_H +#define _FIWIX_SEGMENTS_H + +#include + +#define NR_GDT_ENTRIES 6 /* entries in GDT descriptor */ +#define NR_IDT_ENTRIES 256 /* entries in IDT descriptor */ + +/* low flags of Segment Descriptors */ +#define SD_DATA 0x02 /* DATA Read/Write */ +#define SD_CODE 0x0A /* CODE Exec/Read */ + +#define SD_32TSSA 0x09 /* 32-bit TSS (Available) */ +#define SD_32TSSB 0x0B /* 32-bit TSS (Busy) */ +#define SD_32CALLGATE 0x0C /* 32-bit Call Gate */ +#define SD_32INTRGATE 0x0E /* 32-bit Interrupt Gate (0D110) */ +#define SD_32TRAPGATE 0x0F /* 32-bit Trap Gate (0D111) */ + +#define SD_CD 0x10 /* 0 = system / 1 = code/data */ +#define SD_DPL0 0x00 /* priority level 0 */ +#define SD_DPL1 0x20 /* priority level 1 (unused) */ +#define SD_DPL2 0x40 /* priority level 2 (unused) */ +#define SD_DPL3 0x60 /* priority level 3 (user) */ +#define SD_PRESENT 0x80 /* segment present or valid */ + +/* high flags Segment Descriptors */ +#define SD_OPSIZE32 0x04 /* 32-bit code and data segments */ +#define SD_PAGE4KB 0x08 /* page granularity (4KB) */ + +/* low flags of the TSS Descriptors */ +#define SD_TSSPRESENT 0x89 /* TSS present and not busy flag */ + +#define USR_PL 3 /* User Privilege Level */ + +/* EFLAGS */ +#define EF_IOPL 12 /* IOPL bit */ + +struct desc_r { + __u16 limit; + __u32 base_addr; +} __attribute__((packed)); + +struct seg_desc { + unsigned sd_lolimit : 16; /* segment limit 0-15 bits */ + unsigned sd_lobase : 24; /* base address 0-23 bits */ + unsigned sd_loflags : 8; /* flags (P, DPL, S and TYPE) */ + unsigned sd_hilimit : 4; /* segment limit 16-19 bits */ + unsigned sd_hiflags : 4; /* flags (G, DB, 0 and AVL) */ + unsigned sd_hibase : 8; /* base address 24-31 bits */ +} __attribute__((packed)); + +struct gate_desc { + unsigned gd_looffset: 16; /* offset 0-15 bits */ + unsigned gd_selector: 16; /* segment selector */ + unsigned gd_flags : 16; /* flags (P, DPL, TYPE, 0 and NULL) */ + unsigned gd_hioffset: 16; /* offset 16-31 bits */ +} __attribute__((packed)); + +void gdt_init(void); +void idt_init(void); + +#endif /* _FIWIX_SEGMENTS_H */ diff --git a/include/fiwix/sigcontext.h b/include/fiwix/sigcontext.h new file mode 100644 index 00000000..3009276d --- /dev/null +++ b/include/fiwix/sigcontext.h @@ -0,0 +1,32 @@ +/* + * fiwix/include/fiwix/sigcontext.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_SIGCONTEXT_H +#define _FIWIX_SIGCONTEXT_H + +struct sigcontext { + unsigned int gs; + unsigned int fs; + unsigned int es; + unsigned int ds; + unsigned int edi; + unsigned int esi; + unsigned int ebp; + unsigned int esp; + int ebx; + int edx; + int ecx; + int eax; + int err; + unsigned int eip; + unsigned int cs; + unsigned int eflags; + unsigned int oldesp; + unsigned int oldss; +}; + +#endif /* _FIWIX_SIGCONTEXT_H */ diff --git a/include/fiwix/signal.h b/include/fiwix/signal.h new file mode 100644 index 00000000..59bb26ae --- /dev/null +++ b/include/fiwix/signal.h @@ -0,0 +1,92 @@ +/* + * fiwix/include/fiwix/signal.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_SIGNAL_H +#define _FIWIX_SIGNAL_H + +#define NSIG 32 + +#define SIGHUP 1 /* Hangup or Reset */ +#define SIGINT 2 /* Interrupt */ +#define SIGQUIT 3 /* Quit */ +#define SIGILL 4 /* Illegal Instruction */ +#define SIGTRAP 5 /* Trace Trap */ +#define SIGABRT 6 /* Abort Instruction */ +#define SIGIOT SIGABRT /* I/O Trap Instruction */ +#define SIGBUS 7 /* Bus Error */ +#define SIGFPE 8 /* Floating Point Exception */ +#define SIGKILL 9 /* Kill */ +#define SIGUSR1 10 /* User Defined #1 */ +#define SIGSEGV 11 /* Segmentation Violation */ +#define SIGUSR2 12 /* User Defined #2 */ +#define SIGPIPE 13 /* Broken Pipe */ +#define SIGALRM 14 /* Alarm Clock */ +#define SIGTERM 15 /* Software Termination */ +#define SIGSTKFLT 16 /* Stack Fault */ +#define SIGCHLD 17 /* Child Termination */ +#define SIGCONT 18 /* Continue */ +#define SIGSTOP 19 /* Stop */ +#define SIGTSTP 20 /* Terminal Stop */ +#define SIGTTIN 21 /* Background Read */ +#define SIGTTOU 22 /* Background Write */ +#define SIGURG 23 /* Urgent Data */ +#define SIGXCPU 24 /* CPU eXceeded */ +#define SIGXFSZ 25 /* File Size eXceeded */ +#define SIGVTALRM 26 /* Virtual Time Alarm */ +#define SIGPROF 27 /* Profile Alarm */ +#define SIGWINCH 28 /* Window Change */ +#define SIGIO 29 /* I/O Asyncronous */ +#define SIGPOLL SIGIO +#define SIGPWR 30 /* Power Fault */ +#define SIGUNUSED 31 + +typedef unsigned long int __sigset_t; +typedef void (*__sighandler_t)(int); + +struct sigaction { + __sighandler_t sa_handler; + __sigset_t sa_mask; + int sa_flags; + void (*sa_restorer)(void); +}; + +#define SIG_DFL ((__sighandler_t) 0) +#define SIG_IGN ((__sighandler_t) 1) +#define SIG_ERR ((__sighandler_t) -1) + +/* bits in sa_flags */ +#define SA_NOCLDSTOP 0x00000001 /* don't send SIGCHLD when children stop */ +#define SA_NOCLDWAIT 0x00000002 /* don't create zombie on child death */ +#define SA_ONSTACK 0x08000000 /* invoke handler on alternate stack */ +#define SA_RESTART 0x10000000 /* automatically restart system call */ +#define SA_INTERRUPT 0x20000000 /* unused */ + +/* don't automatically block signal when the handler is executing */ +#define SA_NODEFER 0x40000000 +#define SA_NOMASK SA_NODEFER + +/* reset signal disposition to SIG_DFL before invoking handler */ +#define SA_RESETHAND 0x80000000 +#define SA_ONESHOT SA_RESETHAND + +/* bits in the third argument to 'waitpid/wait4' */ +#define WNOHANG 1 /* don't block waiting */ +#define WUNTRACED 2 /* report status of stopped children */ + +#define SIG_BLOCK 0 /* for blocking signals */ +#define SIG_UNBLOCK 1 /* for unblocking signals */ +#define SIG_SETMASK 2 /* for setting the signal mask */ + +/* SIGKILL and SIGSTOP can't ever be set as blockable signals */ +#define SIG_BLOCKABLE (~(1 << (SIGKILL - 1)) | (1 << (SIGSTOP - 1))) + +#define SIG_MASK(sig) (~(1 << ((sig) - 1))) + +int issig(void); +void psig(unsigned int); + +#endif /* _FIWIX_SIGNAL_H */ diff --git a/include/fiwix/sleep.h b/include/fiwix/sleep.h new file mode 100644 index 00000000..966f769a --- /dev/null +++ b/include/fiwix/sleep.h @@ -0,0 +1,38 @@ +/* + * fiwix/include/fiwix/sleep.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_SLEEP_H +#define _FIWIX_SLEEP_H + +#include + +#define AREA_TTY_READ 0x00000001 +#define AREA_CALLOUT 0x00000002 + +struct sleep { + unsigned short int next; + void *address; + struct proc *proc; +}; + +struct resource { + char locked; + char wanted; +}; + +int sleep(void *, int); +void wakeup(void *); +void wakeup_proc(struct proc *); + +void lock_resource(struct resource *); +void unlock_resource(struct resource *); +int lock_area(unsigned int); +int unlock_area(unsigned int); + +void sleep_init(void); + +#endif /* _FIWIX_SLEEP_H */ diff --git a/include/fiwix/stat.h b/include/fiwix/stat.h new file mode 100644 index 00000000..4639949b --- /dev/null +++ b/include/fiwix/stat.h @@ -0,0 +1,56 @@ +#ifndef _FIWIX_STAT_H +#define _FIWIX_STAT_H + +#include + +/* Encoding of the file mode. These are the standard Unix values, + but POSIX.1 does not specify what values should be used. */ + +#define S_IFMT 0170000 /* Type of file mask */ + +/* File types. */ +#define S_IFIFO 0010000 /* Named pipe (fifo) */ +#define S_IFCHR 0020000 /* Character special */ +#define S_IFDIR 0040000 /* Directory */ +#define S_IFBLK 0060000 /* Block special */ +#define S_IFREG 0100000 /* Regular */ +#define S_IFLNK 0120000 /* Symbolic link */ +#define S_IFSOCK 0140000 /* Socket */ + +/* Protection bits. */ +#define S_IXUSR 00100 /* USER --x------ */ +#define S_IWUSR 00200 /* USER -w------- */ +#define S_IRUSR 00400 /* USER r-------- */ +#define S_IRWXU 00700 /* USER rwx------ */ + +#define S_IXGRP 00010 /* GROUP -----x--- */ +#define S_IWGRP 00020 /* GROUP ----w---- */ +#define S_IRGRP 00040 /* GROUP ---r----- */ +#define S_IRWXG 00070 /* GROUP ---rwx--- */ + +#define S_IXOTH 00001 /* OTHERS --------x */ +#define S_IWOTH 00002 /* OTHERS -------w- */ +#define S_IROTH 00004 /* OTHERS ------r-- */ +#define S_IRWXO 00007 /* OTHERS ------rwx */ + +#define S_ISUID 0004000 /* set user id on execution */ +#define S_ISGID 0002000 /* set group id on execution */ +#define S_ISVTX 0001000 /* sticky bit */ + +#define S_IREAD S_IRUSR /* Read by owner. */ +#define S_IWRITE S_IWUSR /* Write by owner. */ +#define S_IEXEC S_IXUSR /* Execute by owner. */ + +#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) + +#define TO_READ 4 /* test for read permission */ +#define TO_WRITE 2 /* test for write permission */ +#define TO_EXEC 1 /* test for execute permission */ + +#endif /* _FIWIX_STAT_H */ diff --git a/include/fiwix/statbuf.h b/include/fiwix/statbuf.h new file mode 100644 index 00000000..89f4bfae --- /dev/null +++ b/include/fiwix/statbuf.h @@ -0,0 +1,48 @@ +/* + * fiwix/include/fiwix/statbuf.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_STATBUF_H +#define _FIWIX_STATBUF_H + +struct old_stat { + __dev_t st_dev; + unsigned short int st_ino; + __mode_t st_mode; + __nlink_t st_nlink; + __uid_t st_uid; + __gid_t st_gid; + __dev_t st_rdev; + unsigned int st_size; + __time_t st_atime; + __time_t st_mtime; + __time_t st_ctime; +}; + +struct new_stat { + __dev_t st_dev; + unsigned short int __pad1; + __ino_t st_ino; + __mode_t st_mode; + __nlink_t st_nlink; + __uid_t st_uid; + __gid_t st_gid; + __dev_t st_rdev; + unsigned short int __pad2; + __off_t st_size; + __blk_t st_blksize; + __blk_t st_blocks; + __time_t st_atime; + unsigned int __unused1; + __time_t st_mtime; + unsigned int __unused2; + __time_t st_ctime; + unsigned int __unused3; + unsigned int __unused4; + unsigned int __unused5; +}; + +#endif /* _FIWIX_STATBUF_H */ diff --git a/include/fiwix/statfs.h b/include/fiwix/statfs.h new file mode 100644 index 00000000..c97bb884 --- /dev/null +++ b/include/fiwix/statfs.h @@ -0,0 +1,28 @@ +/* + * fiwix/include/fiwix/statfs.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_STATFS_H +#define _FIWIX_STATFS_H + +typedef struct { + long int val[2]; +} fsid_t; + +struct statfs { + long int f_type; + long int f_bsize; + long int f_blocks; + long int f_bfree; + long int f_bavail; + long int f_files; + long int f_ffree; + fsid_t f_fsid; + long int f_namelen; + long int f_spare[6]; +}; + +#endif /* _FIWIX_STATFS_H */ diff --git a/include/fiwix/stdarg.h b/include/fiwix/stdarg.h new file mode 100644 index 00000000..78c7351f --- /dev/null +++ b/include/fiwix/stdarg.h @@ -0,0 +1,42 @@ +/* +Copyright (C) 1988 Free Software Foundation + +This file is part of GNU CC. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY. No author or distributor +accepts responsibility to anyone for the consequences of using it +or for whether it serves any particular purpose or works at all, +unless he says so in writing. Refer to the GNU CC General Public +License for full details. + +Everyone is granted permission to copy, modify and redistribute +GNU CC, but only under the conditions described in the +GNU CC General Public License. A copy of this license is +supposed to have been given to you along with GNU CC so you +can know your rights and responsibilities. It should be in a +file named COPYING. Among other things, the copyright notice +and this notice must be preserved on all copies. +*/ + +#ifndef __stdarg_h +#define __stdarg_h + +typedef char *va_list; + +/* Amount of space required in an argument list for an arg of type TYPE. + TYPE may alternatively be an expression whose type is used. */ + +#define __va_rounded_size(TYPE) \ + (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int)) + +#define va_start(AP, LASTARG) \ + (AP = ((char *) &(LASTARG) + __va_rounded_size(LASTARG))) + +extern void va_end (va_list); +#define va_end(AP) /* Nothing */ + +#define va_arg(AP, TYPE) (AP += __va_rounded_size (TYPE), \ + *((TYPE *) (AP - __va_rounded_size (TYPE)))) + +#endif /* __stdarg_h */ diff --git a/include/fiwix/stdio.h b/include/fiwix/stdio.h new file mode 100644 index 00000000..8e901d40 --- /dev/null +++ b/include/fiwix/stdio.h @@ -0,0 +1,15 @@ +/* + * fiwix/include/fiwix/stdio.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _INCLUDE_STDIO_H +#define _INCLUDE_STDIO_H + +void register_console(void (*fn)(char *, unsigned int)); +void printk(const char *, ...); +int sprintk(char *, const char *, ...); + +#endif /* _INCLUDE_STDIO_H */ diff --git a/include/fiwix/string.h b/include/fiwix/string.h new file mode 100644 index 00000000..b5af5aa3 --- /dev/null +++ b/include/fiwix/string.h @@ -0,0 +1,42 @@ +/* + * fiwix/include/fiwix/string.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _INCLUDE_STRING_H +#define _INCLUDE_STRING_H + +#include + +#ifndef NULL +#define NULL '\0' /* ((void *)0) */ +#endif + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +#define IS_NUMERIC(c) ((c) >= '0' && (c) <= '9') +#define IS_SPACE(c) ((c) == ' ') + +void swap_asc_word(char *, int); +int strcmp(const char *, const char *); +int strncmp(const char *, const char *, __ssize_t); +char * strcpy(char *, const char *); +void strncpy(char *, const char *, int); +char * strcat(char *, const char *); +char * strncat(char *, const char *, __ssize_t); +int strlen(const char *); +char * get_basename(const char *); +char * remove_trailing_slash(char *); +int is_dir(const char *); +int atoi(const char *); +void memcpy_b(void *, const void *, unsigned int); +void memcpy_w(void *, const void *, unsigned int); +void memcpy_l(void *, const void *, unsigned int); +void memset_b(void *, unsigned char, unsigned int); +void memset_w(void *, unsigned short int, unsigned int); +void memset_l(void *, unsigned int, unsigned int); + +#endif /* _INCLUDE_STRING_H */ diff --git a/include/fiwix/syscalls.h b/include/fiwix/syscalls.h new file mode 100644 index 00000000..e0400aff --- /dev/null +++ b/include/fiwix/syscalls.h @@ -0,0 +1,147 @@ +/* + * fiwix/include/fiwix/syscalls.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_SYSCALLS_H +#define _FIWIX_SYSCALLS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NR_SYSCALLS (sizeof(syscall_table) / sizeof(unsigned int)) + +int do_syscall(unsigned int, int, int, int, int, int, struct sigcontext); + +int sys_exit(int); +void do_exit(int); +int sys_fork(int, int, int, int, int, struct sigcontext *); +int sys_read(unsigned int, char *, int); +int sys_write(unsigned int, const char *, int); +int sys_open(const char *, int, __mode_t); +int sys_close(unsigned int); +int sys_waitpid(__pid_t, int *, int); +int sys_creat(const char *, __mode_t); +int sys_link(const char *, const char *); +int sys_unlink(const char *); +int sys_execve(const char *, char **, char **, int, int, struct sigcontext *); +int sys_chdir(const char *); +int sys_time(__time_t *); +int sys_mknod(const char *, __mode_t, __dev_t); +int sys_chmod(const char *, __mode_t); +int sys_chown(const char *, __uid_t, __gid_t); +int sys_stat(const char *, struct old_stat *); +int sys_lseek(unsigned int, __off_t, unsigned int); +int sys_getpid(void); +int sys_mount(const char *, const char *, const char *, unsigned long int, const void *); +int sys_umount(const char *); +int sys_setuid(__uid_t); +int sys_getuid(void); +int sys_stime(__time_t *); +int sys_alarm(unsigned int); +int sys_fstat(unsigned int, struct old_stat *); +int sys_pause(void); +int sys_utime(const char *, struct utimbuf *); +int sys_access(const char *, __mode_t); +int sys_ftime(struct timeb *); +void sys_sync(void); +int sys_kill(__pid_t, __sigset_t); +int sys_rename(const char *, const char *); +int sys_mkdir(const char *, __mode_t); +int sys_rmdir(const char *); +int sys_dup(unsigned int); +int sys_pipe(int *); +int sys_times(struct tms *); +int sys_brk(unsigned int); +int sys_setgid(__gid_t); +int sys_getgid(void); +unsigned int sys_signal(__sigset_t, void(*sighandler)(int)); +int sys_geteuid(void); +int sys_getegid(void); +int sys_umount2(const char *, int); +int sys_ioctl(unsigned int, int, unsigned long int); +int sys_fcntl(int, int, unsigned long int); +int sys_setpgid(__pid_t, __pid_t); +int sys_olduname(struct oldold_utsname *); +int sys_umask(__mode_t); +int sys_chroot(const char *); +int sys_ustat(__dev_t, struct ustat *); +int sys_dup2(int, int); +int sys_getppid(void); +int sys_getpgrp(void); +int sys_setsid(void); +int sys_sigaction(__sigset_t, const struct sigaction *, struct sigaction *); +int sys_sgetmask(void); +int sys_ssetmask(int); +int sys_setreuid(__uid_t, __uid_t); +int sys_setregid(__gid_t, __gid_t); +int sys_sigsuspend(__sigset_t *); +int sys_sigpending(__sigset_t *); +int sys_sethostname(const char *, int); +int sys_setrlimit(int, const struct rlimit *); +int sys_getrlimit(int, struct rlimit *); +int sys_getrusage(int, struct rusage *); +int sys_gettimeofday(struct timeval *, struct timezone *); +int sys_settimeofday(const struct timeval *, const struct timezone *); +int sys_getgroups(__ssize_t, __gid_t *); +int sys_setgroups(__ssize_t, const __gid_t *); +int old_select(unsigned long int *); +int sys_symlink(const char *, const char *); +int sys_lstat(const char *, struct old_stat *); +int sys_readlink(const char *, char *, __size_t); +int sys_reboot(int, int, int); +int old_mmap(struct mmap *); +int sys_munmap(unsigned int, __size_t); +int sys_truncate(const char *, __off_t); +int sys_ftruncate(int, __off_t); +int sys_fchmod(int, __mode_t); +int sys_fchown(int, __uid_t, __gid_t); +int sys_statfs(const char *, struct statfs *); +int sys_fstatfs(unsigned int, struct statfs *); +int sys_ioperm(unsigned long int, unsigned long int, int); +int sys_socketcall(int, unsigned long int *); +int sys_setitimer(int, const struct itimerval *, struct itimerval *); +int sys_getitimer(int, struct itimerval *); +int sys_newstat(const char *, struct new_stat *); +int sys_newlstat(const char *, struct new_stat *); +int sys_newfstat(unsigned int, struct new_stat *); +int sys_uname(struct old_utsname *); +int sys_iopl(int, int, int, int, int, struct sigcontext *); +int sys_wait4(__pid_t, int *, int, struct rusage *); +int sys_sysinfo(struct sysinfo *); +int sys_fsync(int); +int sys_sigreturn(unsigned int, int, int, int, int, struct sigcontext *); +int sys_setdomainname(const char *, int); +int sys_newuname(struct new_utsname *); +int sys_mprotect(unsigned int, __size_t, int); +int sys_sigprocmask(int, const __sigset_t *, __sigset_t *); +int sys_getpgid(__pid_t); +int sys_fchdir(unsigned int); +int sys_personality(unsigned long int); +int sys_setfsuid(__uid_t); +int sys_setfsgid(__gid_t); +int sys_llseek(unsigned int, unsigned long int, unsigned long int, __loff_t *, unsigned int); +int sys_getdents(unsigned int, struct dirent *, unsigned int); +int sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *); +int sys_flock(int, int); +int sys_getsid(__pid_t); +int sys_fdatasync(int); +int sys_nanosleep(const struct timespec *, struct timespec *); +int sys_getcwd(char *, __size_t); + +#endif /* _FIWIX_SYSCALLS_H */ diff --git a/include/fiwix/system.h b/include/fiwix/system.h new file mode 100644 index 00000000..6abd7de5 --- /dev/null +++ b/include/fiwix/system.h @@ -0,0 +1,29 @@ +/* + * fiwix/include/fiwix/system.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_SYSTEM_H +#define _FIWIX_SYSTEM_H + +#define UTS_SYSNAME "Fiwix" +#define UTS_NODENAME "(none)" +#define UTS_RELEASE "1.0.0" +#define UTS_DOMAINNAME "(none)" + +struct sysinfo { + long int uptime; /* seconds since boot */ + unsigned long int loads[3]; /* load average (1, 5 and 15 minutes) */ + unsigned long int totalram; /* total usable main memory size */ + unsigned long int freeram; /* available memory size */ + unsigned long int sharedram; /* amount of shared memory */ + unsigned long int bufferram; /* amount of memory used by buffers */ + unsigned long int totalswap; /* total swap space size */ + unsigned long int freeswap; /* available swap space */ + unsigned short int procs; /* number of current processes */ + char _f[22]; /* pads structure to 64 bytes */ +}; + +#endif /* _FIWIX_SYSTEM_H */ diff --git a/include/fiwix/termbits.h b/include/fiwix/termbits.h new file mode 100644 index 00000000..08f592f4 --- /dev/null +++ b/include/fiwix/termbits.h @@ -0,0 +1,188 @@ +/* + * fiwix/include/fiwix/termbits.h + * + */ + +#ifndef _FIWIX_TERMBITS_H +#define _FIWIX_TERMBITS_H + +/* These definitions match those used by the 4.4 BSD kernel. + If the operating system has termios system calls or ioctls that + correctly implement the POSIX.1 behavior, there should be a + system-dependent version of this file that defines `struct termios', + `tcflag_t', `cc_t', `speed_t' and the `TC*' constants appropriately. */ + +/* Type of terminal control flag masks. */ +typedef unsigned long int tcflag_t; + +/* Type of control characters. */ +typedef unsigned char cc_t; + +/* Type of baud rate specifiers. */ +typedef long int speed_t; + +#define NCCS 19 + +/* Terminal control structure. */ +struct termios { + tcflag_t c_iflag; /* Input mode flags */ + tcflag_t c_oflag; /* Output mode flags */ + tcflag_t c_cflag; /* Control mode flags */ + tcflag_t c_lflag; /* Local mode flags */ + cc_t c_line; /* Line discipline */ + cc_t c_cc[NCCS]; /* Control characters */ +}; + +/* c_iflag bits */ +#define IGNBRK 0000001 /* Ignore break condition */ +#define BRKINT 0000002 /* Signal interrupt on break */ +#define IGNPAR 0000004 /* Ignore characters with parity errors */ +#define PARMRK 0000010 /* Mark parity and framing errors */ +#define INPCK 0000020 /* Enable input parity check */ +#define ISTRIP 0000040 /* Strip 8th bit off characters */ +#define INLCR 0000100 /* Map NL to CR on input */ +#define IGNCR 0000200 /* Ignore CR */ +#define ICRNL 0000400 /* Map CR to NL on input */ +#define IUCLC 0001000 /* Convert to lowercase */ +#define IXON 0002000 /* Enable start/stop output control */ +#define IXANY 0004000 /* Any character will restart after stop */ +#define IXOFF 0010000 /* Enable start/stop input control */ +#define IMAXBEL 0020000 /* Ring bell when input queue is full */ + +/* c_oflag bits */ +#define OPOST 0000001 /* Perform output processing */ +#define OLCUC 0000002 +#define ONLCR 0000004 /* Map NL to CR-NL on output */ +#define OCRNL 0000010 +#define ONOCR 0000020 +#define ONLRET 0000040 +#define OFILL 0000100 +#define OFDEL 0000200 +#define NLDLY 0000400 +#define NL0 0000000 +#define NL1 0000400 +#define CRDLY 0003000 +#define CR0 0000000 +#define CR1 0001000 +#define CR2 0002000 +#define CR3 0003000 +#define TABDLY 0014000 +#define TAB0 0000000 +#define TAB1 0004000 +#define TAB2 0010000 +#define TAB3 0014000 +#define XTABS 0014000 +#define BSDLY 0020000 +#define BS0 0000000 +#define BS1 0020000 +#define VTDLY 0040000 +#define VT0 0000000 +#define VT1 0040000 +#define FFDLY 0100000 +#define FF0 0000000 +#define FF1 0100000 + +/* c_cflag bit meaning */ +#define CBAUD 0010017 +#define B0 0000000 /* hang up */ +#define B50 0000001 /* 50 baud */ +#define B75 0000002 /* 75 baud */ +#define B110 0000003 /* 110 baud */ +#define B134 0000004 /* 134 baud */ +#define B150 0000005 /* 150 baud */ +#define B200 0000006 /* 200 baud */ +#define B300 0000007 /* 300 baud */ +#define B600 0000010 /* 600 baud */ +#define B1200 0000011 /* 1200 baud */ +#define B1800 0000012 /* 1800 baud */ +#define B2400 0000013 /* 2400 baud */ +#define B4800 0000014 /* 4800 baud */ +#define B9600 0000015 /* 9600 baud */ +#define B19200 0000016 /* 19200 baud */ +#define B38400 0000017 /* 38400 baud */ +#define EXTA B19200 +#define EXTB B38400 +#define CSIZE 0000060 /* Number of bits per byte (mask) */ +#define CS5 0000000 /* 5 bits per byte */ +#define CS6 0000020 /* 6 bits per byte */ +#define CS7 0000040 /* 7 bits per byte */ +#define CS8 0000060 /* 8 bits per byte */ +#define CSTOPB 0000100 /* Two stop bits instead of one */ +#define CREAD 0000200 /* Enable receiver */ +#define PARENB 0000400 /* Parity enable */ +#define PARODD 0001000 /* Odd parity instead of even */ +#define HUPCL 0002000 /* Hang up on last close */ +#define CLOCAL 0004000 /* Ignore modem status lines */ +#define CBAUDEX 0010000 +#define B57600 0010001 +#define B115200 0010002 +#define B230400 0010003 +#define B460800 0010004 +#define B500000 0010005 +#define B576000 0010006 +#define B921600 0010007 +#define B1000000 0010010 +#define B1152000 0010011 +#define B1500000 0010012 +#define B2000000 0010013 +#define B2500000 0010014 +#define B3000000 0010015 +#define B3500000 0010016 +#define B4000000 0010017 +#define CIBAUD 002003600000 /* input baud rate (not used) */ +#define CMSPAR 010000000000 /* mark or space (stick) parity */ +#define CRTSCTS 020000000000 /* flow control */ + +/* c_lflag bits */ +#define ISIG 0000001 /* Enable signals */ +#define ICANON 0000002 /* Do erase and kill processing */ +#define XCASE 0000004 +#define ECHO 0000010 /* Enable echo */ +#define ECHOE 0000020 /* Visual erase for ERASE */ +#define ECHOK 0000040 /* Echo NL after KILL */ +#define ECHONL 0000100 /* Echo NL even if echo is OFF */ +#define NOFLSH 0000200 /* Disable flush after interrupt */ +#define TOSTOP 0000400 /* Send SIGTTOU for background output */ +#define ECHOCTL 0001000 /* Echo control characters as ^X */ +#define ECHOPRT 0002000 /* Hardcopy visual erase */ +#define ECHOKE 0004000 /* Visual erase for KILL */ +#define FLUSHO 0010000 /* Output being flushed (state) */ +#define PENDIN 0040000 /* Retype pending input (state) */ +#define IEXTEN 0100000 /* Enable DISCARD and LNEXT */ + +/* c_cc characters */ +#define VINTR 0 /* Interrupt character [ISIG] */ +#define VQUIT 1 /* Quit character [ISIG] */ +#define VERASE 2 /* Erase character [ICANON] */ +#define VKILL 3 /* Kill-line character [ICANON] */ +#define VEOF 4 /* End-of-file character [ICANON] */ +#define VTIME 5 /* Time-out value (1/10 secs) [!ICANON] */ +#define VMIN 6 /* Minimum # of bytes read at once [!ICANON] */ +#define VSWTC 7 +#define VSTART 8 /* Start (X-ON) character [IXON, IXOFF] */ +#define VSTOP 9 /* Stop (X-OFF) character [IXON, IXOFF] */ +#define VSUSP 10 /* Suspend character [ISIG] */ +#define VEOL 11 /* End-of-line character [ICANON] */ +#define VREPRINT 12 /* Reprint-line character [ICANON] */ +#define VDISCARD 13 /* Discard character [IEXTEN] */ +#define VWERASE 14 /* Word-erase character [ICANON] */ +#define VLNEXT 15 /* Literal-next character [IEXTEN] */ +#define VEOL2 16 /* Second EOL character [ICANON] */ + +/* Values for the ACTION argument to `tcflow'. */ +#define TCOOFF 0 /* Suspend output */ +#define TCOON 1 /* Restart suspended output */ +#define TCIOFF 2 /* Send a STOP character */ +#define TCION 3 /* Send a START character */ + +/* Values for the QUEUE_SELECTOR argument to `tcflush'. */ +#define TCIFLUSH 0 /* Discard data received but not yet read */ +#define TCOFLUSH 1 /* Discard data written but not yet sent */ +#define TCIOFLUSH 2 /* Discard all pending data */ + +/* Values for the OPTIONAL_ACTIONS argument to `tcsetattr'. */ +#define TCSANOW 0 /* Change immediately */ +#define TCSADRAIN 1 /* Change when pending output is written */ +#define TCSAFLUSH 2 /* Flush pending input before changing */ + +#endif /* _FIWIX_TERMBITS_H */ diff --git a/include/fiwix/termios.h b/include/fiwix/termios.h new file mode 100644 index 00000000..83b4a9fe --- /dev/null +++ b/include/fiwix/termios.h @@ -0,0 +1,31 @@ +/* + * fiwix/include/fiwix/termios.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_TERMIOS_H +#define _FIWIX_TERMIOS_H + +#include + +struct winsize { + unsigned short int ws_row; + unsigned short int ws_col; + unsigned short int ws_xpixel; + unsigned short int ws_ypixel; +}; + +#define NCC 8 + +struct termio { + unsigned short int c_iflag; /* input mode flags */ + unsigned short int c_oflag; /* output mode flags */ + unsigned short int c_cflag; /* control mode flags */ + unsigned short int c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[NCC]; /* control characters */ +}; + +#endif /* _FIWIX_TERMIOS_H */ diff --git a/include/fiwix/time.h b/include/fiwix/time.h new file mode 100644 index 00000000..a9de1f42 --- /dev/null +++ b/include/fiwix/time.h @@ -0,0 +1,49 @@ +/* + * fiwix/include/fiwix/time.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_TIME_H +#define _FIWIX_TIME_H + +#define ITIMER_REAL 0 +#define ITIMER_VIRTUAL 1 +#define ITIMER_PROF 2 + +struct timespec { + long int tv_sec; /* seconds since 00:00:00, 1 Jan 1970 UTC */ + long int tv_nsec; /* nanoseconds (1000000000ns = 1sec) */ +}; + +struct timeval { + long int tv_sec; /* seconds since 00:00:00, 1 Jan 1970 UTC */ + long int tv_usec; /* microseconds (1000000us = 1sec) */ +}; + +struct timezone { + int tz_minuteswest; /* minutes west of GMT */ + int tz_dsttime; /* type of DST correction */ +}; + +struct itimerval { + struct timeval it_interval; + struct timeval it_value; +}; + +struct mt { + int mt_sec; + int mt_min; + int mt_hour; + int mt_day; + int mt_month; + int mt_year; +}; + +unsigned long int tv2ticks(const struct timeval *); +void ticks2tv(long int, struct timeval *); +int setitimer(int, const struct itimerval *, struct itimerval *); +unsigned long int mktime(struct mt *); + +#endif /* _FIWIX_TIME_H */ diff --git a/include/fiwix/timeb.h b/include/fiwix/timeb.h new file mode 100644 index 00000000..c63b9201 --- /dev/null +++ b/include/fiwix/timeb.h @@ -0,0 +1,18 @@ +/* + * fiwix/include/fiwix/timeb.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_TIMEB_H +#define _FIWIX_TIMEB_H + +struct timeb { + unsigned int time; /* in seconds since Epoch */ + unsigned short int millitm; /* additional milliseconds */ + short int timezone; /* minutes west of GMT */ + short int dstflag; /* nonzero if DST is used */ +}; + +#endif /* _FIWIX_TIMEB_H */ diff --git a/include/fiwix/timer.h b/include/fiwix/timer.h new file mode 100644 index 00000000..4964667b --- /dev/null +++ b/include/fiwix/timer.h @@ -0,0 +1,50 @@ +/* + * fiwix/include/fiwix/timer.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_TIMER_H +#define _FIWIX_TIMER_H + +#include +#include + +#define TIMER_IRQ 0 +#define HZ 100 /* kernel's Hertz rate (100 = 10ms) */ +#define TICK (1000000 / HZ) + +#define UNIX_EPOCH 1970 + +#define LEAP_YEAR(y) ((y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0)) +#define DAYS_PER_YEAR(y) ((LEAP_YEAR(y)) ? 366 : 365) + +#define SECS_PER_MIN 60 +#define SECS_PER_HOUR (SECS_PER_MIN * 60) +#define SECS_PER_DAY (SECS_PER_HOUR * 24) + +#define INFINITE_WAIT 0xFFFFFFFF + +struct callout { + int expires; + void (*fn)(unsigned int); + unsigned int arg; + struct callout *next; +}; + +struct callout_req { + void (*fn)(unsigned int); + unsigned int arg; +}; + +void add_callout(struct callout_req *, unsigned int); +void del_callout(struct callout_req *); +void irq_timer(struct sigcontext *); +void timer_bh(void); +void callouts_bh(void); +void get_system_time(void); +void set_system_time(__time_t); +void timer_init(void); + +#endif /* _FIWIX_TIMER_H */ diff --git a/include/fiwix/times.h b/include/fiwix/times.h new file mode 100644 index 00000000..8b00e7b0 --- /dev/null +++ b/include/fiwix/times.h @@ -0,0 +1,18 @@ +/* + * fiwix/include/fiwix/times.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_TIMES_H +#define _FIWIX_TIMES_H + +struct tms { + __clock_t tms_utime; /* CPU time spent in user-mode */ + __clock_t tms_stime; /* CPU time spent in kernel-mode */ + __clock_t tms_cutime; /* (tms_utime + tms_cutime) of children */ + __clock_t tms_cstime; /* (tms_stime + tms_cstime) of children */ +}; + +#endif /* _FIWIX_TIMES_H */ diff --git a/include/fiwix/traps.h b/include/fiwix/traps.h new file mode 100644 index 00000000..4be6eeaa --- /dev/null +++ b/include/fiwix/traps.h @@ -0,0 +1,46 @@ +/* + * fiwix/include/fiwix/traps.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_TRAPS_H +#define _FIWIX_TRAPS_H + +#include + +#define NR_EXCEPTIONS 32 + +struct traps { + char *name; + void (*handler)(unsigned int, struct sigcontext *); + char errcode; +}; + +void do_divide_error(unsigned int, struct sigcontext *); +void do_debug(unsigned int, struct sigcontext *); +void do_nmi_interrupt(unsigned int, struct sigcontext *); +void do_breakpoint(unsigned int, struct sigcontext *); +void do_overflow(unsigned int, struct sigcontext *); +void do_bound(unsigned int, struct sigcontext *); +void do_invalid_opcode(unsigned int, struct sigcontext *); +void do_no_math_coprocessor(unsigned int, struct sigcontext *); +void do_double_fault(unsigned int, struct sigcontext *); +void do_coprocessor_segment_overrun(unsigned int, struct sigcontext *); +void do_invalid_tss(unsigned int, struct sigcontext *); +void do_segment_not_present(unsigned int, struct sigcontext *); +void do_stack_segment_fault(unsigned int, struct sigcontext *); +void do_general_protection(unsigned int, struct sigcontext *); +void do_page_fault(unsigned int, struct sigcontext *); +void do_reserved(unsigned int, struct sigcontext *); +void do_floating_point_error(unsigned int, struct sigcontext *); +void do_alignment_check(unsigned int, struct sigcontext *); +void do_machine_check(unsigned int, struct sigcontext *); +void do_simd_fault(unsigned int, struct sigcontext *); + +void trap_handler(unsigned int, struct sigcontext); + +int dump_registers(unsigned int, struct sigcontext *); + +#endif /* _FIWIX_TRAPS_H */ diff --git a/include/fiwix/tty.h b/include/fiwix/tty.h new file mode 100644 index 00000000..5722bc3a --- /dev/null +++ b/include/fiwix/tty.h @@ -0,0 +1,83 @@ +/* + * fiwix/include/fiwix/tty.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_TTY_H +#define _FIWIX_TTY_H + +#include +#include + +#define NR_TTYS 15 /* maximum number of tty devices */ + +#define CBSIZE 32 /* number of characters in cblock */ +#define NR_CB_QUEUE 10 /* number of cblocks per queue */ +#define CB_POOL_SIZE 128 /* number of cblocks in the central pool */ + +#define LAST_CHAR(q) ((q)->tail ? (q)->tail->data[(q)->tail->end_off - 1] : NULL) + +struct clist { + unsigned short int count; + unsigned short int cb_num; + struct cblock *head; + struct cblock *tail; +}; + +struct cblock { + unsigned short int start_off; + unsigned short int end_off; + unsigned char data[CBSIZE]; + struct cblock *prev; + struct cblock *next; +}; + +struct tty { + __dev_t dev; + struct clist read_q; + struct clist cooked_q; + struct clist write_q; + unsigned short int count; + struct termios termios; + struct winsize winsize; + __pid_t pid, pgid, sid; + unsigned char lnext; + void *driver_data; + int canon_data; + + /* formerly tty_operations */ + void (*stop)(struct tty *); + void (*start)(struct tty *); + void (*deltab)(struct tty *); + void (*reset)(struct tty *); + void (*input)(struct tty *); + void (*output)(struct tty *); +}; +extern struct tty tty_table[]; + +int register_tty(__dev_t); +struct tty * get_tty(__dev_t); +void disassociate_ctty(struct tty *); +void termios_reset(struct tty *); +void do_cook(struct tty *); +int tty_putchar(struct tty *, unsigned char); +int tty_open(struct inode *, struct fd *); +int tty_close(struct inode *, struct fd *); +int tty_read(struct inode *, struct fd *, char *, __size_t); +int tty_write(struct inode *, struct fd *, const char *, __size_t); +int tty_ioctl(struct inode *, int cmd, unsigned long int); +int tty_lseek(struct inode *, __off_t); +int tty_select(struct inode *, int); +void tty_init(void); + +int tty_queue_putchar(struct tty *, struct clist *, unsigned char); +int tty_queue_unputchar(struct clist *); +unsigned char tty_queue_getchar(struct clist *); +void tty_queue_flush(struct clist *); +void tty_queue_init(struct tty *); + +int vt_ioctl(struct tty *, int, unsigned long int); + +#endif /* _FIWIX_TTY_H */ diff --git a/include/fiwix/types.h b/include/fiwix/types.h new file mode 100644 index 00000000..b85b2cf9 --- /dev/null +++ b/include/fiwix/types.h @@ -0,0 +1,53 @@ +/* + * fiwix/include/fiwix/types.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_TYPES_H +#define _FIWIX_TYPES_H + +typedef __signed__ char __s8; +typedef unsigned char __u8; +typedef __signed__ short int __s16; +typedef unsigned short int __u16; +typedef __signed__ int __s32; +typedef unsigned int __u32; + +typedef __u16 __uid_t; +typedef __u16 __gid_t; +typedef __u32 __ino_t; +typedef __u16 __mode_t; +typedef __u16 __nlink_t; +typedef __u32 __off_t; +typedef __s32 __pid_t; +typedef __s32 __ssize_t; +typedef __u32 __size_t; +typedef unsigned long int __clock_t; +typedef __u32 __time_t; +typedef __u16 __dev_t; +typedef __u16 __key_t; +typedef __s32 __blk_t; /* must be signed in order to return -EIO */ +typedef __s32 __daddr_t; +typedef unsigned long long int __loff_t; + +/* number of descriptors that can fit in an 'fd_set' */ +/* WARNING: this value must be the same as in the C Library */ +#define __FD_SETSIZE 64 + +#define __NFDBITS (sizeof(unsigned long int) * 8) +#define __FDELT(d) ((d) / __NFDBITS) +#define __FDMASK(d) (1 << ((d) % __NFDBITS)) + +/* define the fd_set structure for select() */ +typedef struct { + unsigned long int fds_bits[__FD_SETSIZE / __NFDBITS]; +} fd_set; + +#define __FD_ZERO(set) (memset_b((void *) (set), 0, sizeof(fd_set))) +#define __FD_SET(d, set) ((set)->fds_bits[__FDELT(d)] |= __FDMASK(d)) +#define __FD_CLR(d, set) ((set)->fds_bits[__FDELT(d)] &= ~__FDMASK(d)) +#define __FD_ISSET(d, set) ((set)->fds_bits[__FDELT(d)] & __FDMASK(d)) + +#endif /* _FIWIX_TYPES_H */ diff --git a/include/fiwix/unistd.h b/include/fiwix/unistd.h new file mode 100644 index 00000000..ac664674 --- /dev/null +++ b/include/fiwix/unistd.h @@ -0,0 +1,207 @@ +/* + * fiwix/include/fiwix/unistd.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_UNISTD_H +#define _FIWIX_UNISTD_H + +/* Linux 2.0.40 ABI (one syscall from Linux 2.2.26) */ +/* #define SYS_setup */ +#define SYS_exit 1 +#define SYS_fork 2 +#define SYS_read 3 +#define SYS_write 4 +#define SYS_open 5 +#define SYS_close 6 +#define SYS_waitpid 7 +#define SYS_creat 8 +#define SYS_link 9 +#define SYS_unlink 10 +#define SYS_execve 11 +#define SYS_chdir 12 +#define SYS_time 13 +#define SYS_mknod 14 +#define SYS_chmod 15 +#define SYS_chown 16 +#define SYS_break 17 /* -ENOSYS */ +#define SYS_oldstat 18 +#define SYS_lseek 19 +#define SYS_getpid 20 +#define SYS_mount 21 +#define SYS_umount 22 +#define SYS_setuid 23 +#define SYS_getuid 24 +#define SYS_stime 25 +/* #define SYS_ptrace */ +#define SYS_alarm 27 +#define SYS_oldfstat 28 +#define SYS_pause 29 +#define SYS_utime 30 +#define SYS_stty 31 /* -ENOSYS */ +#define SYS_gtty 32 /* -ENOSYS */ +#define SYS_access 33 +/* #define SYS_nice */ +#define SYS_ftime 35 +#define SYS_sync 36 +#define SYS_kill 37 +#define SYS_rename 38 +#define SYS_mkdir 39 +#define SYS_rmdir 40 +#define SYS_dup 41 +#define SYS_pipe 42 +#define SYS_times 43 +#define SYS_prof 44 /* -ENOSYS */ +#define SYS_brk 45 +#define SYS_setgid 46 +#define SYS_getgid 47 +#define SYS_signal 48 +#define SYS_geteuid 49 +#define SYS_getegid 50 +/* #define SYS_acct */ +#define SYS_umount2 52 /* from 2.2.26, it was sys_phys() */ +#define SYS_lock 53 /* -ENOSYS */ +#define SYS_ioctl 54 +#define SYS_fcntl 55 +#define SYS_mpx 56 /* -ENOSYS */ +#define SYS_setpgid 57 +#define SYS_ulimit 58 /* -ENOSYS */ +#define SYS_olduname 59 +#define SYS_umask 60 +#define SYS_chroot 61 +#define SYS_ustat 62 +#define SYS_dup2 63 +#define SYS_getppid 64 +#define SYS_getpgrp 65 +#define SYS_setsid 66 +#define SYS_sigaction 67 +#define SYS_sgetmask 68 +#define SYS_ssetmask 69 +#define SYS_setreuid 70 +#define SYS_setregid 71 +#define SYS_sigsuspend 72 +#define SYS_sigpending 73 +#define SYS_sethostname 74 +#define SYS_setrlimit 75 +#define SYS_getrlimit 76 +#define SYS_getrusage 77 +#define SYS_gettimeofday 78 +#define SYS_settimeofday 79 +#define SYS_getgroups 80 +#define SYS_setgroups 81 +#define SYS_oldselect 82 +#define SYS_symlink 83 +#define SYS_oldlstat 84 +#define SYS_readlink 85 +/* #define SYS_uselib */ +/* #define SYS_swapon */ +#define SYS_reboot 88 +/* #define SYS_oldreaddir */ +#define SYS_old_mmap 90 +#define SYS_munmap 91 +#define SYS_truncate 92 +#define SYS_ftruncate 93 +#define SYS_fchmod 94 +#define SYS_fchown 95 +/* #define SYS_getpriority */ +/* #define SYS_setpriority */ +/* #define SYS_profil */ +#define SYS_statfs 99 +#define SYS_fstatfs 100 +#define SYS_ioperm 101 +#define SYS_socketcall 102 +/* #define SYS_syslog */ +#define SYS_setitimer 104 +#define SYS_getitimer 105 +#define SYS_newstat 106 +#define SYS_newlstat 107 +#define SYS_newfstat 108 +#define SYS_uname 109 +#define SYS_iopl 110 +/* #define SYS_vhangup */ +/* #define SYS_idle 112 -ENOSYS */ +/* #define SYS_vm86old */ +#define SYS_wait4 114 +/* #define SYS_swapoff */ +#define SYS_sysinfo 116 +/* #define SYS_ipc */ +#define SYS_fsync 118 +#define SYS_sigreturn 119 +/* #define SYS_clone */ +#define SYS_setdomainname 121 +#define SYS_newuname 122 +/* #define SYS_modify_ldt */ +/* #define SYS_adjtimex */ +#define SYS_mprotect 125 +#define SYS_sigprocmask 126 +/* #define SYS_create_module */ +/* #define SYS_init_module */ +/* #define SYS_delete_module */ +/* #define SYS_get_kernel_syms */ +/* #define SYS_quotactl */ +#define SYS_getpgid 132 +#define SYS_fchdir 133 +/* #define SYS_bdflush */ +/* #define SYS_sysfs */ +#define SYS_personality 136 +/* #define afs_syscall */ +#define SYS_setfsuid 138 +#define SYS_setfsgid 139 +#define SYS_llseek 140 +#define SYS_getdents 141 +#define SYS_select 142 +#define SYS_flock 143 +/* #define SYS_msync */ +/* #define SYS_readv */ +/* #define SYS_writev */ +#define SYS_getsid 147 +#define SYS_fdatasync 148 +/* #define SYS_sysctl */ +/* #define SYS_mlock */ +/* #define SYS_munlock */ +/* #define SYS_mlockall */ +/* #define SYS_munlockall */ +/* #define SYS_sched_setparam */ +/* #define SYS_sched_getparam */ +/* #define SYS_sched_setscheduler */ +/* #define SYS_sched_getscheduler */ +/* #define SYS_sched_yield */ +/* #define SYS_sched_get_priority_max */ +/* #define SYS_sched_get_priority_min */ +/* #define SYS_sched_rr_get_interval */ +#define SYS_nanosleep 162 +/* #define SYS_mremap */ + +/* extra system calls from Linux 2.2.26 */ +/* #define SYS_mremap */ +/* #define SYS_setresuid */ +/* #define SYS_getresuid */ +/* #define SYS_ni_syscall */ +/* #define SYS_query_module */ +/* #define SYS_poll */ +/* #define SYS_nfsservctl */ +/* #define SYS_setresgid */ +/* #define SYS_getresgid */ +/* #define SYS_prctl */ +/* #define SYS_rt_sigreturn_wrapper */ +/* #define SYS_rt_sigaction */ +/* #define SYS_rt_sigprocmask */ +/* #define SYS_rt_sigpending */ +/* #define SYS_rt_sigtimedwait */ +/* #define SYS_rt_sigqueueinfo */ +/* #define SYS_rt_sigsuspend_wrapper */ +/* #define SYS_pread */ +/* #define SYS_pwrite */ +/* #define SYS_chown */ +#define SYS_getcwd 183 +/* #define SYS_capget */ +/* #define SYS_capset */ +/* #define SYS_sigaltstack_wrapper */ +/* #define SYS_sendfile */ +/* #define SYS_ni_syscall */ +/* #define SYS_ni_syscall */ +#define SYS_vfork 190 + +#endif /* _FIWIX_UNISTD_H */ diff --git a/include/fiwix/ustat.h b/include/fiwix/ustat.h new file mode 100644 index 00000000..2b73ea45 --- /dev/null +++ b/include/fiwix/ustat.h @@ -0,0 +1,20 @@ +/* + * fiwix/include/fiwix/ustat.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_USTAT_H +#define _FIWIX_USTAT_H + +#include + +struct ustat { + __daddr_t f_tfree; /* total free blocks */ + __ino_t f_tinode; /* number of free inodes */ + char f_fname; /* filesystem name */ + char f_fpack; /* filesystem pack name */ +}; + +#endif /* _FIWIX_USTAT_H */ diff --git a/include/fiwix/utime.h b/include/fiwix/utime.h new file mode 100644 index 00000000..7a18db28 --- /dev/null +++ b/include/fiwix/utime.h @@ -0,0 +1,18 @@ +/* + * fiwix/include/fiwix/utime.h + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifndef _FIWIX_UTIME_H +#define _FIWIX_UTIME_H + +#include + +struct utimbuf { + __time_t actime; /* access time */ + __time_t modtime; /* modification time */ +}; + +#endif /* _FIWIX_UTIME_H */ diff --git a/include/fiwix/utsname.h b/include/fiwix/utsname.h new file mode 100644 index 00000000..14d4d31c --- /dev/null +++ b/include/fiwix/utsname.h @@ -0,0 +1,75 @@ +/* Copyright (C) 1991, 1992, 1994, 1996 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* + * POSIX Standard: 4.4 System Identification + */ + +#ifndef _FIWIX_UTSNAME_H +#define _FIWIX_UTSNAME_H + +#define _OLD_UTSNAME_LENGTH 9 +#define _UTSNAME_LENGTH 65 + +#ifndef _UTSNAME_NODENAME_LENGTH +#define _UTSNAME_NODENAME_LENGTH _UTSNAME_LENGTH +#endif + +/* Very OLD structure describing the system and machine. */ +struct oldold_utsname +{ + char sysname[_OLD_UTSNAME_LENGTH]; + char nodename[_OLD_UTSNAME_LENGTH]; + char release[_OLD_UTSNAME_LENGTH]; + char version[_OLD_UTSNAME_LENGTH]; + char machine[_OLD_UTSNAME_LENGTH]; +}; + +/* OLD structure describing the system and machine. */ +struct old_utsname +{ + char sysname[_UTSNAME_LENGTH]; + char nodename[_UTSNAME_NODENAME_LENGTH]; + char release[_UTSNAME_LENGTH]; + char version[_UTSNAME_LENGTH]; + char machine[_UTSNAME_LENGTH]; +}; + +/* NEW structure describing the system and machine. */ +struct new_utsname +{ + /* Name of the implementation of the operating system. */ + char sysname[_UTSNAME_LENGTH]; + + /* Name of this node on the network. */ + char nodename[_UTSNAME_NODENAME_LENGTH]; + + /* Current release level of this implementation. */ + char release[_UTSNAME_LENGTH]; + /* Current version level of this release. */ + char version[_UTSNAME_LENGTH]; + + /* Name of the hardware type the system is running on. */ + char machine[_UTSNAME_LENGTH]; + char domainname[_UTSNAME_LENGTH]; +}; + +extern struct new_utsname sys_utsname; +extern char UTS_MACHINE[_UTSNAME_LENGTH]; + +#endif /* _FIWIX_UTSNAME_H */ diff --git a/include/fiwix/version.h b/include/fiwix/version.h new file mode 100644 index 00000000..edc95f3a --- /dev/null +++ b/include/fiwix/version.h @@ -0,0 +1 @@ +#define UTS_VERSION "Sat Apr 21 18:53:46 CEST 2018" diff --git a/include/fiwix/vt.h b/include/fiwix/vt.h new file mode 100644 index 00000000..405d3046 --- /dev/null +++ b/include/fiwix/vt.h @@ -0,0 +1,54 @@ +#ifndef VT_H +#define VT_H + +/* prefix 0x56 is 'V', to avoid collision with termios and kd */ + +#define VT_OPENQRY 0x5600 /* find available vt */ + +struct vt_mode { + char mode; /* vt mode */ + char waitv; /* if set, hang on writes if not active */ + short int relsig; /* signal to raise on release req */ + short int acqsig; /* signal to raise on acquisition */ + short int frsig; /* unused (set to 0) */ +}; +#define VT_GETMODE 0x5601 /* get mode of active vt */ +#define VT_SETMODE 0x5602 /* set mode of active vt */ +#define VT_AUTO 0x00 /* auto vt switching */ +#define VT_PROCESS 0x01 /* process controls switching */ +#define VT_ACKACQ 0x02 /* acknowledge switch */ + +struct vt_stat { + unsigned short int v_active; /* active vt */ + unsigned short int v_signal; /* signal to send */ + unsigned short int v_state; /* vt bitmask */ +}; +#define VT_GETSTATE 0x5603 /* get global vt state info */ +#define VT_SENDSIG 0x5604 /* signal to send to bitmask of vts */ + +#define VT_RELDISP 0x5605 /* release display */ + +#define VT_ACTIVATE 0x5606 /* make vt active */ +#define VT_WAITACTIVE 0x5607 /* wait for vt active */ +#define VT_DISALLOCATE 0x5608 /* free memory associated to vt */ + +struct vt_sizes { + unsigned short int v_rows; /* number of rows */ + unsigned short int v_cols; /* number of columns */ + unsigned short int v_scrollsize;/* number of lines of scrollback */ +}; +#define VT_RESIZE 0x5609 /* set kernel's idea of screensize */ + +struct vt_consize { + unsigned short int v_rows; /* number of rows */ + unsigned short int v_cols; /* number of columns */ + unsigned short int v_vlin; /* number of pixel rows on screen */ + unsigned short int v_clin; /* number of pixel rows per character */ + unsigned short int v_vcol; /* number of pixel columns on screen */ + unsigned short int v_ccol; /* number of pixel columns per character */ +}; +#define VT_RESIZEX 0x560A /* set kernel's idea of screensize + more */ +#define VT_LOCKSWITCH 0x560B /* disallow vt switching */ +#define VT_UNLOCKSWITCH 0x560C /* allow vt switching */ + +#endif /* VT_H */ diff --git a/kernel/Makefile b/kernel/Makefile new file mode 100644 index 00000000..6e117a22 --- /dev/null +++ b/kernel/Makefile @@ -0,0 +1,20 @@ +# fiwix/kernel/Makefile +# +# Copyright 2018, Jordi Sanfeliu. All rights reserved. +# Distributed under the terms of the Fiwix License. +# + +.S.o: + $(CC) -traditional -I$(INCLUDE) -c -o $@ $< +.c.o: + $(CC) $(CFLAGS) -c -o $@ $< + +OBJS = boot.o core386.o main.o init.o gdt.o idt.o syscalls.o pic.o pit.o \ + traps.o cpu.o cmos.o timer.o sched.o sleep.o signal.o process.o + +kernel: $(OBJS) + $(LD) $(LDFLAGS) -r $(OBJS) -o kernel.o + +clean: + rm -f *.o + diff --git a/kernel/boot.S b/kernel/boot.S new file mode 100644 index 00000000..91d8fdee --- /dev/null +++ b/kernel/boot.S @@ -0,0 +1,151 @@ +/* + * fiwix/kernel/boot.S + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include + +#define ASM 1 /* GRUB stuff */ +#include + +#define CR0_MP 0x00000002 /* CR0 bit-01 MP (Monitor Coprocessor) */ +#define CR0_NE 0x00000020 /* CR0 bit-05 NE (Numeric Error) */ +#define CR0_WP 0x00010000 /* CR0 bit-16 WP (Write Protect) */ +#define CR0_AM 0x00040000 /* CR0 bit-18 AM (Alignment Mask) */ +#define CR0_PG 0x80000000 /* CR0 bit-31 PG (Paging) */ + +.section .setup, "a" /* "a" attribute means Allocatable section */ + +.align 4 +tmp_gdtr: + .word ((3 * 8) - 1) + .long tmp_gdt + +.align 4 +tmp_gdt: + /* NULL DESCRIPTOR */ + .word 0x0000 + .word 0x0000 + .word 0x0000 + .word 0x0000 + + /* KERNEL CODE */ + .word 0xFFFF /* segment limit 15-00 */ + .word 0x0000 /* base address 15-00 */ + .byte 0x00 /* base address 23-16 */ + .byte 0x9A /* P=1 DPL=00 S=1 TYPE=1010 (exec/read) */ + .byte 0xCF /* G=1 DB=1 0=0 AVL=0 SEGLIM=1111 */ + .byte 0x40 /* base address 31-24 */ + + /* KERNEL DATA */ + .word 0xFFFF /* segment limit 15-00 */ + .word 0x0000 /* base address 15-00 */ + .byte 0x00 /* base address 23-16 */ + .byte 0x92 /* P=1 DPL=00 S=1 TYPE=0010 (read/write) */ + .byte 0xCF /* G=1 DB=1 0=0 AVL=0 SEGLIM=1111 */ + .byte 0x40 /* base address 31-24 */ + + +.text + +.globl start; start: + cli + jmp multiboot_entry + +.align 4 +multiboot_header: /* multiboot header */ + .long MULTIBOOT_HEADER_MAGIC /* magic */ + .long MULTIBOOT_HEADER_FLAGS /* flags */ + /* checksum */ + .long -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) + +#ifndef __ELF__ + .long multiboot_header /* header_addr */ + .long _start /* load_addr */ + .long _edata /* load_end_addr */ + .long _end /* bss_end_addr */ + .long multiboot_entry /* entry_addr */ +#endif /* ! __ELF__ */ + +/* + * We use the CX register in order to keep intact the values in AX and BX + * registers, since they are holding the Multiboot values 'magic' and 'info' + * respectively. + */ +multiboot_entry: + lgdt tmp_gdtr /* load GDTR with the temporary GDT */ + movw $KERNEL_DS, %cx + movw %cx, %ds + movw %cx, %es + movw %cx, %fs + movw %cx, %gs + movw %cx, %ss + ljmp $KERNEL_CS, $1f +1: + + +/* + * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING !!! + * --------------------------------------------------------------------------- + * The minimal page directory of 4MB only works if the in-memory size of the + * kernel is lesser than 3MB. If you need more space go to the setup_minmem() + * function and set the 'mb4' variable accordingly. + * + * In order to know the current size of the Fiwix kernel, just follow this: + * + * # readelf -l fiwix + * Elf file type is EXEC (Executable file) + * Entry point 0xc0100020 + * There are 2 program headers, starting at offset 52 + * + * Program Headers: + * Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align + * LOAD 0x000074 0x00100000 0x00100000 0x00020 0x00020 R 0x4 + * LOAD 0x0000a0 0xc0100020 0x00100020 0x33f8c 0x859a0 RWE 0x20 + * check this value --> ^^^^^^^ + */ + movl $0xC0010000, %esp /* default stack address */ + pushl $0 /* reset EFLAGS */ + popf + + pushl %eax /* save Multiboot magic value */ + call setup_minmem /* setup a minimal page directory */ + movl %eax, %cr3 + + movl %cr0, %eax + andl $0x00000011, %eax /* disable all, preserve ET & PE (GRUB) */ + orl $CR0_PG, %eax /* enable PG (Paging) */ + orl $CR0_AM, %eax /* enable AM (Alignment Mask) */ + orl $CR0_WP, %eax /* enable WP (Write Protect) */ + orl $CR0_NE, %eax /* enable NE (Numeric Error) */ + orl $CR0_MP, %eax /* enable MP (Monitor Coprocessor) */ + movl %eax, %cr0 + + call bss_init /* initialize BSS segment */ + call gdt_init /* setup and load the definitive GDT */ + + pushl %ebx /* save Multiboot info structure */ + call get_last_elf_addr + add $4, %esp + popl %ecx /* restore Multiboot magic value */ + andl $0xFFFFF000, %eax /* page aligned */ + addl $0x3000, %eax /* 2 whole pages for kernel stack */ + subl $4, %eax + movl %eax, %esp /* set kernel stack */ + + pushl %esp /* save kernel stack address */ + pushl %ebx /* save Multiboot info structure */ + pushl %ecx /* save Multiboot magic value */ + call start_kernel + +.align 4 +.globl cpu_idle; cpu_idle: + hlt + jmp cpu_idle + +.align 4 +.org 0x1000 +.globl _fdc_transfer_area +_fdc_transfer_area: .fill 512*2*18,1,0 diff --git a/kernel/cmos.c b/kernel/cmos.c new file mode 100644 index 00000000..ce2ab25d --- /dev/null +++ b/kernel/cmos.c @@ -0,0 +1,57 @@ +/* + * fiwix/kernel/cmos.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include + +int cmos_update_in_progress(void) +{ + return(cmos_read(CMOS_STATA) & CMOS_STATA_UIP); +} + +unsigned char cmos_read_date(unsigned char addr) +{ + /* make sure an update isn't in progress */ + while(cmos_update_in_progress()); + + if(!(cmos_read(CMOS_STATB) & CMOS_STATB_DM)) { + return BCD2BIN(cmos_read(addr)); + } + return cmos_read(addr); +} + +void cmos_write_date(unsigned char addr, unsigned char value) +{ + /* make sure an update isn't in progress */ + while(cmos_update_in_progress()); + + if(!(cmos_read(CMOS_STATB) & CMOS_STATB_DM)) { + cmos_write(addr, BIN2BCD(value)); + } + cmos_write(addr, value); +} + +unsigned char cmos_read(unsigned char addr) +{ + unsigned long int flags; + + SAVE_FLAGS(flags); CLI(); + outport_b(CMOS_INDEX, addr); + RESTORE_FLAGS(flags); + + return inport_b(CMOS_DATA); +} + +void cmos_write(unsigned char addr, unsigned char value) +{ + unsigned long int flags; + + SAVE_FLAGS(flags); CLI(); + outport_b(CMOS_INDEX, addr); + outport_b(CMOS_DATA, value); + RESTORE_FLAGS(flags); +} diff --git a/kernel/core386.S b/kernel/core386.S new file mode 100644 index 00000000..36650c61 --- /dev/null +++ b/kernel/core386.S @@ -0,0 +1,943 @@ +/* + * fiwix/kernel/core386.S + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include + +#define CR0_MP ~(0x00000002) /* CR0 bit-01 MP (Monitor Coprocessor) */ +#define CR0_EM 0x00000004 /* CR0 bit-02 EM (Emulation) */ + +#define SS_RPL3 0x03 /* Request Privilege Level 3 */ + +#define GS 0x00 +#define FS 0x04 +#define ES 0x08 +#define DS 0x0C +#define EDI 0x10 /* \ */ +#define ESI 0x14 /* | */ +#define EBP 0x18 /* | */ +#define ESP 0x1C /* | saved by */ +#define EBX 0x20 /* | 'pusha' */ +#define EDX 0x24 /* | */ +#define ECX 0x28 /* | */ +#define EAX 0x2C /* / */ +#define ERR 0x30 /* error code or padding */ +#define EIP 0x34 /* \ */ +#define CS 0x38 /* | saved by processor */ +#define FLAGS 0x3C /* / */ +#define OLDESP 0x40 /* \ saved by processor on */ +#define OLDSS 0x44 /* / privilege level change */ + +#define SAVE_ALL \ + pushal ;\ + pushl %ds ;\ + pushl %es ;\ + pushl %fs ;\ + pushl %gs + +#define EXCEPTION(exception) \ + pushl $exception ;\ + call trap_handler ;\ + addl $4, %esp + +#define IRQ(irq) \ + pushl $irq ;\ + call irq_handler ;\ + addl $4, %esp + +/* + * Check only for signals if we are returning from user-mode. issig() function + * returns 1 if there are signals or 0 otherwise. If there are signals psig() + * function is called with the stack as the first argument. + */ +#define CHECK_SIGNALS \ + cmpw $KERNEL_CS, CS(%esp) ;\ + je 1f ;\ + call issig ;\ + cmpl $0, %eax ;\ + je 1f ;\ + movl %esp, %eax ;\ + pushl %eax ;\ + call psig ;\ + addl $4, %esp ;\ +1: + +#define SCHEDULE \ + cmpl $0, need_resched ;\ + je 1f ;\ + call do_sched ;\ +1: + +#define BOTTOM_HALVES \ +/* sti */ ;\ + call do_bh + +#define RESTORE_ALL \ + popl %gs ;\ + popl %fs ;\ + popl %es ;\ + popl %ds ;\ + popal ;\ + addl $4, %esp # suppress error code (or padding) from stack + + +.text + +.align 4 +.globl except0; except0: # DIVIDE ERROR + pushl $0 # save simulated error code to stack + SAVE_ALL + EXCEPTION(0x0) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl except1; except1: # DEBUG + pushl $0 # save simulated error code to stack + SAVE_ALL + EXCEPTION(0x1) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl except2; except2: # NMI INTERRUPT + pushl $0 # save simulated error code to stack + SAVE_ALL + EXCEPTION(0x2) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl except3; except3: # BREAKPOINT INT3 + pushl $0 # save simulated error code to stack + SAVE_ALL + EXCEPTION(0x3) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl except4; except4: # OVERFLOW + pushl $0 # save simulated error code to stack + SAVE_ALL + EXCEPTION(0x4) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl except5; except5: # BOUND + pushl $0 # save simulated error code to stack + SAVE_ALL + EXCEPTION(0x5) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl except6; except6: # INVALID OPCODE + pushl $0 # save simulated error code to stack + SAVE_ALL + EXCEPTION(0x6) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl except7; except7: # NO MATH COPROCESSOR + pushl $0 # save simulated error code to stack + SAVE_ALL + EXCEPTION(0x7) + clts # floating-opcode cached! + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl except8; except8: # DOUBLE FAULT + SAVE_ALL + EXCEPTION(0x8) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl except9; except9: # COPROCESSOR SEGMENT OVERRUN + pushl $0 # save simulated error code to stack + SAVE_ALL + EXCEPTION(0x9) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl exceptA; exceptA: # INVALID TSS + SAVE_ALL + EXCEPTION(0xA) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl exceptB; exceptB: # SEGMENT NOT PRESENT + SAVE_ALL + EXCEPTION(0xB) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl exceptC; exceptC: # STACK SEGMENT FAULT + SAVE_ALL + EXCEPTION(0xC) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl exceptD; exceptD: # GENERAL PROTECTION FAULT + SAVE_ALL + EXCEPTION(0xD) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl exceptE; exceptE: # PAGE FAULT + SAVE_ALL + EXCEPTION(0xE) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl exceptF; exceptF: # INTEL RESERVED + pushl $0 # save simulated error code to stack + SAVE_ALL + EXCEPTION(0xF) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.globl except10; except10: # FLOATING POINT ERROR + pushl $0 # save simulated error code to stack + SAVE_ALL + EXCEPTION(0x10) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.globl except11; except11: # ALIGNMENT CHECK + EXCEPTION(0x11) + SAVE_ALL + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.globl except12; except12: # MACHINE CHECK + pushl $0 # save simulated error code to stack + SAVE_ALL + EXCEPTION(0x12) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.globl except13; except13: # SIMD FLOATING POINT + pushl $0 # save simulated error code to stack + SAVE_ALL + EXCEPTION(0x13) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.globl except14; except14: # INTEL RESERVED +.globl except15; except15: # INTEL RESERVED +.globl except16; except16: # INTEL RESERVED +.globl except17; except17: # INTEL RESERVED +.globl except18; except18: # INTEL RESERVED +.globl except19; except19: # INTEL RESERVED +.globl except1A; except1A: # INTEL RESERVED +.globl except1B; except1B: # INTEL RESERVED +.globl except1C; except1C: # INTEL RESERVED +.globl except1D; except1D: # INTEL RESERVED +.globl except1E; except1E: # INTEL RESERVED +.globl except1F; except1F: # INTEL RESERVED + pushl $0 # save simulated error code to stack + SAVE_ALL + EXCEPTION(0x14) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl irq0; irq0: # TIMER + pushl $0 # save simulated error code to stack + SAVE_ALL + IRQ(0) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl irq1; irq1: # KEYBOARD + pushl $0 # save simulated error code to stack + SAVE_ALL + IRQ(1) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl irq2; irq2: # CASCADE + pushl $0 # save simulated error code to stack + SAVE_ALL + IRQ(2) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl irq3; irq3: # SERIAL + pushl $0 # save simulated error code to stack + SAVE_ALL + IRQ(3) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl irq4; irq4: # SERIAL + pushl $0 # save simulated error code to stack + SAVE_ALL + IRQ(4) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl irq5; irq5: + pushl $0 # save simulated error code to stack + SAVE_ALL + IRQ(5) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl irq6; irq6: # FLOPPY + pushl $0 # save simulated error code to stack + SAVE_ALL + IRQ(6) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl irq7; irq7: + pushl $0 # save simulated error code to stack + SAVE_ALL + IRQ(7) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl irq8; irq8: + pushl $0 # save simulated error code to stack + SAVE_ALL + IRQ(8) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl irq9; irq9: + pushl $0 # save simulated error code to stack + SAVE_ALL + IRQ(9) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl irq10; irq10: + pushl $0 # save simulated error code to stack + SAVE_ALL + IRQ(10) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl irq11; irq11: + pushl $0 # save simulated error code to stack + SAVE_ALL + IRQ(11) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl irq12; irq12: + pushl $0 # save simulated error code to stack + SAVE_ALL + IRQ(12) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl irq13; irq13: + pushl $0 # save simulated error code to stack + SAVE_ALL + IRQ(13) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl irq14; irq14: # IDE Primary + pushl $0 # save simulated error code to stack + SAVE_ALL + IRQ(14) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl irq15; irq15: # IDE Secondary + pushl $0 # save simulated error code to stack + SAVE_ALL + IRQ(15) + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES + RESTORE_ALL + iret + +.align 4 +.globl unknown_irq; unknown_irq: + pushl $0 # save simulated error code to stack + SAVE_ALL + IRQ(-1) + RESTORE_ALL + iret + +.align 4 +.globl switch_to_user_mode; switch_to_user_mode: + cli + xorl %eax, %eax # initialize %eax + movl %eax, %ebx # initialize %ebx + movl %eax, %ecx # initialize %ecx + movl %eax, %edx # initialize %edx + movl %eax, %esi # initialize %esi + movl %eax, %edi # initialize %edi + movl %eax, %ebp # initialize %ebp + movl $(USER_DS | SS_RPL3), %eax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + pushl %eax + pushl $KERNEL_BASE_ADDR - 4 # user stack address + pushl $0x202 # initialize eflags (Linux 2.2 = 0x292) + popfl + pushfl + movl $(USER_CS | SS_RPL3), %eax + pushl %eax + pushl $KERNEL_BASE_ADDR - 0x1000 # go to init_trampoline() in user mode + iret + +.align 4 +.globl sighandler_trampoline; sighandler_trampoline: + pushl %eax + call *%ecx + popl %ebx + + movl $SYS_sigreturn, %eax + int $0x80 + + # never reached, otherwise call sys_exit() + movl $SYS_exit, %eax + int $0x80 + ret +.align 4 +.globl end_sighandler_trampoline; end_sighandler_trampoline: + nop + +.align 4 +.globl syscall; syscall: # SYSTEM CALL ENTRY + pushl %eax # save the system call number + SAVE_ALL + + pushl %edi # \ 5th parameter + pushl %esi # | 4th parameter + pushl %edx # | 3rd parameter + pushl %ecx # | 2nd parameter + pushl %ebx # / 1st parameter + pushl %eax # system call number + call do_syscall + addl $24, %esp # suppress all 6 pushl from the stack + movl %eax, EAX(%esp) # save the return value + + SCHEDULE + CHECK_SIGNALS + BOTTOM_HALVES +.align 4 +.globl return_from_syscall; return_from_syscall: + RESTORE_ALL + iret + +.align 4 +.globl do_switch; do_switch: + movl %esp, %ebx + pushal + pushfl + movl 0x4(%ebx), %eax # save ESP to 'prev->tss.esp' + movl %esp, (%eax) + movl 0x8(%ebx), %eax # save EIP to 'prev->tss.eip' + movl $1f, (%eax) + movl 0xC(%ebx), %esp # load 'next->tss.esp' into ESP + pushl 0x10(%ebx) # push 'next->tss.eip' into ESP + movl 0x14(%ebx), %eax # load 'next->tss.cr3' into CR3 + ltr 0x18(%ebx) # load TSS + movl %eax, %cr3 + ret +1: + popfl + popal + +.align 4 +.globl cpuid; cpuid: + pushl %ebp + movl %esp, %ebp + pushl %edi + pushl %esi + pushl %ebx + + pushf + pop %eax # put original EFLAGS in EAX + mov %eax, %ecx # save original EFLAGS in ECX + xor $0x200000, %eax # change bit 21 (ID) in EFLAGS + push %eax # save new EFLAGS on stack + popf # replace current EFLAGS + pushf + pop %eax # put EFLAGS in EAX + cmp %ecx, %eax # compare if both EFLAGS are equal + + je test386 # can't toggle ID bit, no CPUID + xor %ebx, %ebx # CPUID available, will return 0 + jmp end_cpuid + +test386: + mov %ecx, %eax # get original EFLAGS + xor $0x40000, %eax # change bit 18 (AC) in EFLAGS + push %eax # save new EFLAGS on stack + popf # replace current EFLAGS + pushf + pop %eax + cmp %ecx, %eax # compare if both EFLAGS are equal + movb $3, %bl # looks like an i386, return 3 + je end_cpuid + movb $4, %bl # otherwise is an old i486, return 4 + +end_cpuid: + push %ecx # push original EFLAGS + popf # restore original EFLAGS + xor %eax, %eax + movb %bl, %al # put return value to AL + + popl %ebx + popl %esi + popl %edi + popl %ebp + ret + +.align 4 +.globl getfpu; getfpu: + pushl %ebp + movl %esp, %ebp + pushl %edi + pushl %esi + pushl %ebx + + fninit + movl $0x5a5a, _fpstatus + fnstsw _fpstatus + movl _fpstatus, %eax + cmp $0, %al + movl $0, _fpstatus + jne end_getfpu + +check_control_word: + fnstcw _fpstatus + movl _fpstatus, %eax + andl $0x103f, %eax + cmp $0x3f, %ax + movl $0, _fpstatus + jne end_getfpu + movl $1, _fpstatus + +end_getfpu: + movl _fpstatus, %eax + cmp $0, %al + jne 1f # return if there is a coprocessor + movl %cr0, %eax # otherwise (no math processor): + orl $CR0_EM, %eax # - set EM (Emulation) + andl $CR0_MP, %eax # - clear MP (Monitor Coprocessor) + movl %eax, %cr0 + movl $0, %eax +1: + popl %ebx + popl %esi + popl %edi + popl %ebp + ret + +.align 4 +.globl vendor_id; vendor_id: + pushl %ebp + movl %esp, %ebp + pushl %ebx + pushl %edx + pushl %ecx + + mov $0, %eax + cpuid + movl %ebx, _vendorid # save the 12 bytes of vendor ID string + movl %edx, _vendorid+4 + movl %ecx, _vendorid+8 + + popl %ecx + popl %edx + popl %ebx + popl %ebp + ret # EAX returns the highest CPUID value + +.align 4 +.globl signature_flags; signature_flags: + pushl %ebp + movl %esp, %ebp + pushl %edi + pushl %esi + pushl %ebx + pushl %edx + + mov $1, %eax + cpuid + movl %eax, _cpusignature # signature (model and stepping) + movl %ebx, _brandid # misc. information + movl %edx, _cpuflags # feature flags + shrl $8, %eax + andl $0xF, %eax + movl %eax, _cputype # family + + popl %edx + popl %ebx + popl %esi + popl %edi + popl %ebp + ret + +.align 4 +.globl brand_str; brand_str: + pushl %ebp + movl %esp, %ebp + pushl %edi + pushl %esi + pushl %ebx + + movl $0x80000000, %eax + cpuid + cmp $0x80000000, %eax # check if brand string is supported + jbe no_brand_str + movl $0x80000002, %eax # get first 16 bytes of brand string + cpuid + movl %eax, _brandstr + movl %ebx, _brandstr+4 + movl %ecx, _brandstr+8 + movl %edx, _brandstr+12 + movl $0x80000003, %eax # get more 16 bytes of brand string + cpuid + movl %eax, _brandstr+16 + movl %ebx, _brandstr+20 + movl %ecx, _brandstr+24 + movl %edx, _brandstr+28 + movl $0x80000004, %eax # get last 16 bytes of brand string + cpuid + movl %eax, _brandstr+32 + movl %ebx, _brandstr+36 + movl %ecx, _brandstr+40 + movl %edx, _brandstr+44 + jmp end_brand_str + +no_brand_str: + movl $1, %eax + +end_brand_str: + movl $0, %eax + popl %ebx + popl %esi + popl %edi + popl %ebp + ret + +.align 4 +.globl tlbinfo; tlbinfo: + pushl %edx + pushl %ecx + mov $2, %eax + cpuid + movl %eax, _tlbinfo_eax # store cache information + movl %ebx, _tlbinfo_ebx + movl %edx, _tlbinfo_ecx + movl %ecx, _tlbinfo_edx + popl %ecx + popl %edx + ret + +.align 4 +.globl inport_b; inport_b: + pushl %ebp + movl %esp, %ebp + + movw 0x08(%ebp), %dx # port addr + inb %dx, %al + + jmp 1f # recovery time +1: jmp 1f # recovery time +1: popl %ebp + ret + +.align 4 +.globl inport_w; inport_w: + pushl %ebp + movl %esp, %ebp + + movw 0x08(%ebp), %dx # port addr + inw %dx, %ax + + jmp 1f # recovery time +1: jmp 1f # recovery time +1: popl %ebp + ret + +.align 4 +.globl inport_sw; inport_sw: + pushl %ebp + movl %esp, %ebp + pushl %edx + pushl %edi + pushl %ecx + + cld + mov 0x8(%ebp), %edx # port addr + mov 0xC(%ebp), %edi # dest + mov 0x10(%ebp), %ecx # count + rep + insw + + popl %ecx + popl %edi + popl %edx + popl %ebp + ret + +.align 4 +.globl outport_b; outport_b: + pushl %ebp + movl %esp, %ebp + + movw 0x8(%ebp), %dx # port addr + movb 0xC(%ebp), %al # data + outb %al, %dx + + jmp 1f # recovery time +1: jmp 1f # recovery time +1: popl %ebp + ret + +.align 4 +.globl outport_w; outport_w: + pushl %ebp + movl %esp, %ebp + + movw 0x8(%ebp), %dx # port addr + movw 0xC(%ebp), %ax # data + outw %ax, %dx + + jmp 1f # recovery time +1: jmp 1f # recovery time +1: popl %ebp + ret + +.align 4 +.globl outport_sw; outport_sw: + pushl %ebp + movl %esp, %ebp + pushl %edx + pushl %esi + pushl %ecx + + cld + mov 0x8(%ebp), %edx # port addr + mov 0xC(%ebp), %esi # src + mov 0x10(%ebp), %ecx # count + rep + outsw + + popl %ecx + popl %esi + popl %edx + popl %ebp + ret + +.align 4 +.globl load_gdt; load_gdt: + movl 0x4(%esp), %eax + lgdt (%eax) + movw $KERNEL_DS, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + ljmp $KERNEL_CS, $1f +1: + ret + +.align 4 +.globl load_idt; load_idt: + movl 0x4(%esp), %eax + lidt (%eax) + ret + +.align 4 +.globl activate_kpage_dir; activate_kpage_dir: + movl kpage_dir, %eax + movl %eax, %cr3 + ret + +.align 4 +.globl load_tr; load_tr: + mov 0x4(%esp), %ax + ltr %ax + ret + +.align 4 +.globl get_rdtsc; get_rdtsc: + cpuid + rdtsc + ret + +.align 4 +.globl invalidate_tlb; invalidate_tlb: + movl %cr3, %eax + movl %eax, %cr3 + ret + + +.data + +.globl _cputype +.globl _cpusignature +.globl _cpuflags +.globl _fpstatus +.globl _brandid +.globl _vendorid +.globl _brandstr +.globl _tlbinfo_eax +.globl _tlbinfo_ebx +.globl _tlbinfo_ecx +.globl _tlbinfo_edx + +_cputype: .int 0 +_cpusignature: .int 0 +_cpuflags: .int 0 +_fpstatus: .int 0 +_brandid: .int 0 +_vendorid: .fill 13,1,0 +_brandstr: .fill 49,1,0 +_tlbinfo_eax: .int 0 +_tlbinfo_ebx: .int 0 +_tlbinfo_ecx: .int 0 +_tlbinfo_edx: .int 0 diff --git a/kernel/cpu.c b/kernel/cpu.c new file mode 100644 index 00000000..b3085402 --- /dev/null +++ b/kernel/cpu.c @@ -0,0 +1,263 @@ +/* + * fiwix/kernel/cpu.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char UTS_MACHINE[_UTSNAME_LENGTH]; + +static struct cpu_type intel[] = { + { 4, + { "i486 DX", "i486 DX", "i486 SX", "i486 DX/2", + "i486 SL", "i486 SX/2", NULL, "i486 DX/2 WBE", + "i486 DX/4", NULL, NULL, NULL, NULL, NULL, NULL, NULL } + }, + { 5, + { NULL, "Pentium 60/66", "Pentium 75-200", "Pentium ODfor486", + "PentiumMMX", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL } + }, + { 6, + { NULL, "Pentium Pro", NULL, "Pentium II", NULL, "Pentium II", + "Intel Celeron", "Pentium III", "Pentium III", NULL, + "Pentium III Xeon", "Pentium III", NULL, NULL, NULL, NULL } + } +}; + +static const char *cpu_flags[] = { + "FPU", "VME", "DE", "PSE", "TSC", "MSR", "PAE", "MCE", "CX8", "APIC", + "10", "SEP", "MTRR", "PGE", "MCA", "CMOV", "PAT", "PSE-36", "PSN", + "CLFSH", "20", "DS", "ACPI", "MMX", "FXSR", "SSE", "SSE2", "SS", + "HTT", "TM", "30", "PBE" +}; + +static unsigned long int detect_cpuspeed(void) +{ + unsigned long long int tsc1, tsc2; + + outport_b(MODEREG, SEL_CHAN2 | LSB_MSB | TERM_COUNT | BINARY_CTR); + outport_b(CHANNEL2, (OSCIL / HZ) & 0xFF); + outport_b(CHANNEL2, (OSCIL / HZ) >> 8); + outport_b(PS2_SYSCTRL_B, inport_b(PS2_SYSCTRL_B) | ENABLE_SDATA | ENABLE_TMR2G); + + tsc1 = 0; + tsc1 = get_rdtsc(); + + while(!(inport_b(PS2_SYSCTRL_B) & 0x20)); + + tsc2 = 0; + tsc2 = get_rdtsc(); + + outport_b(PS2_SYSCTRL_B, inport_b(PS2_SYSCTRL_B) & ~(ENABLE_SDATA | ENABLE_TMR2G)); + + return (tsc2 - tsc1) * HZ; +} + +/* + * These are the 2nd and 3rd level cache values according to Intel Processor + * Identification and the CPUID Instruction. + * Application Note 485. Document Number: 241618-031. September 2006. + */ +static void show_cache(int value) +{ + switch(value) { + /* 2nd level cache */ + case 0x39: + case 0x3B: + case 0x41: + case 0x79: + cpu_table.cache = "128KB L2"; + break; + case 0x3A: + cpu_table.cache = "192KB L2"; + break; + case 0x3C: + case 0x42: + case 0x7A: + case 0x82: + cpu_table.cache = "256KB L2"; + break; + case 0x3D: + cpu_table.cache = "384KB L2"; + break; + case 0x3E: + case 0x43: + case 0x7B: + case 0x7F: + case 0x83: + case 0x86: + cpu_table.cache = "512KB L2"; + break; + case 0x44: + case 0x78: + case 0x7C: + case 0x84: + case 0x87: + cpu_table.cache = "1MB L2"; + break; + case 0x45: + case 0x7D: + case 0x85: + cpu_table.cache = "2MB L2"; + break; + + /* 3rd level cache */ + case 0x22: + cpu_table.cache = "512KB L3"; + break; + case 0x23: + cpu_table.cache = "1MB L3"; + break; + case 0x25: + cpu_table.cache = "2MB L3"; + break; + case 0x29: + case 0x46: + cpu_table.cache = "4MB L3"; + break; + case 0x49: + cpu_table.cache = "4MB L3 & L2"; + break; + case 0x4A: + cpu_table.cache = "6MB L3"; + break; + case 0x47: + case 0x4B: + cpu_table.cache = "8MB L3"; + break; + case 0x4C: + cpu_table.cache = "12MB L3"; + break; + case 0x4D: + cpu_table.cache = "16MB L3"; + break; + default: + break; + } +} + +static void check_cache(int maxcpuid) +{ + int n, maxcpuids; + + maxcpuids = 1; + if(maxcpuid >= 2) { + for(n = 0; n < maxcpuids; n++) { + tlbinfo(); + maxcpuids = _tlbinfo_eax & 0xFF; + show_cache((_tlbinfo_eax >> 8) & 0xFF); + show_cache((_tlbinfo_eax >> 16) & 0xFF); + show_cache((_tlbinfo_eax >> 24) & 0xFF); + if(!(_tlbinfo_ebx & RESERVED_DESC)) { + show_cache(_tlbinfo_ebx & 0xFF); + show_cache((_tlbinfo_ebx >> 8) & 0xFF); + show_cache((_tlbinfo_ebx >> 16) & 0xFF); + show_cache((_tlbinfo_ebx >> 24) & 0xFF); + } + if(!(_tlbinfo_ecx & RESERVED_DESC)) { + show_cache(_tlbinfo_ecx & 0xFF); + show_cache((_tlbinfo_ecx >> 8) & 0xFF); + show_cache((_tlbinfo_ecx >> 16) & 0xFF); + show_cache((_tlbinfo_ecx >> 24) & 0xFF); + } + if(!(_tlbinfo_edx & RESERVED_DESC)) { + show_cache(_tlbinfo_edx & 0xFF); + show_cache((_tlbinfo_edx >> 8) & 0xFF); + show_cache((_tlbinfo_edx >> 16) & 0xFF); + show_cache((_tlbinfo_edx >> 24) & 0xFF); + } + } + } +} + +int get_cpu_flags(char *buffer, int offset) +{ + int n, size; + unsigned int mask; + + size = sprintk(buffer + offset, "flags :"); + for(n = 0, mask = 1; n < 32; n++, mask <<= 1) { + if(_cpuflags & mask) { + size += sprintk(buffer + offset + size, " %s", cpu_flags[n]); + } + } + size += sprintk(buffer + offset + size, "\n"); + return size; +} + +void cpu_init(void) +{ + unsigned int n; + int maxcpuid; + + memset_b(&cpu_table, NULL, sizeof(cpu_table)); + cpu_table.model = -1; + cpu_table.stepping = -1; + + printk("cpu - -\t"); + cpu_table.family = cpuid(); + if(!cpu_table.family) { + cpu_table.has_cpuid = 1; + maxcpuid = vendor_id(); + cpu_table.vendor_id = _vendorid; + if(maxcpuid >= 1) { + signature_flags(); + cpu_table.family = _cputype; + cpu_table.flags = _cpuflags; + sprintk(UTS_MACHINE, "i%c86", _cputype <= 6 ? ('0' + _cputype) : '6'); + strncpy(sys_utsname.machine, UTS_MACHINE, _UTSNAME_LENGTH); + if(!strcmp((char *)_vendorid, "GenuineIntel")) { + printk("Intel "); + for(n = 0; n < sizeof(intel) / sizeof(struct cpu_type); n++) { + if(intel[n].cpu == _cputype) { + cpu_table.model_name = !intel[n].name[(((int)_cpusignature >> 4) & 0xF)] ? NULL : intel[n].name[(((int)_cpusignature >> 4) & 0xF)]; + break; + } + } + if(cpu_table.model_name) { + printk("%s", cpu_table.model_name); + } else { + printk("processor"); + } + } else if(!strcmp((char *)_vendorid, "AuthenticAMD")) { + printk("AMD processor"); + } else { + printk("x86"); + } + if(_cpuflags & CPU_TSC) { + cpu_table.hz = detect_cpuspeed(); + printk(" at %d.%d Mhz", (cpu_table.hz / 1000000), ((cpu_table.hz % 1000000) / 100000)); + check_cache(maxcpuid); + if(cpu_table.cache) { + printk(" (%s)", cpu_table.cache); + } + } + printk("\n"); + printk("\t\t\t\tvendorid=%s ", _vendorid); + cpu_table.model = (_cpusignature >> 4) & 0xF; + cpu_table.stepping = _cpusignature & 0xF; + printk("model=%d stepping=%d\n", cpu_table.model, cpu_table.stepping); + } + if(!brand_str()) { + cpu_table.model_name = _brandstr; + if(cpu_table.model_name[0]) { + printk("\t\t\t\t%s\n", cpu_table.model_name); + } + } + } else { + printk("80%d86\n", cpu_table.family); + cpu_table.has_cpuid = 0; + } + cpu_table.has_fpu = getfpu(); +} diff --git a/kernel/gdt.c b/kernel/gdt.c new file mode 100644 index 00000000..db5bd9b7 --- /dev/null +++ b/kernel/gdt.c @@ -0,0 +1,54 @@ +/* + * fiwix/kernel/gdt.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct seg_desc gdt[NR_GDT_ENTRIES]; + +struct desc_r gdtr = { + sizeof(gdt) - 1, + (unsigned int)&gdt +}; + +static void gdt_set_entry(int num, unsigned int base_addr, unsigned int limit, char loflags, char hiflags) +{ + num /= sizeof(struct seg_desc); + gdt[num].sd_lolimit = limit & 0xFFFF; + gdt[num].sd_lobase = base_addr & 0xFFFFFF; + gdt[num].sd_loflags = loflags; + gdt[num].sd_hilimit = (limit >> 16) & 0x0F; + gdt[num].sd_hiflags = hiflags; + gdt[num].sd_hibase = (base_addr >> 24) & 0xFF; +} + +void gdt_init(void) +{ + unsigned char loflags; + + gdt_set_entry(0, 0, 0, 0, 0); /* null descriptor */ + + loflags = SD_CODE | SD_CD | SD_DPL0 | SD_PRESENT; + gdt_set_entry(KERNEL_CS, 0, 0xFFFFFFFF, loflags, SD_OPSIZE32 | SD_PAGE4KB); + loflags = SD_DATA | SD_CD | SD_DPL0 | SD_PRESENT; + gdt_set_entry(KERNEL_DS, 0, 0xFFFFFFFF, loflags, SD_OPSIZE32 | SD_PAGE4KB); + + loflags = SD_CODE | SD_CD | SD_DPL3 | SD_PRESENT; + gdt_set_entry(USER_CS, 0, 0xFFFFFFFF, loflags, SD_OPSIZE32 | SD_PAGE4KB); + loflags = SD_DATA | SD_CD | SD_DPL3 | SD_PRESENT; + gdt_set_entry(USER_DS, 0, 0xFFFFFFFF, loflags, SD_OPSIZE32 | SD_PAGE4KB); + + loflags = SD_TSSPRESENT; + gdt_set_entry(TSS, 0, sizeof(struct proc) - 1, loflags, SD_OPSIZE32); + + load_gdt((unsigned int)&gdtr); +} diff --git a/kernel/idt.c b/kernel/idt.c new file mode 100644 index 00000000..6327201d --- /dev/null +++ b/kernel/idt.c @@ -0,0 +1,93 @@ +/* + * fiwix/kernel/idt.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +struct gate_desc idt[NR_IDT_ENTRIES]; + +struct desc_r idtr = { + sizeof(idt) - 1, + (unsigned int)idt +}; + +static void set_idt_entry(int num, __off_t handler, unsigned int flags) +{ + idt[num].gd_looffset = handler & 0x0000FFFF; + idt[num].gd_selector = KERNEL_CS; + idt[num].gd_flags = flags << 8; + idt[num].gd_hioffset = handler >> 16; +} + +void idt_init(void) +{ + int n; + + memset_b(idt, NULL, sizeof(idt)); + for(n = 0; n < NR_IDT_ENTRIES; n++) { + set_idt_entry(n, (__off_t)&unknown_irq, SD_32INTRGATE | SD_PRESENT); + } + + /* FIXME: must be SD_32TRAPGATE for true multitasking */ + set_idt_entry(0x00, (__off_t)&except0, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x01, (__off_t)&except1, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x02, (__off_t)&except2, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x03, (__off_t)&except3, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x04, (__off_t)&except4, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x05, (__off_t)&except5, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x06, (__off_t)&except6, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x07, (__off_t)&except7, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x08, (__off_t)&except8, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x09, (__off_t)&except9, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x0A, (__off_t)&exceptA, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x0B, (__off_t)&exceptB, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x0C, (__off_t)&exceptC, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x0D, (__off_t)&exceptD, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x0E, (__off_t)&exceptE, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x0F, (__off_t)&exceptF, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x10, (__off_t)&except10, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x11, (__off_t)&except11, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x12, (__off_t)&except12, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x13, (__off_t)&except13, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x14, (__off_t)&except14, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x15, (__off_t)&except15, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x16, (__off_t)&except16, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x17, (__off_t)&except17, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x18, (__off_t)&except18, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x19, (__off_t)&except19, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x1A, (__off_t)&except1A, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x1B, (__off_t)&except1B, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x1C, (__off_t)&except1C, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x1D, (__off_t)&except1D, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x1E, (__off_t)&except1E, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x1F, (__off_t)&except1F, SD_32INTRGATE | SD_PRESENT); + + set_idt_entry(0x20, (__off_t)&irq0, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x21, (__off_t)&irq1, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x22, (__off_t)&irq2, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x23, (__off_t)&irq3, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x24, (__off_t)&irq4, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x25, (__off_t)&irq5, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x26, (__off_t)&irq6, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x27, (__off_t)&irq7, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x28, (__off_t)&irq8, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x29, (__off_t)&irq9, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x2A, (__off_t)&irq10, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x2B, (__off_t)&irq11, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x2C, (__off_t)&irq12, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x2D, (__off_t)&irq13, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x2E, (__off_t)&irq14, SD_32INTRGATE | SD_PRESENT); + set_idt_entry(0x2F, (__off_t)&irq15, SD_32INTRGATE | SD_PRESENT); + + /* FIXME: must be SD_32TRAPGATE for true multitasking */ + set_idt_entry(0x80, (__off_t)&syscall, SD_32INTRGATE | SD_DPL3 | SD_PRESENT); + + load_idt((unsigned int)&idtr); +} diff --git a/kernel/init.c b/kernel/init.c new file mode 100644 index 00000000..8f050f94 --- /dev/null +++ b/kernel/init.c @@ -0,0 +1,125 @@ +/* + * fiwix/kernel/init.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define INIT_TRAMPOLINE_SIZE 128 /* max. size of init_trampoline() */ + +char *init_argv[] = { "init", NULL }; +char *init_envp[] = { "HOME=/", "TERM=linux", NULL }; + +static void init_trampoline(void) +{ + USER_SYSCALL(SYS_open, "/dev/console", O_RDWR, 0); /* stdin */ + USER_SYSCALL(SYS_dup, 0, NULL, NULL); /* stdout */ + USER_SYSCALL(SYS_dup, 0, NULL, NULL); /* stderr */ + USER_SYSCALL(SYS_execve, INIT_PROGRAM, init_argv, init_envp); + + /* only reached in case of error in sys_execve() */ + USER_SYSCALL(SYS_exit, NULL, NULL, NULL); +} + +void init_init(void) +{ + int n; + unsigned int page; + struct inode *i; + unsigned int *pgdir; + struct proc *init; + + if(namei(INIT_PROGRAM, &i, NULL, FOLLOW_LINKS)) { + PANIC("can't find %s.\n", INIT_PROGRAM); + } + if(!S_ISREG(i->i_mode)) { + PANIC("%s is not a regular file.\n", INIT_PROGRAM); + } + iput(i); + + /* INIT slot was already created in main.c */ + init = &proc_table[INIT]; + + /* INIT process starts with the current (kernel) Page Directory */ + if(!(pgdir = (void *)kmalloc())) { + goto init_init__die; + } + init->rss++; + memcpy_b(pgdir, kpage_dir, PAGE_SIZE); + init->tss.cr3 = V2P((unsigned int)pgdir); + + if(!(init->vma = (void *)kmalloc())) { + goto init_init__die; + } + init->rss++; + memset_b(init->vma, NULL, PAGE_SIZE); + + init->ppid = 0; + init->pgid = 0; + init->sid = 0; + init->flags = 0; + init->children = 0; + init->priority = DEF_PRIORITY; + init->start_time = CURRENT_TIME; + init->sleep_address = NULL; + init->uid = init->gid = 0; + init->euid = init->egid = 0; + init->suid = init->sgid = 0; + memset_b(init->fd, NULL, sizeof(init->fd)); + memset_b(init->fd_flags, NULL, sizeof(init->fd_flags)); + init->root = current->root; + init->pwd = current->pwd; + strcpy(init->argv0, init_argv[0]); + sprintk(init->pidstr, "%d", init->pid); + init->sigpending = 0; + init->sigblocked = 0; + init->sigexecuting = 0; + memset_b(init->sigaction, NULL, sizeof(init->sigaction)); + memset_b(&init->usage, NULL, sizeof(struct rusage)); + memset_b(&init->cusage, NULL, sizeof(struct rusage)); + init->timeout = 0; + for(n = 0; n < RLIM_NLIMITS; n++) { + init->rlim[n].rlim_cur = init->rlim[n].rlim_max = RLIM_INFINITY; + } + init->rlim[RLIMIT_NOFILE].rlim_cur = OPEN_MAX; + init->rlim[RLIMIT_NOFILE].rlim_max = NR_OPENS; + init->rlim[RLIMIT_NPROC].rlim_cur = CHILD_MAX; + init->rlim[RLIMIT_NPROC].rlim_cur = NR_PROCS; + init->umask = 0022; + + /* setup the stack */ + if(!(init->tss.esp0 = kmalloc())) { + goto init_init__die; + } + init->tss.esp0 += PAGE_SIZE - 4; + init->rss++; + init->tss.ss0 = KERNEL_DS; + + /* setup the init_trampoline */ + page = map_page(init, KERNEL_BASE_ADDR - PAGE_SIZE, 0, PROT_READ | PROT_WRITE); + memcpy_b((void *)page, init_trampoline, INIT_TRAMPOLINE_SIZE); + + init->tss.eip = (unsigned int)switch_to_user_mode; + init->tss.esp = page + PAGE_SIZE - 4; + + init->state = PROC_RUNNING; + nr_processes++; + return; + +init_init__die: + PANIC("unable to run init process.\n"); +} diff --git a/kernel/main.c b/kernel/main.c new file mode 100644 index 00000000..69fa9665 --- /dev/null +++ b/kernel/main.c @@ -0,0 +1,337 @@ +/* + * fiwix/kernel/main.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * check if the bit BIT in Multiboot FLAGS is set + * ---------------------------------------------- + * bit 11 -> vbe_* + * bit 10 -> apm_table + * bit 9 -> boot_loader_name + * bit 8 -> config_table + * bit 7 -> drives_length and drives_addr + * bit 6 -> mmap_length and mmap_addr + * bit 5 -> ELF symbols + * bit 4 -> a.out symbols + * bit 3 -> mods_count and mods_addr + * bit 2 -> cmdline + * bit 1 -> boot_device + * bit 0 -> mem_lower and mem_upper values + */ +#define CHECK_FLAG(flags,bit) ((flags) & (1 << (bit))) + +Elf32_Shdr *symtab, *strtab; +unsigned int _last_data_addr; +int _memsize; +int _extmemsize; +int _rootdev; +int _noramdisk; +int _ramdisksize; +char _rootfstype[10]; +char _rootdevname[DEVNAME_MAX + 1]; +int _syscondev; + +char cmdline[NAME_MAX + 1]; + +struct new_utsname sys_utsname = { + UTS_SYSNAME, + UTS_NODENAME, + UTS_RELEASE, + UTS_VERSION, + "", + UTS_DOMAINNAME, +}; + +struct kernel_stat kstat; + +/* + * This function returns the last address used by kernel symbols. This is + * intended to setup the kernel stack beyond this address. + */ +unsigned int get_last_elf_addr(unsigned int info) +{ + multiboot_info_t *info_boot; + Elf32_Shdr *shdr; + elf_section_header_table_t *hdr; + unsigned short int n; + + symtab = strtab = NULL; + info_boot = (multiboot_info_t *)info; + hdr = &(info_boot->u.elf_sec); + for(n = 0; n < hdr->num; n++) { + shdr = (Elf32_Shdr *)(hdr->addr + (n * hdr->size)); + if(shdr->sh_type == SHT_SYMTAB) { + symtab = shdr; + } + if(shdr->sh_type == SHT_STRTAB) { + strtab = shdr; + } + } + return P2V((strtab->sh_addr + strtab->sh_size)); +} + +/* check the validity of a command line parameter */ +static int check_parm(struct kparms *parm, const char *value) +{ + int n; + + if(!strcmp(parm->name, "root=")) { + for(n = 0; parm->value[n]; n++) { + if(!strcmp(parm->value[n], value)) { + _rootdev = parm->sysval[n]; + strncpy(_rootdevname, value, DEVNAME_MAX); + return 0; + } + } + return 1; + } + if(!strcmp(parm->name, "noramdisk")) { + _noramdisk = 1; + return 0; + } + if(!strcmp(parm->name, "ramdisksize=")) { + int size = atoi(value); + if(!size || size > RAMDISK_MAXSIZE) { + printk("WARNING: 'ramdisksize' value is out of limits, defaulting to 4096KB.\n"); + _ramdisksize = 0; + } else { + _ramdisksize = size; + } + return 0; + } + if(!strcmp(parm->name, "rootfstype=")) { + for(n = 0; parm->value[n]; n++) { + if(!strcmp(parm->value[n], value)) { + strncpy(_rootfstype, value, sizeof(_rootfstype)); + return 0; + } + } + return 1; + } + if(!strcmp(parm->name, "console=")) { + for(n = 0; parm->value[n]; n++) { + if(!strcmp(parm->value[n], value)) { + _syscondev = parm->sysval[n]; + return 0; + } + } + return 1; + } + printk("WARNING: the parameter '%s' looks valid but it's not defined!\n", parm->name); + return 0; +} + +static void parse_arg(const char *arg) +{ + int n; + char *str; + + str = (char *)arg; + while(*(str++)) { + if(*str == ' ') { + return; + } + } + + for(n = 0; parm_table[n].name; n++) { + if(!strncmp(arg, parm_table[n].name, strlen(parm_table[n].name))) { + arg += strlen(parm_table[n].name); + if(check_parm(&parm_table[n], arg)) { + printk("WARNING: invalid value '%s' in the '%s' parameter.\n", arg, parm_table[n].name); + } + return; + } + } + printk("WARNING: invalid cmdline parameter: '%s'.\n", arg); +} + +static void parse_cmdline(const char *str) +{ + char *from, *to; + char arg[CMDL_ARG_LEN]; + char c; + + from = to = (char *)str; + for(;;) { + c = *(str++); + if(c == ' ' || !c) { + if(to - from < CMDL_ARG_LEN) { + memcpy_b(arg, from, to - from); + arg[to - from] = NULL; + if(arg[0] != NULL) { + parse_arg(arg); + } + } else { + memcpy_b(arg, from, CMDL_ARG_LEN); + arg[CMDL_ARG_LEN - 1] = NULL; + printk("WARNING: invalid length of the cmdline parameter '%s'.\n", arg); + } + from = ++to; + if(!c) { + break; + } + continue; + } + to++; + } +} + +void start_kernel(unsigned long magic, unsigned long info, unsigned int stack) +{ + struct proc *init, *p_kswapd; + multiboot_info_t mbi; + + /* default kernel values */ + strcpy(_rootfstype, "minix"); /* filesystem is minix */ + _syscondev = MKDEV(VCONSOLES_MAJOR, 0); /* console is /dev/tty0 */ + + pic_init(); + idt_init(); + dev_init(); + tty_init(); + + printk(" Welcome to %s\n", UTS_SYSNAME); + printk(" Copyright (c) 2018, Jordi Sanfeliu\n"); + printk("\n"); + printk(" kernel v%s for i386 architecture\n", UTS_RELEASE); + printk(" (GCC %s, built on %s)\n", __VERSION__, UTS_VERSION); + printk("\n"); + printk("DEVICE ADDRESS IRQ COMMENT\n"); + printk("-------------------------------------------------------------------------------\n"); + + if(magic != MULTIBOOT_BOOTLOADER_MAGIC) { + printk("WARNING: invalid multiboot-bootloader magic number: 0x%x.\n\n", (unsigned long int)magic); + memset_b(&mbi, NULL, sizeof(struct multiboot_info)); + } else { + memcpy_b(&mbi, (void *)info, sizeof(struct multiboot_info)); + } + + memset_b(&kstat, NULL, sizeof(kstat)); + + cpu_init(); + + /* check if a command line was supplied */ + if(CHECK_FLAG(mbi.flags, 2)) { + int n, len; + char c; + char *p; + + p = (char *)mbi.cmdline; + len = strlen(p); + /* suppress 'fiwix' */ + for(n = 0; n < len; n++) { + c = *(p++); + if(c == ' ') { + break; + } + } + strcpy(cmdline, p); + parse_cmdline(cmdline); + } else { + printk("WARNING: no cmdline detected!\n"); + } + + printk("kernel 0x%08X - cmdline='%s'\n", KERNEL_ENTRY_ADDR, cmdline); + + timer_init(); + vconsole_init(); + keyboard_init(); + + if(!CHECK_FLAG(mbi.flags, 0)) { + printk("WARNING: values in mem_lower and mem_upper are not valid!\n"); + } + _memsize = (unsigned int)mbi.mem_lower; + _extmemsize = (unsigned int)mbi.mem_upper; + + if(CHECK_FLAG(mbi.flags, 6)) { + bios_map_init((memory_map_t *)mbi.mmap_addr, mbi.mmap_length); + } else { + bios_map_init(NULL, 0); + } + + _last_data_addr = stack - KERNEL_BASE_ADDR; + mem_init(); + proc_init(); + + if(!(CHECK_FLAG(mbi.flags, 5))) { + printk("WARNING: ELF section header table is not valid!\n"); + } + + /* IDLE is now the current process */ + set_tss(current); + load_tr(TSS); + current->tss.cr3 = (unsigned int)kpage_dir; + current->flags |= PF_KPROC; + + /* reserve the slot 1 for the INIT process */ + init = get_proc_free(); + proc_slot_init(init); + init->pid = get_unused_pid(); + + /* create and setup kswapd process */ + p_kswapd = kernel_process(kswapd); + + /* kswapd will take over the rest of the kernel initialization */ + p_kswapd->state = PROC_RUNNING; + need_resched = 1; + + STI(); /* let's rock! */ + cpu_idle(); +} + +void stop_kernel(void) +{ + struct proc *p; + + printk("\n"); + printk("** Safe to Power Off **\n"); + printk(" -or-\n"); + printk("** Press Any Key to Reboot **\n"); + any_key_to_reboot = 1; + + /* put all processes to sleep and reset all pending signals */ + FOR_EACH_PROCESS(p) { + p->state = PROC_SLEEPING; + p->sigpending = 0; + } + + /* TODO: disable all interrupts */ + CLI(); + disable_irq(TIMER_IRQ); + + /* switch to IDLE process */ + if(current) { + do_sched(); + } + + STI(); + enable_irq(KEYBOARD_IRQ); + cpu_idle(); +} diff --git a/kernel/pic.c b/kernel/pic.c new file mode 100644 index 00000000..0fe290d0 --- /dev/null +++ b/kernel/pic.c @@ -0,0 +1,266 @@ +/* + * fiwix/kernel/pic.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* interrupt vector base addresses */ +#define IRQ0_ADDR 0x20 +#define IRQ8_ADDR 0x28 + +/* + * pic.c implements a bottom half table using a singly linked list. + * + * head tail + * +---------+ ----------+ ... ----------+ + * |data|next| |data|next| ... |data|next| + * | | --> | | --> ... | | / | + * +---------+ ----------+ ... ----------+ + * (bh) (bh) (bh) + */ + +struct bh bh_pool[NR_BH]; +struct bh *bh_pool_head; +struct bh *bh_head; +struct bh *bh_tail; + +static struct bh *get_free_bh(void) +{ + struct bh *new; + + new = NULL; + if(bh_pool_head) { + new = bh_pool_head; + bh_pool_head = bh_pool_head->next; + new->next = NULL; + } + return new; +} + +static void put_free_bh(struct bh *old) +{ + old->next = bh_pool_head; + bh_pool_head = old; +} + +/* + * This sends the command OCW3 to PIC (master or slave) to obtain the register + * values. Slave is chained and represents IRQs 8-15. Master represents IRQs + * 0-7, with IRQ 2 being the chain. + */ +static unsigned short int pic_get_irq_reg(int ocw3) +{ + outport_b(PIC_MASTER, ocw3); + outport_b(PIC_SLAVE, ocw3); + return (inport_b(PIC_SLAVE) << 8) | inport_b(PIC_MASTER); +} + +void add_bh(void (*fn)(void)) +{ + unsigned long int flags; + struct bh *b; + + SAVE_FLAGS(flags); CLI(); + + if(!(b = get_free_bh())) { + RESTORE_FLAGS(flags); + PANIC("no more bottom half slots!\n"); + } + + /* initialize bh */ + memset_b(b, NULL, sizeof(struct bh)); + b->fn = fn; + + if(!bh_tail) { + bh_head = bh_tail = b; + } else { + bh_tail->next = b; + bh_tail = b; + } + + RESTORE_FLAGS(flags); + return; +} + +void del_bh(void) +{ + unsigned long int flags; + struct bh *b; + + if(!bh_head) { + return; + } + + SAVE_FLAGS(flags); CLI(); + + b = bh_head; + if(bh_head == bh_tail) { + bh_head = bh_tail = NULL; + } else { + bh_head = bh_head->next; + } + put_free_bh(b); + + RESTORE_FLAGS(flags); + return; +} + +void enable_irq(int irq) +{ + int addr; + + addr = (irq > 7) ? PIC_SLAVE + DATA : PIC_MASTER + DATA; + irq &= 0x0007; + + outport_b(addr, inport_b(addr) & ~(1 << irq)); +} + +void disable_irq(int irq) +{ + int addr; + + addr = (irq > 7) ? PIC_SLAVE + DATA : PIC_MASTER + DATA; + irq &= 0x0007; + + outport_b(addr, inport_b(addr) | (1 << irq)); +} + +int register_irq(int irq, char *name, void *addr) +{ + if(irq < 0 || irq >= NR_IRQS) { + return -EINVAL; + } + + if(irq_table[irq].registered) { + printk("WARNING: %s(): interrupt %d already registered!\n", __FUNCTION__, irq); + return -EINVAL; + } + irq_table[irq].ticks = 0; + irq_table[irq].name = name; + irq_table[irq].registered = 1; + irq_table[irq].handler = addr; + return 0; +} + +int unregister_irq(int irq) +{ + if(irq < 0 || irq >= NR_IRQS) { + return -EINVAL; + } + + if(!irq_table[irq].registered) { + printk("WARNING: %s(): trying to unregister an unregistered interrupt %d.\n", __FUNCTION__, irq); + return -EINVAL; + } + memset_b(&irq_table[irq], NULL, sizeof(struct interrupts)); + return 0; +} + +/* each ISR points to this function */ +void irq_handler(int irq, struct sigcontext sc) +{ + int real; + + /* this should help to detect hardware problems */ + if(irq == -1) { + printk("Unknown IRQ received!\n"); + return; + } + + /* spurious interrupt treatment */ + if(!irq_table[irq].handler) { + real = pic_get_irq_reg(PIC_READ_ISR); + if(!real) { + /* + * If IRQ was not real and came from slave, then send + * an EOI to master because it doesn't know if the IRQ + * was a spurious interrupt from slave. + */ + if(irq > 7) { + outport_b(PIC_MASTER, EOI); + } + printk("WARNING: spurious interrupt detected (unregistered IRQ %d).\n", irq); + kstat.sirqs++; + return; + } + if(irq > 7) { + outport_b(PIC_SLAVE, EOI); + } + outport_b(PIC_MASTER, EOI); + return; + } + + disable_irq(irq); + if(irq > 7) { + outport_b(PIC_SLAVE, EOI); + } + outport_b(PIC_MASTER, EOI); + + kstat.irqs++; + irq_table[irq].ticks++; + irq_table[irq].handler(&sc); + enable_irq(irq); +} + +/* do bottom halves (interrupts are (FIXME) enabled) */ +void do_bh(void) +{ + struct bh *b; + void (*fn)(void); + + if((b = bh_head)) { + while(b) { + fn = b->fn; + b = b->next; + del_bh(); + (*fn)(); + } + } +} + +void pic_init(void) +{ + int n; + struct bh *b; + + memset_b(irq_table, NULL, sizeof(irq_table)); + memset_b(bh_pool, NULL, sizeof(bh_pool)); + + /* bh free list initialization */ + bh_pool_head = NULL; + n = NR_BH; + while(n--) { + b = &bh_pool[n]; + put_free_bh(b); + } + bh_head = bh_tail = NULL; + + /* remap interrupts for PIC1 */ + outport_b(PIC_MASTER, ICW1_RESET); + outport_b(PIC_MASTER + DATA, IRQ0_ADDR); /* ICW2 */ + outport_b(PIC_MASTER + DATA, 1 << CASCADE_IRQ); /* ICW3 */ + outport_b(PIC_MASTER + DATA, ICW4_8086EOI); + + /* remap interrupts for PIC2 */ + outport_b(PIC_SLAVE, ICW1_RESET); + outport_b(PIC_SLAVE + DATA, IRQ8_ADDR); /* ICW2 */ + outport_b(PIC_SLAVE + DATA, CASCADE_IRQ); /* ICW3 */ + outport_b(PIC_SLAVE + DATA, ICW4_8086EOI); + + /* mask all IRQs except cascade */ + outport_b(PIC_MASTER + DATA, ~(1 << CASCADE_IRQ)); + + /* mask all IRQs */ + outport_b(PIC_SLAVE + DATA, OCW1); +} diff --git a/kernel/pit.c b/kernel/pit.c new file mode 100644 index 00000000..813df572 --- /dev/null +++ b/kernel/pit.c @@ -0,0 +1,29 @@ +/* + * fiwix/kernel/pit.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include + +void pit_beep_on(void) +{ + outport_b(MODEREG, SEL_CHAN2 | LSB_MSB | SQUARE_WAVE | BINARY_CTR); + outport_b(CHANNEL2, (OSCIL / BEEP_FREQ) & 0xFF); /* LSB */ + outport_b(CHANNEL2, (OSCIL / BEEP_FREQ) >> 8); /* MSB */ + outport_b(PS2_SYSCTRL_B, inport_b(PS2_SYSCTRL_B) | ENABLE_SDATA | ENABLE_TMR2G); +} + +void pit_beep_off(unsigned int unused) +{ + outport_b(PS2_SYSCTRL_B, inport_b(PS2_SYSCTRL_B) & ~(ENABLE_SDATA | ENABLE_TMR2G)); +} + +void pit_init(unsigned short int hertz) +{ + outport_b(MODEREG, SEL_CHAN0 | LSB_MSB | RATE_GEN | BINARY_CTR); + outport_b(CHANNEL0, (OSCIL / hertz) & 0xFF); /* LSB */ + outport_b(CHANNEL0, (OSCIL / hertz) >> 8); /* MSB */ +} diff --git a/kernel/process.c b/kernel/process.c new file mode 100644 index 00000000..d9814016 --- /dev/null +++ b/kernel/process.c @@ -0,0 +1,399 @@ +/* + * fiwix/kernel/process.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct proc *proc_table; +struct proc *current; + +struct proc *proc_pool_head; +struct proc *proc_table_head; +struct proc *proc_table_tail; +unsigned int free_proc_slots = 0; + +static struct resource slot_resource = { NULL, NULL }; +static struct resource pid_resource = { NULL, NULL }; + +int nr_processes = 0; +__pid_t lastpid = 0; + +int kill_pid(__pid_t pid, __sigset_t signum) +{ + struct proc *p; + + FOR_EACH_PROCESS(p) { + if(p->pid == pid && (p->state && p->state != PROC_ZOMBIE)) { + return send_sig(p, signum); + } + } + return -ESRCH; +} + +int kill_pgrp(__pid_t pgid, __sigset_t signum) +{ + struct proc *p; + int found; + + found = 0; + FOR_EACH_PROCESS(p) { + if(p->pgid == pgid && (p->state && p->state != PROC_ZOMBIE)) { + send_sig(p, signum); + found = 1; + } + } + + if(!found) { + return -ESRCH; + } + return 0; +} + +/* sum up child (and its children) statistics */ +void add_crusage(struct proc *p, struct rusage *cru) +{ + cru->ru_utime.tv_sec = p->usage.ru_utime.tv_sec + p->cusage.ru_utime.tv_sec; + cru->ru_utime.tv_usec = p->usage.ru_utime.tv_usec + p->cusage.ru_utime.tv_usec; + if(cru->ru_utime.tv_usec >= 1000000) { + cru->ru_utime.tv_sec++; + cru->ru_utime.tv_usec -= 1000000; + } + cru->ru_stime.tv_sec = p->usage.ru_stime.tv_sec + p->cusage.ru_stime.tv_sec; + cru->ru_stime.tv_usec = p->usage.ru_stime.tv_usec + p->cusage.ru_stime.tv_usec; + if(cru->ru_stime.tv_usec >= 1000000) { + cru->ru_stime.tv_sec++; + cru->ru_stime.tv_usec -= 1000000; + } + cru->ru_maxrss = p->usage.ru_maxrss + p->cusage.ru_maxrss; + cru->ru_ixrss = p->usage.ru_ixrss + p->cusage.ru_ixrss; + cru->ru_idrss = p->usage.ru_idrss + p->cusage.ru_idrss; + cru->ru_isrss = p->usage.ru_isrss + p->cusage.ru_isrss; + cru->ru_minflt = p->usage.ru_minflt + p->cusage.ru_minflt; + cru->ru_majflt = p->usage.ru_majflt + p->cusage.ru_majflt; + cru->ru_nswap = p->usage.ru_nswap + p->cusage.ru_nswap; + cru->ru_inblock = p->usage.ru_inblock + p->cusage.ru_inblock; + cru->ru_oublock = p->usage.ru_oublock + p->cusage.ru_oublock; + cru->ru_msgsnd = p->usage.ru_msgsnd + p->cusage.ru_msgsnd; + cru->ru_msgrcv = p->usage.ru_msgrcv + p->cusage.ru_msgrcv; + cru->ru_nsignals = p->usage.ru_nsignals + p->cusage.ru_nsignals; + cru->ru_nvcsw = p->usage.ru_nvcsw + p->cusage.ru_nvcsw; + cru->ru_nivcsw = p->usage.ru_nivcsw + p->cusage.ru_nivcsw; +} + +void get_rusage(struct proc *p, struct rusage *ru) +{ + struct rusage cru; + + /* + * Conforms with SUSv3 which specifies that if SIGCHLD is being ignored + * then the child statistics should not be added to the values returned + * by RUSAGE_CHILDREN. + */ + if(current->sigaction[SIGCHLD - 1].sa_handler == SIG_IGN) { + return; + } + + add_crusage(p, &cru); + memcpy_b(ru, &cru, sizeof(struct rusage)); +} + +/* add child statistics to parent */ +void add_rusage(struct proc *p) +{ + struct rusage cru; + + add_crusage(p, &cru); + current->cusage.ru_utime.tv_sec += cru.ru_utime.tv_sec; + current->cusage.ru_utime.tv_usec += cru.ru_utime.tv_usec; + if(current->cusage.ru_utime.tv_usec >= 1000000) { + current->cusage.ru_utime.tv_sec++; + current->cusage.ru_utime.tv_usec -= 1000000; + } + current->cusage.ru_stime.tv_sec += cru.ru_stime.tv_sec; + current->cusage.ru_stime.tv_usec += cru.ru_stime.tv_usec; + if(current->cusage.ru_stime.tv_usec >= 1000000) { + current->cusage.ru_stime.tv_sec++; + current->cusage.ru_stime.tv_usec -= 1000000; + } + current->cusage.ru_maxrss += cru.ru_maxrss; + current->cusage.ru_ixrss += cru.ru_ixrss; + current->cusage.ru_idrss += cru.ru_idrss; + current->cusage.ru_isrss += cru.ru_isrss; + current->cusage.ru_minflt += cru.ru_minflt; + current->cusage.ru_majflt += cru.ru_majflt; + current->cusage.ru_nswap += cru.ru_nswap; + current->cusage.ru_inblock += cru.ru_inblock; + current->cusage.ru_oublock += cru.ru_oublock; + current->cusage.ru_msgsnd += cru.ru_msgsnd; + current->cusage.ru_msgrcv += cru.ru_msgrcv; + current->cusage.ru_nsignals += cru.ru_nsignals; + current->cusage.ru_nvcsw += cru.ru_nvcsw; + current->cusage.ru_nivcsw += cru.ru_nivcsw; +} + +struct proc * get_next_zombie(struct proc *parent) +{ + struct proc *p; + + if(proc_table_head == NULL) { + PANIC("process table is empty!\n"); + } + + FOR_EACH_PROCESS(p) { + if(p->ppid == parent->pid && p->state == PROC_ZOMBIE) { + return p; + } + } + + return NULL; +} + +__pid_t remove_zombie(struct proc *p) +{ + __pid_t pid; + + pid = p->pid; + kfree((unsigned int)p->vma); current->rss--; + kfree(p->tss.esp0); current->rss--; + kfree(P2V(p->tss.cr3)); current->rss--; + release_proc(p); + current->children--; + return pid; +} + +/* + * An orphaned process group is a process group in which the parent of every + * member is either itself a member of the group or is not a member of the + * group's session. + */ +int is_orphaned_pgrp(__pid_t pgid) +{ + struct proc *p, *pp; + + FOR_EACH_PROCESS(p) { + if(p->pgid != pgid) { + continue; + } + if(p->state && p->state != PROC_ZOMBIE) { + pp = get_proc_by_pid(p->ppid); + /* return if only one is found that breaks the rule */ + if(pp->pgid != pgid || pp->sid == p->sid) { + return 0; + } + } + } + return 1; +} + +struct proc * get_proc_free(void) +{ + struct proc *p = NULL; + + if(free_proc_slots <= SAFE_SLOTS && !IS_SUPERUSER) { + printk("WARNING: %s(): the remaining slots are only for root user!\n", __FUNCTION__); + return NULL; + } + + lock_resource(&slot_resource); + + if(proc_pool_head) { + + /* get (remove) a process slot from the free list */ + p = proc_pool_head; + proc_pool_head = proc_pool_head->next; + + free_proc_slots--; + } else { + printk("WARNING: %s(): no more slots free in proc table!\n", __FUNCTION__); + } + + unlock_resource(&slot_resource); + return p; +} + +void release_proc(struct proc *p) +{ + lock_resource(&slot_resource); + + /* remove a process from the proc_table */ + if(p == proc_table_tail) { + if(proc_table_head == proc_table_tail) { + proc_table_head = proc_table_tail = NULL; + } else { + proc_table_tail = proc_table_tail->prev; + proc_table_tail->next = NULL; + } + } else { + p->prev->next = p->next; + p->next->prev = p->prev; + } + + /* initialize and put a process slot back in the free list */ + memset_b(p, NULL, sizeof(struct proc)); + p->next = proc_pool_head; + proc_pool_head = p; + free_proc_slots++; + + unlock_resource(&slot_resource); +} + +int get_unused_pid(void) +{ + short int loop; + struct proc *p; + + loop = 0; + lock_resource(&pid_resource); + +loop: + lastpid++; + if(lastpid > MAX_PID_VALUE) { + loop++; + lastpid = INIT; + } + if(loop > 1) { + printk("WARNING: %s(): system ran out of PID numbers!\n"); + return 0; + } + FOR_EACH_PROCESS(p) { + if(p->state != PROC_UNUSED) { + /* + * Make sure the kernel never reuses active pid, pgid + * or sid values. + */ + if(lastpid == p->pid || lastpid == p->pgid || lastpid == p->sid) { + goto loop; + } + } + } + + unlock_resource(&pid_resource); + return lastpid; +} + +struct proc * get_proc_by_pid(__pid_t pid) +{ + struct proc *p; + + FOR_EACH_PROCESS(p) { + if(p->state && p->pid == pid) { + return p; + } + } + + PANIC("would return NULL! (current->pid=%d pid=%d)\n", current->pid, pid); + return NULL; +} + +int get_new_user_fd(int fd) +{ + int n; + + for(n = fd; n < OPEN_MAX && n < current->rlim[RLIMIT_NOFILE].rlim_cur; n++) { + if(current->fd[n] == 0) { + current->fd[n] = -1; + current->fd_flags[n] = 0; + return n; + } + } + + return -EMFILE; +} + +void release_user_fd(int ufd) +{ + current->fd[ufd] = 0; +} + +struct proc * kernel_process(int (*fn)(void)) +{ + struct proc *p; + + p = get_proc_free(); + proc_slot_init(p); + p->pid = get_unused_pid(); + p->ppid = 0; + p->flags |= PF_KPROC; + p->priority = DEF_PRIORITY; + if(!(p->tss.esp0 = kmalloc())) { + release_proc(p); + return NULL; + } + p->tss.esp0 += PAGE_SIZE - 4; + p->rss++; + p->tss.cr3 = (unsigned int)kpage_dir; + p->tss.eip = (unsigned int)fn; + p->tss.esp = p->tss.esp0; + sprintk(p->pidstr, "%d", p->pid); + p->state = PROC_RUNNING; + return p; +} + +void proc_slot_init(struct proc *p) +{ + int n; + + /* insert process at the end of proc_table */ + lock_resource(&slot_resource); + if(proc_table_head == NULL) { + p->prev = NULL; + p->next = NULL; + proc_table_head = proc_table_tail = p; + } else { + p->prev = proc_table_tail; + p->next = NULL; + proc_table_tail->next = p; + proc_table_tail = p; + } + p->sleep_prev = p->sleep_next = NULL; + unlock_resource(&slot_resource); + + memset_b(&p->tss, NULL, sizeof(struct i386tss)); + p->tss.io_bitmap_addr = (unsigned int)&p->io_bitmap; + + /* + * At the moment, all io_bitmap bits are setup to 0, which means full + * access. This must be changed to 1 once we have fixed the ioperm() + * system call. + */ + for(n = 0; n < IO_BITMAP_SIZE + 1; n++) { + p->io_bitmap[n] = 0; /* FIXME: change it to 1 */ + } + p->state = PROC_IDLE; +} + +void proc_init(void) +{ + int n; + struct proc *p; + + memset_b(proc_table, NULL, proc_table_size); + + /* free list initialization */ + proc_pool_head = NULL; + n = (proc_table_size / sizeof(struct proc)) - 1; + do { + p = &proc_table[n]; + + /* fill the free list */ + p->next = proc_pool_head; + proc_pool_head = p; + free_proc_slots++; + } while(n--); + proc_table_head = proc_table_tail = NULL; + + /* slot 0 is for the IDLE process */ + current = get_proc_free(); + proc_slot_init(current); +} diff --git a/kernel/sched.c b/kernel/sched.c new file mode 100644 index 00000000..2449ded3 --- /dev/null +++ b/kernel/sched.c @@ -0,0 +1,85 @@ +/* + * fiwix/kernel/sched.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct seg_desc gdt[NR_GDT_ENTRIES]; +int need_resched = 0; + +static void context_switch(struct proc *next) +{ + struct proc *prev; + + kstat.ctxt++; + prev = current; + set_tss(next); + current = next; + do_switch(&prev->tss.esp, &prev->tss.eip, next->tss.esp, next->tss.eip, next->tss.cr3, TSS); +} + +void set_tss(struct proc *p) +{ + struct seg_desc *g; + + g = &gdt[TSS / sizeof(struct seg_desc)]; + + g->sd_lobase = (unsigned int)p; + g->sd_loflags = SD_TSSPRESENT; + g->sd_hibase = (char)(((unsigned int)p) >> 24); +} + +/* Round Robin algorithm */ +void do_sched(void) +{ + int count; + struct proc *p, *selected; + + /* let the current running process consume its time slice */ + if(!need_resched && current->state == PROC_RUNNING && current->cpu_count > 0) { + return; + } + + need_resched = 0; + for(;;) { + count = -1; + selected = &proc_table[IDLE]; + + FOR_EACH_PROCESS(p) { + if(p->state == PROC_RUNNING && p->cpu_count > count) { + count = p->cpu_count; + selected = p; + } + } + if(count) { + break; + } + + /* reassigns new quantum to all processes */ + FOR_EACH_PROCESS(p) { + if(p->state) { + p->cpu_count = p->priority; + } + } + } + if(current != selected) { + context_switch(selected); + } +} + +void sched_init(void) +{ + get_system_time(); +} diff --git a/kernel/signal.c b/kernel/signal.c new file mode 100644 index 00000000..8619f226 --- /dev/null +++ b/kernel/signal.c @@ -0,0 +1,202 @@ +/* + * fiwix/kernel/signal.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int send_sig(struct proc *p, __sigset_t signum) +{ + struct proc *z; + + if(signum > NSIG || !p) { + return -EINVAL; + } + + if(!IS_SUPERUSER && current->euid != p->euid && current->sid != p->sid) { + return -EPERM; + } + + /* kernel processes can't receive signals */ + if(p->flags & PF_KPROC) { + return 0; + } + + switch(signum) { + case 0: + return 0; + case SIGKILL: + case SIGCONT: + if(p->state == PROC_STOPPED) { + p->state = PROC_RUNNING; + } + /* discard all pending stop signals */ + p->sigpending &= SIG_MASK(SIGSTOP); + p->sigpending &= SIG_MASK(SIGTSTP); + p->sigpending &= SIG_MASK(SIGTTIN); + p->sigpending &= SIG_MASK(SIGTTOU); + break; + case SIGSTOP: + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + /* discard all pending SIGCONT signals */ + p->sigpending &= SIG_MASK(SIGCONT); + break; + default: + break; + } + + if(p->sigaction[signum - 1].sa_handler == SIG_IGN && signum != SIGCHLD) { + return 0; + } + + /* SIGCHLD is ignored by default */ + if(p->sigaction[signum - 1].sa_handler == SIG_DFL) { + /* + if(p->pid == INIT) { + return 0; + } + */ + if(signum == SIGCHLD) { + return 0; + } + } + + /* if SIGCHLD is ignored reap its children (prevent zombies) */ + if(p->sigaction[signum - 1].sa_handler == SIG_IGN) { + if(signum == SIGCHLD) { + while((z = get_next_zombie(p))) { + remove_zombie(z); + } + return 0; + } + } + + p->sigpending |= 1 << (signum - 1); + p->usage.ru_nsignals++; + + /* wake up the process only if that signal is not blocked */ + if(!(p->sigblocked & (1 << (signum - 1)))) { + wakeup_proc(p); + } + + return 0; +} + +int issig(void) +{ + __sigset_t signum; + unsigned int mask; + struct proc *p; + + if(!(current->sigpending & ~current->sigblocked)) { + return 0; + } + + for(signum = 1, mask = 1; signum < NSIG; signum++, mask <<= 1) { + if(current->sigpending & mask) { + if(signum == SIGCHLD) { + if(current->sigaction[signum - 1].sa_handler == SIG_IGN) { + /* this process ignores SIGCHLD */ + while((p = get_next_zombie(current))) { + remove_zombie(p); + } + } else { + if(current->sigaction[signum - 1].sa_handler != SIG_DFL) { + return signum; + } + } + } else { + if(current->sigaction[signum - 1].sa_handler != SIG_IGN) { + return signum; + } + } + current->sigpending &= ~mask; + } + } + return 0; +} + +void psig(unsigned int stack) +{ + int len; + __sigset_t signum; + unsigned int mask; + struct sigcontext *sc; + + sc = (struct sigcontext *)stack; + for(signum = 1, mask = 1; signum < NSIG; signum++, mask <<= 1) { + if(current->sigpending & mask) { + current->sigpending &= ~mask; + + if((unsigned int)current->sigaction[signum - 1].sa_handler) { + current->sigexecuting = mask; + if(!(current->sigaction[signum - 1].sa_flags & SA_NODEFER)) { + current->sigblocked |= mask; + } + + /* save the current sigcontext */ + memcpy_b(¤t->sc[signum - 1], sc, sizeof(struct sigcontext)); + /* setup the jump to the user signal handler */ + len = ((int)end_sighandler_trampoline - (int)sighandler_trampoline); + sc->oldesp -= len; + sc->oldesp -= 4; + sc->oldesp &= ~3; /* round up */ + memcpy_b((void *)sc->oldesp, sighandler_trampoline, len); + sc->ecx = (unsigned int)current->sigaction[signum - 1].sa_handler; + sc->eax= signum; + sc->eip = sc->oldesp; + + if(current->sigaction[signum - 1].sa_flags & SA_RESETHAND) { + current->sigaction[signum - 1].sa_handler = SIG_DFL; + } + return; + } + if(current->sigaction[signum - 1].sa_handler == SIG_DFL) { + switch(signum) { + case SIGCONT: + current->state = PROC_RUNNING; + need_resched = 1; + break; + case SIGSTOP: + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + current->exit_code = signum; + current->state = PROC_STOPPED; + if(!(current->sigaction[signum - 1].sa_flags & SA_NOCLDSTOP)) { + send_sig(get_proc_by_pid(current->ppid), SIGCHLD); + } + need_resched = 1; + break; + case SIGCHLD: + break; + default: + do_exit(signum); + } + } + } + } + + /* coming from a system call that needs to be restarted */ + if(sc->err > 0) { + if(sc->eax == -ERESTART) { + sc->eax = sc->err; /* syscall was saved in 'err' */ + sc->eip -= 2; /* point again to 'int 0x80' */ + } + } +} diff --git a/kernel/sleep.c b/kernel/sleep.c new file mode 100644 index 00000000..2eb6cfb5 --- /dev/null +++ b/kernel/sleep.c @@ -0,0 +1,198 @@ +/* + * fiwix/kernel/sleep.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NR_BUCKETS (NR_PROCS * 10) / 100 /* 10% of NR_PROCS */ +#define SLEEP_HASH(addr) ((addr) % NR_BUCKETS) + +struct proc *sleep_hash_table[NR_BUCKETS]; +static unsigned int area = 0; + +int sleep(void *address, int state) +{ + unsigned long int flags; + struct proc **h; + int signum, i; + + /* return if it has signals */ + if(state == PROC_INTERRUPTIBLE) { + if((signum = issig())) { + return signum; + } + } + + if(current->state == PROC_SLEEPING) { + printk("WARNING: %s(): process with pid '%d' is already sleeping!\n", __FUNCTION__, current->pid); + return 0; + } + + SAVE_FLAGS(flags); CLI(); + i = SLEEP_HASH((unsigned int)address); + h = &sleep_hash_table[i]; + + /* insert process in the head */ + if(!*h) { + *h = current; + (*h)->sleep_prev = (*h)->sleep_next = NULL; + } else { + current->sleep_prev = NULL; + current->sleep_next = *h; + (*h)->sleep_prev = current; + *h = current; + } + current->sleep_address = address; + current->state = PROC_SLEEPING; + + do_sched(); + + signum = 0; + if(state == PROC_INTERRUPTIBLE) { + signum = issig(); + } + + RESTORE_FLAGS(flags); + return signum; +} + +void wakeup(void *address) +{ + unsigned long int flags; + struct proc **h; + int i; + + SAVE_FLAGS(flags); CLI(); + i = SLEEP_HASH((unsigned int)address); + h = &sleep_hash_table[i]; + + while(*h) { + if((*h)->sleep_address == address) { + (*h)->sleep_address = NULL; + (*h)->state = PROC_RUNNING; + need_resched = 1; + if((*h)->sleep_next) { + (*h)->sleep_next->sleep_prev = (*h)->sleep_prev; + } + if((*h)->sleep_prev) { + (*h)->sleep_prev->sleep_next = (*h)->sleep_next; + } + if(h == &sleep_hash_table[i]) { /* if it's the head */ + *h = (*h)->sleep_next; + continue; + } + } + if(*h) { + h = &(*h)->sleep_next; + } + } + RESTORE_FLAGS(flags); +} + +void wakeup_proc(struct proc *p) +{ + unsigned long int flags; + struct proc **h; + int i; + + if(p->state != PROC_SLEEPING && p->state != PROC_STOPPED) { + return; + } + + SAVE_FLAGS(flags); CLI(); + + /* stopped processes don't have sleep address */ + if(p->sleep_address) { + if(p->sleep_next) { + p->sleep_next->sleep_prev = p->sleep_prev; + } + if(p->sleep_prev) { + p->sleep_prev->sleep_next = p->sleep_next; + } + + i = SLEEP_HASH((unsigned int)p->sleep_address); + h = &sleep_hash_table[i]; + if(*h == p) { /* if it's the head */ + *h = (*h)->sleep_next; + } + } + p->sleep_address = NULL; + p->state = PROC_RUNNING; + need_resched = 1; + + RESTORE_FLAGS(flags); + return; +} + +void lock_resource(struct resource *resource) +{ + unsigned long int flags; + + for(;;) { + SAVE_FLAGS(flags); CLI(); + if(resource->locked) { + resource->wanted = 1; + RESTORE_FLAGS(flags); + sleep(resource, PROC_UNINTERRUPTIBLE); + } else { + break; + } + } + resource->locked = 1; + RESTORE_FLAGS(flags); +} + +void unlock_resource(struct resource *resource) +{ + unsigned long int flags; + + SAVE_FLAGS(flags); CLI(); + resource->locked = 0; + if(resource->wanted) { + resource->wanted = 0; + wakeup(resource); + } + RESTORE_FLAGS(flags); +} + +int lock_area(unsigned int flag) +{ + unsigned long int flags; + int retval; + + SAVE_FLAGS(flags); CLI(); + retval = area & flag; + area |= flag; + RESTORE_FLAGS(flags); + + return retval; +} + +int unlock_area(unsigned int flag) +{ + unsigned long int flags; + int retval; + + SAVE_FLAGS(flags); CLI(); + retval = area & flag; + area &= ~flag; + RESTORE_FLAGS(flags); + + return retval; +} + +void sleep_init(void) +{ + memset_b(sleep_hash_table, NULL, sizeof(sleep_hash_table)); +} diff --git a/kernel/syscalls.c b/kernel/syscalls.c new file mode 100644 index 00000000..3826e9d7 --- /dev/null +++ b/kernel/syscalls.c @@ -0,0 +1,407 @@ +/* + * fiwix/kernel/syscalls.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +void free_name(const char *name) +{ + kfree((unsigned int)name); +} + +/* + * This function has two objectives: + * 1. to check the memory address validity of the char pointer supplied by the + * user, while at the same time limit its length to PAGE_SIZE (4096) bytes. + * 2. to create a copy of 'filename' in the kernel data space before using it. + */ +int malloc_name(const char *filename, char **name) +{ + struct vma *vma; + unsigned int start; + short int n, len; + char *b; + + /* + * Verifies if the 'vma' array of that process is not empty. It can + * only be empty during the initialization of INIT, when it calls to + * sys_execve and sys_open without having yet a proper setup. + */ + if(current->vma[0].s_type != 0) { + if(!filename) { + return -EFAULT; + } + start = (unsigned int)filename; + if(!(vma = find_vma_region(start))) { + return -EFAULT; + } + if(!(vma->prot & PROT_READ)) { + return -EFAULT; + } + len = MIN(vma->end - start, PAGE_SIZE); + if(len < PAGE_SIZE) { + if((vma = find_vma_region(vma->end))) { + if(vma->prot & PROT_READ) { + len = PAGE_SIZE; + } + } + } + } else { + len = PAGE_SIZE; + } + if(!(b = (char *)kmalloc())) { + return -ENOMEM; + } + *name = b; + for(n = 0; n < len; n++) { + if(!(*b = *filename)) { + return 0; + } + b++; + filename++; + } + + free_name(*name); + return -ENAMETOOLONG; +} + +int check_user_permission(struct inode *i) +{ + if(!IS_SUPERUSER) { + if(current->euid != i->i_uid) { + return 1; + } + } + return 0; +} + +int check_group(struct inode *i) +{ + int n; + __gid_t gid; + + if(current->flags & PF_USEREAL) { + gid = current->gid; + } else { + gid = current->egid; + } + + if(i->i_gid == gid) { + return 0; + } + + for(n = 0; n < NGROUPS_MAX; n++) { + if(current->groups[n] == -1) { + break; + } + if(current->groups[n] == i->i_gid) { + return 0; + } + } + return 1; +} + +int check_user_area(int type, const void *addr, unsigned int size) +{ + struct vma *vma; + unsigned int start; + + /* + * Verifies if the 'vma' array of that process is not empty. It can + * only be empty during the initialization of INIT, when it calls to + * sys_execve and sys_open without having yet a proper setup. + */ + if(current->vma[0].s_type != 0) { + start = (unsigned int)addr; + if(!(vma = find_vma_region(start))) { + return -EFAULT; + } + + for(;;) { + if(type == VERIFY_WRITE) { + if(!(vma->prot & PROT_WRITE)) { + return -EFAULT; + } + } else { + if(!(vma->prot & PROT_READ)) { + return -EFAULT; + } + } + if(start + size <= vma->end) { + break; + } + if(!(vma = find_vma_region(vma->end))) { + return -EFAULT; + } + } + } + + return 0; +} + +int check_permission(int mask, struct inode *i) +{ + __uid_t uid; + + if(current->flags & PF_USEREAL) { + uid = current->uid; + } else { + uid = current->euid; + } + + if(uid == 0) { + return 0; + } + if(i->i_uid == uid) { + if((((i->i_mode >> 6) & 7) & mask) == mask) { + return 0; + } + } + if(!check_group(i)) { + if((((i->i_mode >> 3) & 7) & mask) == mask) { + return 0; + } + } + if(((i->i_mode & 7) & mask) == mask) { + return 0; + } + + return -EACCES; +} + +/* Linux 2.0.40 ABI system call (some from 2.2.26) */ +void *syscall_table[] = { + NULL, /* 0 */ /* sys_setup (-ENOSYS) */ + sys_exit, + sys_fork, + sys_read, + sys_write, + sys_open, /* 5 */ + sys_close, + sys_waitpid, + sys_creat, + sys_link, + sys_unlink, /* 10 */ + sys_execve, + sys_chdir, + sys_time, + sys_mknod, + sys_chmod, /* 15 */ + sys_chown, + NULL, /* sys_break (-ENOSYS) */ + sys_stat, + sys_lseek, + sys_getpid, /* 20 */ + sys_mount, + sys_umount, + sys_setuid, + sys_getuid, + sys_stime, /* 25 */ + NULL, // sys_ptrace + sys_alarm, + sys_fstat, + sys_pause, + sys_utime, /* 30 */ + NULL, /* sys_stty (-ENOSYS) */ + NULL, /* sys_gtty (-ENOSYS) */ + sys_access, + NULL, // sys_nice + sys_ftime, /* 35 */ + sys_sync, + sys_kill, + sys_rename, + sys_mkdir, + sys_rmdir, /* 40 */ + sys_dup, + sys_pipe, + sys_times, + NULL, // sys_prof + sys_brk, /* 45 */ + sys_setgid, + sys_getgid, + sys_signal, + sys_geteuid, + sys_getegid, /* 50 */ + NULL, // sys_acct + sys_umount2, + NULL, /* sys_lock (-ENOSYS) */ + sys_ioctl, + sys_fcntl, /* 55 */ + NULL, /* sys_mpx (-ENOSYS) */ + sys_setpgid, + NULL, /* sys_ulimit (-ENOSYS) */ + sys_olduname, + sys_umask, /* 60 */ + sys_chroot, + sys_ustat, + sys_dup2, + sys_getppid, + sys_getpgrp, /* 65 */ + sys_setsid, + sys_sigaction, + sys_sgetmask, + sys_ssetmask, + sys_setreuid, /* 70 */ + sys_setregid, + sys_sigsuspend, + sys_sigpending, + sys_sethostname, + sys_setrlimit, /* 75 */ + sys_getrlimit, + sys_getrusage, + sys_gettimeofday, + sys_settimeofday, + sys_getgroups, /* 80 */ + sys_setgroups, + old_select, + sys_symlink, + sys_lstat, + sys_readlink, /* 85 */ + NULL, // sys_uselib + NULL, // sys_swapon + sys_reboot, + NULL, // old_readdir + old_mmap, /* 90 */ + sys_munmap, + sys_truncate, + sys_ftruncate, + sys_fchmod, + sys_fchown, /* 95 */ + NULL, // sys_getpriority + NULL, // sys_setpriority + NULL, /* sys_profil (-ENOSYS) */ + sys_statfs, + sys_fstatfs, /* 100 */ + sys_ioperm, + sys_socketcall, // sys_socketcall XXX + NULL, // sys_syslog + sys_setitimer, + sys_getitimer, /* 105 */ + sys_newstat, + sys_newlstat, + sys_newfstat, + sys_uname, + sys_iopl, /* 110 */ + NULL, // sys_vhangup + NULL, /* sys_idle (-ENOSYS) */ + NULL, // sys_vm86old + sys_wait4, + NULL, // sys_swapoff /* 115 */ + sys_sysinfo, + NULL, // sys_ipc + sys_fsync, + sys_sigreturn, + NULL, // sys_clone /* 120 */ + sys_setdomainname, + sys_newuname, + NULL, // sys_modify_ldt + NULL, // sys_adjtimex + sys_mprotect, /* 125 */ + sys_sigprocmask, + NULL, // sys_create_module + NULL, // sys_init_module + NULL, // sys_delete_module + NULL, // sys_get_kernel_syms /* 130 */ + NULL, // sys_quotactl + sys_getpgid, + sys_fchdir, + NULL, // sys_bdflush + NULL, // sys_sysfs /* 135 */ + sys_personality, + NULL, /* afs_syscall (-ENOSYS) */ + sys_setfsuid, + sys_setfsgid, + sys_llseek, /* 140 */ + sys_getdents, + sys_select, + sys_flock, + NULL, // sys_msync + NULL, // sys_readv /* 145 */ + NULL, // sys_writev + sys_getsid, + sys_fdatasync, + NULL, // sys_sysctl + NULL, // sys_mlock /* 150 */ + NULL, // sys_munlock + NULL, // sys_mlockall + NULL, // sys_munlockall + NULL, // sys_sched_setparam + NULL, // sys_sched_getparam /* 155 */ + NULL, // sys_sched_setscheduler + NULL, // sys_sched_getscheduler + NULL, // sys_sched_yield + NULL, // sys_sched_get_priority_max + NULL, // sys_sched_get_priority_min /* 160 */ + NULL, // sys_sched_rr_get_interval + sys_nanosleep, + NULL, // sys_mremap + + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + sys_chown, /* 182 */ + sys_getcwd, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + sys_fork, /* 190 (sys_vfork) */ +}; + +static void do_bad_syscall(unsigned int num) +{ +#ifdef __DEBUG__ + printk("***** (pid %d) system call %d not supported yet *****\n", current->pid, num); +#endif /*__DEBUG__ */ +} + +/* + * The argument 'struct sigcontext' is needed because there are some system + * calls (such as sys_iopl and sys_fork) that need to get information from + * certain registers (EFLAGS and ESP). The rest of system calls will ignore + * such extra argument. + */ +int do_syscall(unsigned int num, int arg1, int arg2, int arg3, int arg4, int arg5, struct sigcontext sc) +{ + int (*sys_func)(int, ...); + + if(num > NR_SYSCALLS) { + do_bad_syscall(num); + return -ENOSYS; + } + sys_func = syscall_table[num]; + if(!sys_func) { + do_bad_syscall(num); + return -ENOSYS; + } + current->sp = (unsigned int)≻ + return sys_func(arg1, arg2, arg3, arg4, arg5, &sc); +} diff --git a/kernel/syscalls/Makefile b/kernel/syscalls/Makefile new file mode 100644 index 00000000..4c7a85a3 --- /dev/null +++ b/kernel/syscalls/Makefile @@ -0,0 +1,20 @@ +# fiwix/kernel/syscalls/Makefile +# +# Copyright 2018, Jordi Sanfeliu. All rights reserved. +# Distributed under the terms of the Fiwix License. +# + +.S.o: + $(CC) -traditional -I$(INCLUDE) -c -o $@ $< +.c.o: + $(CC) $(CFLAGS) -c -o $@ $< + +SRC = $(wildcard *.c) +OBJS = $(patsubst %.c,%.o,$(SRC)) + +lib: $(OBJS) + $(LD) $(LDFLAGS) -r $(OBJS) -o syscalls.o + +clean: + rm -f *.o + diff --git a/kernel/syscalls/access.c b/kernel/syscalls/access.c new file mode 100644 index 00000000..18c22ea3 --- /dev/null +++ b/kernel/syscalls/access.c @@ -0,0 +1,61 @@ +/* + * fiwix/kernel/syscalls/access.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_access(const char *filename, __mode_t mode) +{ + struct inode *i; + char *tmp_name; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_access('%s', %d)", current->pid, filename, mode); +#endif /*__DEBUG__ */ + + if((mode & S_IRWXO) != mode) { + return -EINVAL; + } + if((errno = malloc_name(filename, &tmp_name)) < 0) { + return errno; + } + current->flags |= PF_USEREAL; + if((errno = namei(tmp_name, &i, NULL, FOLLOW_LINKS))) { + current->flags &= ~PF_USEREAL; + free_name(tmp_name); + return errno; + } + if(mode & TO_WRITE) { + if(S_ISREG(i->i_mode) || S_ISDIR(i->i_mode) || S_ISLNK(i->i_mode)) { + if(IS_RDONLY_FS(i)) { + current->flags &= ~PF_USEREAL; + iput(i); + free_name(tmp_name); + return -EROFS; + } + } + } + errno = check_permission(mode, i); + +#ifdef __DEBUG__ + printk(" -> returning %d\n", errno); +#endif /*__DEBUG__ */ + + current->flags &= ~PF_USEREAL; + iput(i); + free_name(tmp_name); + return errno; +} diff --git a/kernel/syscalls/alarm.c b/kernel/syscalls/alarm.c new file mode 100644 index 00000000..5ee4021e --- /dev/null +++ b/kernel/syscalls/alarm.c @@ -0,0 +1,39 @@ +/* + * fiwix/kernel/syscalls/alarm.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_alarm(unsigned int secs) +{ + struct itimerval value, ovalue; + +#ifdef __DEBUG__ + printk("(pid %d) sys_alarm(%d)\n", current->pid, secs); +#endif /*__DEBUG__ */ + + value.it_interval.tv_sec = 0; + value.it_interval.tv_usec = 0; + value.it_value.tv_sec = secs; + value.it_value.tv_usec = 0; + setitimer(ITIMER_REAL, &value, &ovalue); + + /* + * If there are still some usecs left and since the return value has + * not enough granularity for them, then just add 1 second to it. + */ + if(ovalue.it_value.tv_usec) { + ovalue.it_value.tv_sec++; + } + + return ovalue.it_value.tv_sec; +} diff --git a/kernel/syscalls/brk.c b/kernel/syscalls/brk.c new file mode 100644 index 00000000..5fbff98c --- /dev/null +++ b/kernel/syscalls/brk.c @@ -0,0 +1,54 @@ +/* + * fiwix/kernel/syscalls/brk.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_brk(unsigned int brk) +{ + unsigned int newbrk; + +#ifdef __DEBUG__ + printk("(pid %d) sys_brk(0x%08x) -> ", current->pid, brk); +#endif /*__DEBUG__ */ + + if(!brk || brk < current->brk_lower) { +#ifdef __DEBUG__ + printk("0x%08x\n", current->brk); +#endif /*__DEBUG__ */ + return current->brk; + } + + newbrk = PAGE_ALIGN(brk); + if(newbrk == current->brk || newbrk < current->brk_lower) { +#ifdef __DEBUG__ + printk("0x%08x\n", current->brk); +#endif /*__DEBUG__ */ + return brk; + } + + if(brk < current->brk) { + do_munmap(newbrk, current->brk - newbrk); + current->brk = brk; + return brk; + } + if(!expand_heap(newbrk)) { + current->brk = brk; + } else { + return -ENOMEM; + } +#ifdef __DEBUG__ + printk("0x%08x\n", current->brk); +#endif /*__DEBUG__ */ + return current->brk; +} diff --git a/kernel/syscalls/chdir.c b/kernel/syscalls/chdir.c new file mode 100644 index 00000000..6ca42e3c --- /dev/null +++ b/kernel/syscalls/chdir.c @@ -0,0 +1,49 @@ +/* + * fiwix/kernel/syscalls/chdir.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_chdir(const char *dirname) +{ + struct inode *i; + char *tmp_name; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_chdir('%s')\n", current->pid, dirname); +#endif /*__DEBUG__ */ + + if((errno = malloc_name(dirname, &tmp_name)) < 0) { + return errno; + } + if((errno = namei(tmp_name, &i, NULL, FOLLOW_LINKS))) { + free_name(tmp_name); + return errno; + } + if(!S_ISDIR(i->i_mode)) { + iput(i); + free_name(tmp_name); + return -ENOTDIR; + } + if((errno = check_permission(TO_EXEC, i))) { + iput(i); + free_name(tmp_name); + return errno; + } + iput(current->pwd); + current->pwd = i; + free_name(tmp_name); + return 0; +} diff --git a/kernel/syscalls/chmod.c b/kernel/syscalls/chmod.c new file mode 100644 index 00000000..636b0f04 --- /dev/null +++ b/kernel/syscalls/chmod.c @@ -0,0 +1,56 @@ +/* + * fiwix/kernel/syscalls/chmod.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_chmod(const char *filename, __mode_t mode) +{ + struct inode *i; + char *tmp_name; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_chmod('%s', %d)\n", current->pid, filename, mode); +#endif /*__DEBUG__ */ + + if((errno = malloc_name(filename, &tmp_name)) < 0) { + return errno; + } + if((errno = namei(tmp_name, &i, NULL, FOLLOW_LINKS))) { + free_name(tmp_name); + return errno; + } + + if(IS_RDONLY_FS(i)) { + iput(i); + free_name(tmp_name); + return -EROFS; + } + if(check_user_permission(i)) { + iput(i); + free_name(tmp_name); + return -EPERM; + } + + i->i_mode &= S_IFMT; + i->i_mode |= mode & ~S_IFMT; + i->i_ctime = CURRENT_TIME; + i->dirty = 1; + iput(i); + free_name(tmp_name); + return 0; +} diff --git a/kernel/syscalls/chown.c b/kernel/syscalls/chown.c new file mode 100644 index 00000000..a939b5af --- /dev/null +++ b/kernel/syscalls/chown.c @@ -0,0 +1,68 @@ +/* + * fiwix/kernel/syscalls/chown.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_chown(const char *filename, __uid_t owner, __gid_t group) +{ + struct inode *i; + char *tmp_name; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_chown('%s', %d, %d)\n", current->pid, filename, owner, group); +#endif /*__DEBUG__ */ + + if((errno = malloc_name(filename, &tmp_name)) < 0) { + return errno; + } + if((errno = namei(tmp_name, &i, NULL, FOLLOW_LINKS))) { + free_name(tmp_name); + return errno; + } + + if(IS_RDONLY_FS(i)) { + iput(i); + free_name(tmp_name); + return -EROFS; + } + if(check_user_permission(i)) { + iput(i); + free_name(tmp_name); + return -EPERM; + } + + if(owner == (__uid_t)-1) { + owner = i->i_uid; + } else { + i->i_mode &= ~(S_ISUID); + i->i_ctime = CURRENT_TIME; + } + if(group == (__gid_t)-1) { + group = i->i_gid; + } else { + i->i_mode &= ~(S_ISGID); + i->i_ctime = CURRENT_TIME; + } + + i->i_uid = owner; + i->i_gid = group; + i->dirty = 1; + iput(i); + free_name(tmp_name); + return 0; +} diff --git a/kernel/syscalls/chroot.c b/kernel/syscalls/chroot.c new file mode 100644 index 00000000..b9c44ed2 --- /dev/null +++ b/kernel/syscalls/chroot.c @@ -0,0 +1,44 @@ +/* + * fiwix/kernel/syscalls/chroot.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_chroot(const char *dirname) +{ + struct inode *i; + char *tmp_name; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_chroot('%s')\n", current->pid, dirname); +#endif /*__DEBUG__ */ + + if((errno = malloc_name(dirname, &tmp_name)) < 0) { + return errno; + } + if((errno = namei(tmp_name, &i, NULL, FOLLOW_LINKS))) { + free_name(tmp_name); + return errno; + } + if(!S_ISDIR(i->i_mode)) { + iput(i); + free_name(tmp_name); + return -ENOTDIR; + } + iput(current->root); + current->root = i; + free_name(tmp_name); + return 0; +} diff --git a/kernel/syscalls/close.c b/kernel/syscalls/close.c new file mode 100644 index 00000000..7c7bb18d --- /dev/null +++ b/kernel/syscalls/close.c @@ -0,0 +1,39 @@ +/* + * fiwix/kernel/syscalls/close.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +int sys_close(unsigned int ufd) +{ + unsigned int fd; + struct inode *i; + +#ifdef __DEBUG__ + printk("(pid %d) sys_close(%d)\n", current->pid, ufd); +#endif /*__DEBUG__ */ + + CHECK_UFD(ufd); + fd = current->fd[ufd]; + release_user_fd(ufd); + + if(--fd_table[fd].count) { + return 0; + } + i = fd_table[fd].inode; + flock_release_inode(i); + if(i->fsop && i->fsop->close) { + i->fsop->close(i, &fd_table[fd]); + release_fd(fd); + iput(i); + return 0; + } + printk("WARNING: %s(): ufd %d without the close() method!\n", __FUNCTION__, ufd); + return -EINVAL; +} diff --git a/kernel/syscalls/creat.c b/kernel/syscalls/creat.c new file mode 100644 index 00000000..83fb493d --- /dev/null +++ b/kernel/syscalls/creat.c @@ -0,0 +1,23 @@ +/* + * fiwix/kernel/syscalls/creat.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_creat(const char *filename, __mode_t mode) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_creat('%s', %d)\n", current->pid, filename, mode); +#endif /*__DEBUG__ */ + return sys_open(filename, O_CREAT | O_WRONLY | O_TRUNC, mode); +} diff --git a/kernel/syscalls/dup.c b/kernel/syscalls/dup.c new file mode 100644 index 00000000..cf5d4d18 --- /dev/null +++ b/kernel/syscalls/dup.c @@ -0,0 +1,37 @@ +/* + * fiwix/kernel/syscalls/dup.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_dup(unsigned int ufd) +{ + int new_ufd; + +#ifdef __DEBUG__ + printk("(pid %d) sys_dup(%d)", current->pid, ufd); +#endif /*__DEBUG__ */ + + CHECK_UFD(ufd); + if((new_ufd = get_new_user_fd(0)) < 0) { + return new_ufd; + } + +#ifdef __DEBUG__ + printk(" -> %d\n", new_ufd); +#endif /*__DEBUG__ */ + + current->fd[new_ufd] = current->fd[ufd]; + fd_table[current->fd[new_ufd]].count++; + return new_ufd; +} diff --git a/kernel/syscalls/dup2.c b/kernel/syscalls/dup2.c new file mode 100644 index 00000000..aee6f688 --- /dev/null +++ b/kernel/syscalls/dup2.c @@ -0,0 +1,41 @@ +/* + * fiwix/kernel/syscalls/dup2.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_dup2(int old_ufd, int new_ufd) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_dup2(%d, %d)", current->pid, old_ufd, new_ufd); +#endif /*__DEBUG__ */ + + CHECK_UFD(old_ufd); + if(new_ufd < 0 || new_ufd > OPEN_MAX) { + return -EINVAL; + } + if(old_ufd == new_ufd) { + return new_ufd; + } + if(current->fd[new_ufd]) { + sys_close(new_ufd); + } + if((new_ufd = get_new_user_fd(new_ufd)) < 0) { + return new_ufd; + } + current->fd[new_ufd] = current->fd[old_ufd]; + fd_table[current->fd[new_ufd]].count++; +#ifdef __DEBUG__ + printk(" --> returning %d\n", new_ufd); +#endif /*__DEBUG__ */ + return new_ufd; +} diff --git a/kernel/syscalls/execve.c b/kernel/syscalls/execve.c new file mode 100644 index 00000000..903cc626 --- /dev/null +++ b/kernel/syscalls/execve.c @@ -0,0 +1,83 @@ +/* + * fiwix/kernel/syscalls/execve.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_execve(const char *filename, char *argv[], char *envp[], int arg4, int arg5, struct sigcontext *sc) +{ + int n, errno; + struct inode *i; + char *tmp_name; + +#ifdef __DEBUG__ + printk("(pid %d) sys_execve('%s', ...)\n", current->pid, filename); +#endif /*__DEBUG__ */ + + if((errno = malloc_name(filename, &tmp_name)) < 0) { + return errno; + } + if((errno = namei(tmp_name, &i, NULL, FOLLOW_LINKS))) { + free_name(tmp_name); + return errno; + } + if(!S_ISREG(i->i_mode)) { + iput(i); + free_name(tmp_name); + return -EACCES; + } + if(check_permission(TO_EXEC, i) < 0) { + iput(i); + free_name(tmp_name); + return -EACCES; + } + + if((errno = elf_load(i, &(*argv), &(*envp), sc))) { + iput(i); + free_name(tmp_name); + return errno; + } + + if(i->i_mode & S_ISUID) { + current->euid = i->i_uid; + } + if(i->i_mode & S_ISGID) { + current->egid = i->i_gid; + } + + for(n = 0; n < OPEN_MAX; n++) { + if(current->fd[n] && (current->fd_flags[n] & FD_CLOEXEC)) { + sys_close(n); + } + } + + current->suid = current->euid; + current->sgid = current->egid; + current->sigpending = 0; + current->sigexecuting = 0; + for(n = 0; n < NSIG; n++) { + current->sigaction[n].sa_mask = 0; + current->sigaction[n].sa_flags = 0; + if(current->sigaction[n].sa_handler != SIG_IGN) { + current->sigaction[n].sa_handler = SIG_DFL; + } + } + current->sleep_address = NULL; + current->flags |= PF_PEXEC; + iput(i); + free_name(tmp_name); + return 0; +} diff --git a/kernel/syscalls/exit.c b/kernel/syscalls/exit.c new file mode 100644 index 00000000..b62b6c6e --- /dev/null +++ b/kernel/syscalls/exit.c @@ -0,0 +1,118 @@ +/* + * fiwix/kernel/syscalls/exit.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void do_exit(int exit_code) +{ + int n; + struct proc *p, *init; + +#ifdef __DEBUG__ + printk("\n"); + printk("sys_exit(pid %d, ppid %d)\n", current->pid, current->ppid); + printk("------------------------------\n"); +#endif /*__DEBUG__ */ + + CLI(); + current->state = PROC_ZOMBIE; + + release_binary(); + current->argv = NULL; + current->envp = NULL; + current->sigpending = 0; + current->sigblocked = 0; + current->sigexecuting = 0; + for(n = 0; n < NSIG; n++) { + current->sigaction[n].sa_mask = 0; + current->sigaction[n].sa_flags = 0; + current->sigaction[n].sa_handler = SIG_IGN; + } + + init = get_proc_by_pid(INIT); + FOR_EACH_PROCESS(p) { + if(SESS_LEADER(current)) { + if(p->sid == current->sid && p->state != PROC_ZOMBIE) { + p->pgid = 0; + p->sid = 0; + p->ctty = NULL; + send_sig(p, SIGHUP); + send_sig(p, SIGCONT); + } + } + + /* make INIT inherit the children of this exiting process */ + if(p->state && p->ppid == current->pid) { + p->ppid = INIT; + init->children++; + if(p->state == PROC_ZOMBIE) { + send_sig(init, SIGCHLD); + } + } + } + + if(SESS_LEADER(current)) { + disassociate_ctty(current->ctty); + } + + for(n = 0; n < OPEN_MAX; n++) { + if(current->fd[n]) { + sys_close(n); + } + } + + iput(current->root); + current->root = NULL; + iput(current->pwd); + current->pwd = NULL; + current->exit_code = exit_code; + if(!--nr_processes) { + printk("\n"); + printk("WARNING: the last user process has exited. The kernel will stop itself.\n"); + stop_kernel(); + } + + /* notify the parent about the child's death */ + if((p = get_proc_by_pid(current->ppid))) { + send_sig(p, SIGCHLD); + if(p->sleep_address == &sys_wait4) { + wakeup_proc(p); + } + } + + need_resched = 1; + + /* make sure to recover if the process returns from the death (!?) */ + for(;;) { + current->state = PROC_ZOMBIE; + do_sched(); + } +} + +int sys_exit(int exit_code) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_exit()\n", current->pid); +#endif /*__DEBUG__ */ + + /* exit code in the second byte. + * 15 8 7 0 + * +-------------------+-------------------+ + * | exit code (0-255) | 0 | + * +-------------------+-------------------+ + */ + do_exit((exit_code & 0xFF) << 8); + return 0; +} diff --git a/kernel/syscalls/fchdir.c b/kernel/syscalls/fchdir.c new file mode 100644 index 00000000..8d211cf3 --- /dev/null +++ b/kernel/syscalls/fchdir.c @@ -0,0 +1,34 @@ +/* + * fiwix/kernel/syscalls/fchdir.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_fchdir(unsigned int ufd) +{ + struct inode *i; + +#ifdef __DEBUG__ + printk("(pid %d) sys_fchdir(%d)\n", current->pid, ufd); +#endif /*__DEBUG__ */ + + CHECK_UFD(ufd); + i = fd_table[current->fd[ufd]].inode; + if(!S_ISDIR(i->i_mode)) { + return -ENOTDIR; + } + iput(current->pwd); + current->pwd = i; + current->pwd->count++; + return 0; +} diff --git a/kernel/syscalls/fchmod.c b/kernel/syscalls/fchmod.c new file mode 100644 index 00000000..4f241d09 --- /dev/null +++ b/kernel/syscalls/fchmod.c @@ -0,0 +1,42 @@ +/* + * fiwix/kernel/syscalls/fchmod.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_fchmod(int ufd, __mode_t mode) +{ + struct inode *i; + +#ifdef __DEBUG__ + printk("(pid %d) sys_fchmod(%d, %d)\n", current->pid, ufd, mode); +#endif /*__DEBUG__ */ + + CHECK_UFD(ufd); + i = fd_table[current->fd[ufd]].inode; + + if(IS_RDONLY_FS(i)) { + return -EROFS; + } + if(check_user_permission(i)) { + return -EPERM; + } + + i->i_mode &= S_IFMT; + i->i_mode |= mode & ~S_IFMT; + i->i_ctime = CURRENT_TIME; + i->dirty = 1; + return 0; +} diff --git a/kernel/syscalls/fchown.c b/kernel/syscalls/fchown.c new file mode 100644 index 00000000..6656027f --- /dev/null +++ b/kernel/syscalls/fchown.c @@ -0,0 +1,53 @@ +/* + * fiwix/kernel/syscalls/fchown.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_fchown(int ufd, __uid_t owner, __gid_t group) +{ + struct inode *i; + +#ifdef __DEBUG__ + printk("(pid %d) sys_fchown(%d, %d, %d)\n", current->pid, ufd, owner, group); +#endif /*__DEBUG__ */ + + CHECK_UFD(ufd); + i = fd_table[current->fd[ufd]].inode; + + if(IS_RDONLY_FS(i)) { + return -EROFS; + } + if(check_user_permission(i)) { + return -EPERM; + } + + if(owner == (__uid_t)-1) { + owner = i->i_uid; + } else { + i->i_mode &= ~(S_ISUID); + } + if(group == (__gid_t)-1) { + group = i->i_gid; + } else { + i->i_mode &= ~(S_ISGID); + } + + i->i_uid = owner; + i->i_gid = group; + i->i_ctime = CURRENT_TIME; + i->dirty = 1; + return 0; +} diff --git a/kernel/syscalls/fcntl.c b/kernel/syscalls/fcntl.c new file mode 100644 index 00000000..dbe1dfec --- /dev/null +++ b/kernel/syscalls/fcntl.c @@ -0,0 +1,64 @@ +/* + * fiwix/kernel/syscalls/fcntl.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_fcntl(int ufd, int cmd, unsigned long int arg) +{ + int new_ufd, errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_fcntl(%d, %d, 0x%08x)\n", current->pid, ufd, cmd, arg); +#endif /*__DEBUG__ */ + + CHECK_UFD(ufd); + switch(cmd) { + case F_DUPFD: + CHECK_UFD(ufd); + if(arg >= OPEN_MAX) { + return -EINVAL; + } + if((new_ufd = get_new_user_fd(arg)) < 0) { + return new_ufd; + } + current->fd[new_ufd] = current->fd[ufd]; + fd_table[current->fd[new_ufd]].count++; +#ifdef __DEBUG__ + printk("\t--> returning %d\n", new_ufd); +#endif /*__DEBUG__ */ + return new_ufd; + case F_GETFD: + return (current->fd_flags[ufd] & FD_CLOEXEC); + case F_SETFD: + current->fd_flags[ufd] = (arg & FD_CLOEXEC); + break; + case F_GETFL: + return fd_table[current->fd[ufd]].flags; + case F_SETFL: + fd_table[current->fd[ufd]].flags &= ~(O_APPEND | O_NONBLOCK); + fd_table[current->fd[ufd]].flags |= arg & (O_APPEND | O_NONBLOCK); + break; + case F_GETLK: + case F_SETLK: + case F_SETLKW: + if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct flock)))) { + return errno; + } + return posix_lock(ufd, cmd, (struct flock *)arg); + default: + return -EINVAL; + } + return 0; +} diff --git a/kernel/syscalls/fdatasync.c b/kernel/syscalls/fdatasync.c new file mode 100644 index 00000000..ed07cf56 --- /dev/null +++ b/kernel/syscalls/fdatasync.c @@ -0,0 +1,22 @@ +/* + * fiwix/kernel/syscalls/fdatasync.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_fdatasync(int ufd) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_fdatasync(%d)\n", current->pid, ufd); +#endif /*__DEBUG__ */ + + return sys_fsync(ufd); +} diff --git a/kernel/syscalls/flock.c b/kernel/syscalls/flock.c new file mode 100644 index 00000000..6047469a --- /dev/null +++ b/kernel/syscalls/flock.c @@ -0,0 +1,28 @@ +/* + * fiwix/kernel/syscalls/flock.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_flock(int ufd, int op) +{ + struct inode *i; + +#ifdef __DEBUG__ + printk("(pid %d) sys_flock(%d, %d)\n", current->pid, ufd, op); +#endif /*__DEBUG__ */ + + CHECK_UFD(ufd); + i = fd_table[current->fd[ufd]].inode; + return flock_inode(i, op); +} diff --git a/kernel/syscalls/fork.c b/kernel/syscalls/fork.c new file mode 100644 index 00000000..59b4981b --- /dev/null +++ b/kernel/syscalls/fork.c @@ -0,0 +1,155 @@ +/* + * fiwix/kernel/syscalls/fork.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int sys_fork(int arg1, int arg2, int arg3, int arg4, int arg5, struct sigcontext *sc) +{ + int count, pages; + unsigned int n; + unsigned int *child_pgdir; + struct sigcontext *stack; + struct proc *child, *p; + struct vma *vma; + __pid_t pid; + +#ifdef __DEBUG__ + printk("(pid %d) sys_fork()\n", current->pid); +#endif /*__DEBUG__ */ + + /* check the number of processes already allocated by this UID */ + count = 0; + FOR_EACH_PROCESS(p) { + if(p->state && p->uid == current->uid) { + count++; + } + } + if(count > current->rlim[RLIMIT_NPROC].rlim_cur) { + printk("WARNING: %s(): RLIMIT_NPROC exceeded.\n", __FUNCTION__); + return -EAGAIN; + } + + if(!(pid = get_unused_pid())) { + return -EAGAIN; + } + if(!(child = get_proc_free())) { + return -EAGAIN; + } + + /* + * This memcpy() will overwrite the prev and next pointers, so that's + * the reason why proc_slot_init() is separated from get_proc_free(). + */ + memcpy_b(child, current, sizeof(struct proc)); + + proc_slot_init(child); + child->pid = pid; + memset_b(&child->tss, NULL, sizeof(struct i386tss)); + sprintk(child->pidstr, "%d", child->pid); + child->state = PROC_IDLE; + + if(!(child_pgdir = (void *)kmalloc())) { + release_proc(child); + return -ENOMEM; + } + child->rss++; + memcpy_b(child_pgdir, kpage_dir, PAGE_SIZE); + child->tss.cr3 = V2P((unsigned int)child_pgdir); + + child->ppid = current->pid; + child->flags = 0; + child->children = 0; + child->cpu_count = child->priority; + child->start_time = CURRENT_TIME; + child->sleep_address = NULL; + + if(!(child->vma = (void *)kmalloc())) { + kfree((unsigned int)child_pgdir); + release_proc(child); + return -ENOMEM; + } + child->rss++; + memcpy_b(child->vma, current->vma, PAGE_SIZE); + vma = child->vma; + for(n = 0; n < VMA_REGIONS && vma->start; n++, vma++) { + if(vma->inode) { + vma->inode->count++; + } + } + + child->sigpending = 0; + child->sigexecuting = 0; + memset_b(&child->sc, NULL, sizeof(struct sigcontext)); + memset_b(&child->usage, NULL, sizeof(struct rusage)); + memset_b(&child->cusage, NULL, sizeof(struct rusage)); + child->it_real_interval = 0; + child->it_real_value = 0; + child->it_virt_interval = 0; + child->it_virt_value = 0; + child->it_prof_interval = 0; + child->it_prof_value = 0; + + if(!(child->tss.esp0 = kmalloc())) { + kfree((unsigned int)child_pgdir); + kfree((unsigned int)child->vma); + release_proc(child); + return -ENOMEM; + } + + if(!(pages = clone_pages(child))) { + printk("WARNING: %s(): not enough memory, can't clone pages.\n", __FUNCTION__); + free_page_tables(child); + kfree((unsigned int)child_pgdir); + kfree((unsigned int)child->vma); + release_proc(child); + return -ENOMEM; + } + child->rss += pages; + invalidate_tlb(); + + child->tss.esp0 += PAGE_SIZE - 4; + child->rss++; + child->tss.ss0 = KERNEL_DS; + + memcpy_b((unsigned int *)(child->tss.esp0 & PAGE_MASK), (void *)((unsigned int)(sc) & PAGE_MASK), PAGE_SIZE); + stack = (struct sigcontext *)((child->tss.esp0 & PAGE_MASK) + ((unsigned int)(sc) & ~PAGE_MASK)); + + child->tss.eip = (unsigned int)return_from_syscall; + child->tss.esp = (unsigned int)stack; + stack->eax = 0; /* child returns 0 */ + + child->state = PROC_RUNNING; + + /* increase file descriptors usage */ + for(n = 0; n < OPEN_MAX; n++) { + if(current->fd[n]) { + fd_table[current->fd[n]].count++; + } + } + if(current->root) { + current->root->count++; + } + if(current->pwd) { + current->pwd->count++; + } + + kstat.processes++; + nr_processes++; + current->children++; + + return child->pid; /* parent returns child's PID */ +} diff --git a/kernel/syscalls/fstat.c b/kernel/syscalls/fstat.c new file mode 100644 index 00000000..1c63d4b1 --- /dev/null +++ b/kernel/syscalls/fstat.c @@ -0,0 +1,44 @@ +/* + * fiwix/kernel/syscalls/fstat.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_fstat(unsigned int ufd, struct old_stat *statbuf) +{ + struct inode *i; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_fstat(%d, 0x%08x) -> returning structure\n", current->pid, ufd, (unsigned int )statbuf); +#endif /*__DEBUG__ */ + + CHECK_UFD(ufd); + if((errno = check_user_area(VERIFY_WRITE, statbuf, sizeof(struct old_stat)))) { + return errno; + } + i = fd_table[current->fd[ufd]].inode; + statbuf->st_dev = i->dev; + statbuf->st_ino = i->inode; + statbuf->st_mode = i->i_mode; + statbuf->st_nlink = i->i_nlink; + statbuf->st_uid = i->i_uid; + statbuf->st_gid = i->i_gid; + statbuf->st_rdev = i->rdev; + statbuf->st_size = i->i_size; + statbuf->st_atime = i->i_atime; + statbuf->st_mtime = i->i_mtime; + statbuf->st_ctime = i->i_ctime; + return 0; +} diff --git a/kernel/syscalls/fstatfs.c b/kernel/syscalls/fstatfs.c new file mode 100644 index 00000000..d0d94d25 --- /dev/null +++ b/kernel/syscalls/fstatfs.c @@ -0,0 +1,36 @@ +/* + * fiwix/kernel/syscalls/fstatfs.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_fstatfs(unsigned int ufd, struct statfs *statfsbuf) +{ + struct inode *i; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_fstatfs(%d, 0x%08x)\n", current->pid, ufd, (unsigned int)statfsbuf); +#endif /*__DEBUG__ */ + + CHECK_UFD(ufd); + if((errno = check_user_area(VERIFY_WRITE, statfsbuf, sizeof(struct statfs)))) { + return errno; + } + i = fd_table[current->fd[ufd]].inode; + if(i->sb && i->sb->fsop && i->sb->fsop->statfs) { + i->sb->fsop->statfs(i->sb, statfsbuf); + return 0; + } + return -ENOSYS; +} diff --git a/kernel/syscalls/fsync.c b/kernel/syscalls/fsync.c new file mode 100644 index 00000000..cfce64c6 --- /dev/null +++ b/kernel/syscalls/fsync.c @@ -0,0 +1,39 @@ +/* + * fiwix/kernel/syscalls/fsync.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_fsync(int ufd) +{ + struct inode *i; + +#ifdef __DEBUG__ + printk("(pid %d) sys_fsync(%d)\n", current->pid, ufd); +#endif /*__DEBUG__ */ + + CHECK_UFD(ufd); + i = fd_table[current->fd[ufd]].inode; + if(!S_ISREG(i->i_mode)) { + return -EINVAL; + } + if(IS_RDONLY_FS(i)) { + return -EROFS; + } + sync_superblocks(i->dev); + sync_inodes(i->dev); + sync_buffers(i->dev); + return 0; +} diff --git a/kernel/syscalls/ftime.c b/kernel/syscalls/ftime.c new file mode 100644 index 00000000..a4804d89 --- /dev/null +++ b/kernel/syscalls/ftime.c @@ -0,0 +1,34 @@ +/* + * fiwix/kernel/syscalls/ftime.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_ftime(struct timeb *tp) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_ftime()\n", current->pid); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_WRITE, tp, sizeof(struct timeb)))) { + return errno; + } + tp->time = CURRENT_TIME; + tp->millitm = ((kstat.ticks % HZ) * 1000000) / HZ; + /* FIXME: 'timezone' and 'dstflag' fields are not used */ + + return 0; +} diff --git a/kernel/syscalls/ftruncate.c b/kernel/syscalls/ftruncate.c new file mode 100644 index 00000000..79858e5c --- /dev/null +++ b/kernel/syscalls/ftruncate.c @@ -0,0 +1,53 @@ +/* + * fiwix/kernel/syscalls/ftruncate.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_ftruncate(int ufd, __off_t length) +{ + struct inode *i; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_ftruncate(%d, %d)\n", current->pid, ufd, length); +#endif /*__DEBUG__ */ + + CHECK_UFD(ufd); + i = fd_table[current->fd[ufd]].inode; + if((fd_table[current->fd[ufd]].flags & O_ACCMODE) == O_RDONLY) { + return -EINVAL; + } + if(S_ISDIR(i->i_mode)) { + return -EISDIR; + } + if(IS_RDONLY_FS(i)) { + return -EROFS; + } + if(check_permission(TO_WRITE, i) < 0) { + return -EPERM; + } + if(length == i->i_size) { + return 0; + } + + errno = 0; + if(i->fsop && i->fsop->truncate) { + inode_lock(i); + errno = i->fsop->truncate(i, length); + inode_unlock(i); + } + return errno; +} diff --git a/kernel/syscalls/getcwd.c b/kernel/syscalls/getcwd.c new file mode 100644 index 00000000..543d3eb5 --- /dev/null +++ b/kernel/syscalls/getcwd.c @@ -0,0 +1,29 @@ +/* + * fiwix/kernel/syscalls/getcwd.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_getcwd(char *buf, __size_t size) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_getcwd(0x%08x, %d)\n", current->pid, (unsigned int)buf, size); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_WRITE, buf, size))) { + return errno; + } + return -ENOSYS; +} diff --git a/kernel/syscalls/getdents.c b/kernel/syscalls/getdents.c new file mode 100644 index 00000000..46087822 --- /dev/null +++ b/kernel/syscalls/getdents.c @@ -0,0 +1,45 @@ +/* + * fiwix/kernel/syscalls/getdents.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_getdents(unsigned int ufd, struct dirent *dirent, unsigned int count) +{ + struct inode *i; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_getdents(%d, 0x%08x, %d)", current->pid, ufd, (unsigned int)dirent, count); +#endif /*__DEBUG__ */ + + CHECK_UFD(ufd); + if((errno = check_user_area(VERIFY_WRITE, dirent, sizeof(struct dirent)))) { + return errno; + } + i = fd_table[current->fd[ufd]].inode; + + if(!S_ISDIR(i->i_mode)) { + return -ENOTDIR; + } + + if(i->fsop && i->fsop->readdir) { + errno = i->fsop->readdir(i, &fd_table[current->fd[ufd]], dirent, count); + #ifdef __DEBUG__ + printk(" -> returning %d\n", errno); + #endif /*__DEBUG__ */ + return errno; + } + return -EINVAL; +} diff --git a/kernel/syscalls/getegid.c b/kernel/syscalls/getegid.c new file mode 100644 index 00000000..a054349b --- /dev/null +++ b/kernel/syscalls/getegid.c @@ -0,0 +1,20 @@ +/* + * fiwix/kernel/syscalls/getegid.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_getegid(void) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_getegid() -> %d\n", current->pid, current->egid); +#endif /*__DEBUG__ */ + return current->egid; +} diff --git a/kernel/syscalls/geteuid.c b/kernel/syscalls/geteuid.c new file mode 100644 index 00000000..735044f5 --- /dev/null +++ b/kernel/syscalls/geteuid.c @@ -0,0 +1,20 @@ +/* + * fiwix/kernel/syscalls/geteuid.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_geteuid(void) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_geteuid() -> %d\n", current->pid, current->euid); +#endif /*__DEBUG__ */ + return current->euid; +} diff --git a/kernel/syscalls/getgid.c b/kernel/syscalls/getgid.c new file mode 100644 index 00000000..2bb8b209 --- /dev/null +++ b/kernel/syscalls/getgid.c @@ -0,0 +1,20 @@ +/* + * fiwix/kernel/syscalls/getgid.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_getgid(void) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_getgid() -> %d\n", current->pid, current->gid); +#endif /*__DEBUG__ */ + return current->gid; +} diff --git a/kernel/syscalls/getgroups.c b/kernel/syscalls/getgroups.c new file mode 100644 index 00000000..8b80d939 --- /dev/null +++ b/kernel/syscalls/getgroups.c @@ -0,0 +1,40 @@ +/* + * fiwix/kernel/syscalls/getgroups.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_getgroups(__ssize_t size, __gid_t *list) +{ + int n, errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_getgroups(%d, 0x%08x)\n", current->pid, size, (unsigned int)list); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_WRITE, list, sizeof(__gid_t)))) { + return errno; + } + for(n = 0; n < NGROUPS_MAX; n++) { + if(current->groups[n] == -1) { + break; + } + if(size) { + if(n > size) { + return -EINVAL; + } + list[n] = (__gid_t)current->groups[n]; + } + } + return n; +} diff --git a/kernel/syscalls/getitimer.c b/kernel/syscalls/getitimer.c new file mode 100644 index 00000000..20ec7c67 --- /dev/null +++ b/kernel/syscalls/getitimer.c @@ -0,0 +1,48 @@ +/* + * fiwix/kernel/syscalls/getitimer.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_getitimer(int which, struct itimerval *curr_value) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_getitimer(%d, 0x%08x) -> \n", current->pid, which, (unsigned int)curr_value); +#endif /*__DEBUG__ */ + + if((unsigned int)curr_value) { + if((errno = check_user_area(VERIFY_WRITE, curr_value, sizeof(struct itimerval)))) { + return errno; + } + } + + switch(which) { + case ITIMER_REAL: + ticks2tv(current->it_real_interval, &curr_value->it_interval); + ticks2tv(current->it_real_value, &curr_value->it_value); + break; + case ITIMER_VIRTUAL: + ticks2tv(current->it_virt_interval, &curr_value->it_interval); + ticks2tv(current->it_virt_value, &curr_value->it_value); + break; + case ITIMER_PROF: + ticks2tv(current->it_prof_interval, &curr_value->it_interval); + ticks2tv(current->it_prof_value, &curr_value->it_value); + break; + default: + return -EINVAL; + } + return 0; +} diff --git a/kernel/syscalls/getpgid.c b/kernel/syscalls/getpgid.c new file mode 100644 index 00000000..d2756609 --- /dev/null +++ b/kernel/syscalls/getpgid.c @@ -0,0 +1,37 @@ +/* + * fiwix/kernel/syscalls/getpgid.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_getpgid(__pid_t pid) +{ + struct proc *p; + +#ifdef __DEBUG__ + printk("(pid %d) sys_getpgid(%d)\n", current->pid, pid); +#endif /*__DEBUG__ */ + + if(pid < 0) { + return -EINVAL; + } + if(!pid) { + return current->pgid; + } + FOR_EACH_PROCESS(p) { + if(p->state != PROC_UNUSED && p->pid == pid) { + return p->pgid; + } + } + return -ESRCH; +} diff --git a/kernel/syscalls/getpgrp.c b/kernel/syscalls/getpgrp.c new file mode 100644 index 00000000..ef0517f4 --- /dev/null +++ b/kernel/syscalls/getpgrp.c @@ -0,0 +1,20 @@ +/* + * fiwix/kernel/syscalls/getpgrp.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_getpgrp(void) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_getpgrp() -> %d\n", current->pid, current->pgid); +#endif /*__DEBUG__ */ + return current->pgid; +} diff --git a/kernel/syscalls/getpid.c b/kernel/syscalls/getpid.c new file mode 100644 index 00000000..4638f9c5 --- /dev/null +++ b/kernel/syscalls/getpid.c @@ -0,0 +1,20 @@ +/* + * fiwix/kernel/syscalls/getpid.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_getpid(void) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_getpid() -> %d\n", current->pid, current->pid); +#endif /*__DEBUG__ */ + return current->pid; +} diff --git a/kernel/syscalls/getppid.c b/kernel/syscalls/getppid.c new file mode 100644 index 00000000..8e449bd5 --- /dev/null +++ b/kernel/syscalls/getppid.c @@ -0,0 +1,20 @@ +/* + * fiwix/kernel/syscalls/getppid.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_getppid(void) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_getppid() -> %d\n", current->pid, current->ppid); +#endif /*__DEBUG__ */ + return current->ppid; +} diff --git a/kernel/syscalls/getrlimit.c b/kernel/syscalls/getrlimit.c new file mode 100644 index 00000000..7e514752 --- /dev/null +++ b/kernel/syscalls/getrlimit.c @@ -0,0 +1,35 @@ +/* + * fiwix/kernel/syscalls/getrlimit.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_getrlimit(int resource, struct rlimit *rlim) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_getrlimit(%d, 0x%08x)\n", current->pid, resource, (unsigned int)rlim); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_WRITE, rlim, sizeof(struct rlimit)))) { + return errno; + } + if(resource < 0 || resource >= RLIM_NLIMITS) { + return -EINVAL; + } + + rlim->rlim_cur = current->rlim[resource].rlim_cur; + rlim->rlim_max = current->rlim[resource].rlim_max; + return 0; +} diff --git a/kernel/syscalls/getrusage.c b/kernel/syscalls/getrusage.c new file mode 100644 index 00000000..0dc04eec --- /dev/null +++ b/kernel/syscalls/getrusage.c @@ -0,0 +1,40 @@ +/* + * fiwix/kernel/syscalls/getrusage.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_getrusage(int who, struct rusage *usage) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_getrusage(%d, 0x%08x)\n", current->pid, who, (unsigned int)usage); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_WRITE, usage, sizeof(struct rusage)))) { + return errno; + } + switch(who) { + case RUSAGE_SELF: + memcpy_b(usage, ¤t->usage, sizeof(struct rusage)); + break; + case RUSAGE_CHILDREN: + memcpy_b(usage, ¤t->cusage, sizeof(struct rusage)); + break; + default: + return -EINVAL; + } + return 0; +} diff --git a/kernel/syscalls/getsid.c b/kernel/syscalls/getsid.c new file mode 100644 index 00000000..18647f08 --- /dev/null +++ b/kernel/syscalls/getsid.c @@ -0,0 +1,38 @@ +/* + * fiwix/kernel/syscalls/getsid.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_getsid(__pid_t pid) +{ + struct proc *p; + +#ifdef __DEBUG__ + printk("(pid %d) sys_getsid(%d)\n", current->pid, pid); +#endif /*__DEBUG__ */ + + if(pid < 0) { + return -EINVAL; + } + if(!pid) { + return current->sid; + } + + FOR_EACH_PROCESS(p) { + if(p->state != PROC_UNUSED && p->pid == pid) { + return p->sid; + } + } + return -ESRCH; +} diff --git a/kernel/syscalls/gettimeofday.c b/kernel/syscalls/gettimeofday.c new file mode 100644 index 00000000..baf03b09 --- /dev/null +++ b/kernel/syscalls/gettimeofday.c @@ -0,0 +1,41 @@ +/* + * fiwix/kernel/syscalls/gettimeofday.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_gettimeofday(struct timeval *tv, struct timezone *tz) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_gettimeofday()\n", current->pid); +#endif /*__DEBUG__ */ + + if(tv) { + if((errno = check_user_area(VERIFY_WRITE, tv, sizeof(struct timeval)))) { + return errno; + } + tv->tv_sec = CURRENT_TIME; + tv->tv_usec = ((kstat.ticks % HZ) * 1000000) / HZ; + } + if(tz) { + if((errno = check_user_area(VERIFY_WRITE, tz, sizeof(struct timezone)))) { + return errno; + } + tz->tz_minuteswest = kstat.tz_minuteswest; + tz->tz_dsttime = kstat.tz_dsttime; + } + return 0; +} diff --git a/kernel/syscalls/getuid.c b/kernel/syscalls/getuid.c new file mode 100644 index 00000000..98f53e5d --- /dev/null +++ b/kernel/syscalls/getuid.c @@ -0,0 +1,21 @@ +/* + * fiwix/kernel/syscalls/getuid.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_getuid(void) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_getuid() -> %d\n", current->pid, current->uid); +#endif /*__DEBUG__ */ + + return current->uid; +} diff --git a/kernel/syscalls/ioctl.c b/kernel/syscalls/ioctl.c new file mode 100644 index 00000000..bf2e4d42 --- /dev/null +++ b/kernel/syscalls/ioctl.c @@ -0,0 +1,41 @@ +/* + * fiwix/kernel/syscalls/ioctl.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_ioctl(unsigned int fd, int cmd, unsigned long int arg) +{ + int errno; + struct inode *i; + +#ifdef __DEBUG__ + printk("(pid %d) sys_ioctl(%d, 0x%x, 0x%08x) -> ", current->pid, fd, cmd, arg); +#endif /*__DEBUG__ */ + + CHECK_UFD(fd); + i = fd_table[current->fd[fd]].inode; + if(i->fsop && i->fsop->ioctl) { + errno = i->fsop->ioctl(i, cmd, arg); + +#ifdef __DEBUG__ + printk("%d\n", errno); +#endif /*__DEBUG__ */ + + return errno; + } + +#ifdef __DEBUG__ + printk("%d\n", -ENOTTY); +#endif /*__DEBUG__ */ + + return -ENOTTY; +} diff --git a/kernel/syscalls/ioperm.c b/kernel/syscalls/ioperm.c new file mode 100644 index 00000000..50d7e960 --- /dev/null +++ b/kernel/syscalls/ioperm.c @@ -0,0 +1,28 @@ +/* + * fiwix/kernel/syscalls/ioperm.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_ioperm(unsigned long int from, unsigned long int num, int turn_on) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_ioperm(0x%08x, 0x%08x, 0x%08x)\n", current->pid, from, num, turn_on); +#endif /*__DEBUG__ */ + + if(!IS_SUPERUSER) { + return -EPERM; + } + + /* FIXME: to be implemented */ + + return 0; +} diff --git a/kernel/syscalls/iopl.c b/kernel/syscalls/iopl.c new file mode 100644 index 00000000..490a95b6 --- /dev/null +++ b/kernel/syscalls/iopl.c @@ -0,0 +1,58 @@ +/* + * fiwix/kernel/syscalls/iopl.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +/* + * Chapter Input/Output of IA-32 Intel(R) Architecture Software Developer's + * Manual Volume 1 Basic Architecture, says the processor permits applications + * to access I/O ports in either of two ways: by using I/O address space or by + * using memory-mapped I/O. Linux 2.0 and Fiwix uses the first one. + * + * This system call sets the IOPL field in the EFLAGS register to the value of + * 'level' (which is pressumably zero), so the current process will have + * privileges to use any port, even if that port is beyond of the default size + * of the I/O bitmap in TSS (which is IO_BITMAP_SIZE = 32). Otherwise the + * processor checks the I/O permission bit map to determine if access to a + * specific I/O port is allowed. + * + * So, we leave it here as in Linux 2.0. That means, leaving to I/O bit map to + * control the ports up to 0x3FF, and the rest of ports will be controlled by + * using this system call. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_iopl(int level, int arg2, int arg3, int arg4, int arg5, struct sigcontext *sc) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_iopl(%d) -> ", current->pid, level); +#endif /*__DEBUG__ */ + if(level > USR_PL) { +#ifdef __DEBUG__ + printk("-EINVAL\n"); +#endif /*__DEBUG__ */ + return -EINVAL; + } + if(!IS_SUPERUSER) { +#ifdef __DEBUG__ + printk("-EPERM\n"); +#endif /*__DEBUG__ */ + return -EPERM; + } + + sc->eflags = (sc->eflags & 0xFFFFCFFF) | (level << EF_IOPL); +#ifdef __DEBUG__ + printk("0\n"); +#endif /*__DEBUG__ */ + return 0; +} diff --git a/kernel/syscalls/kill.c b/kernel/syscalls/kill.c new file mode 100644 index 00000000..9e53093e --- /dev/null +++ b/kernel/syscalls/kill.c @@ -0,0 +1,46 @@ +/* + * fiwix/kernel/syscalls/kill.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_kill(__pid_t pid, __sigset_t signum) +{ + int count; + struct proc *p; + +#ifdef __DEBUG__ + printk("(pid %d) sys_kill(%d, %d)\n", current->pid, pid, signum); +#endif /*__DEBUG__ */ + + if(signum < 1 || signum > NSIG) { + return -EINVAL; + } + if(pid == -1) { + count = 0; + FOR_EACH_PROCESS(p) { + if(p->pid > 1 && p != current) { + count++; + send_sig(p, signum); + } + } + return count ? 0 : -ESRCH; + } + if(!pid) { + return kill_pgrp(current->pgid, signum); + } + if(pid < 1) { + return kill_pgrp(-pid, signum); + } + + return kill_pid(pid, signum); +} diff --git a/kernel/syscalls/link.c b/kernel/syscalls/link.c new file mode 100644 index 00000000..99967d3f --- /dev/null +++ b/kernel/syscalls/link.c @@ -0,0 +1,113 @@ +/* + * fiwix/kernel/syscalls/link.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_link(const char *oldname, const char *newname) +{ + struct inode *i, *dir, *i_new, *dir_new; + char *tmp_oldname, *tmp_newname, *basename; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_link('%s', '%s')\n", current->pid, oldname, newname); +#endif /*__DEBUG__ */ + + if((errno = malloc_name(oldname, &tmp_oldname)) < 0) { + return errno; + } + if((errno = malloc_name(newname, &tmp_newname)) < 0) { + free_name(tmp_oldname); + return errno; + } + + if((errno = namei(tmp_oldname, &i, &dir, !FOLLOW_LINKS))) { + if(dir) { + iput(dir); + } + free_name(tmp_oldname); + free_name(tmp_newname); + return errno; + } + if(S_ISDIR(i->i_mode)) { + iput(i); + iput(dir); + free_name(tmp_oldname); + free_name(tmp_newname); + return -EPERM; + } + if(IS_RDONLY_FS(i)) { + iput(i); + iput(dir); + free_name(tmp_oldname); + free_name(tmp_newname); + return -EROFS; + } + if(i->i_nlink == LINK_MAX) { + iput(i); + iput(dir); + free_name(tmp_oldname); + free_name(tmp_newname); + return -EMLINK; + } + + basename = get_basename(tmp_newname); + if((errno = namei(tmp_newname, &i_new, &dir_new, !FOLLOW_LINKS))) { + if(!dir_new) { + iput(i); + iput(dir); + free_name(tmp_oldname); + free_name(tmp_newname); + return errno; + } + } + if(!errno) { + iput(i); + iput(dir); + iput(i_new); + iput(dir_new); + free_name(tmp_oldname); + free_name(tmp_newname); + return -EEXIST; + } + if(i->dev != dir_new->dev) { + iput(i); + iput(dir); + iput(dir_new); + free_name(tmp_oldname); + free_name(tmp_newname); + return -EXDEV; + } + if(check_permission(TO_EXEC | TO_WRITE, dir_new) < 0) { + iput(i); + iput(dir); + iput(dir_new); + free_name(tmp_oldname); + free_name(tmp_newname); + return -EACCES; + } + + if(dir_new->fsop && dir_new->fsop->link) { + errno = dir_new->fsop->link(i, dir_new, basename); + } else { + errno = -EPERM; + } + iput(i); + iput(dir); + iput(dir_new); + free_name(tmp_oldname); + free_name(tmp_newname); + return errno; +} diff --git a/kernel/syscalls/llseek.c b/kernel/syscalls/llseek.c new file mode 100644 index 00000000..6a9736ea --- /dev/null +++ b/kernel/syscalls/llseek.c @@ -0,0 +1,56 @@ +/* + * fiwix/kernel/syscalls/llseek.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_llseek(unsigned int ufd, unsigned long int offset_high, unsigned long int offset_low, __loff_t *result, unsigned int whence) +{ + struct inode *i; + __loff_t offset; + __loff_t new_offset; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_llseek(%d, %d, %d, %08x, %d)", current->pid, ufd, offset_high, offset_low, result, whence); +#endif /*__DEBUG__ */ + + CHECK_UFD(ufd); + if((errno = check_user_area(VERIFY_WRITE, result, sizeof(__loff_t)))) { + return errno; + } + i = fd_table[current->fd[ufd]].inode; + offset = (__loff_t) (((__loff_t)offset_high << 32) | offset_low); + switch(whence) { + case SEEK_SET: + new_offset = offset; + break; + case SEEK_CUR: + new_offset = fd_table[current->fd[ufd]].offset + offset; + break; + case SEEK_END: + new_offset = i->i_size + offset; + break; + default: + return -EINVAL; + } + fd_table[current->fd[ufd]].offset = new_offset; + +#ifdef __DEBUG__ + printk(" -> returning %d\n", new_offset); +#endif /*__DEBUG__ */ + + memcpy_b(result, &new_offset, sizeof(__loff_t)); + return 0; +} diff --git a/kernel/syscalls/lseek.c b/kernel/syscalls/lseek.c new file mode 100644 index 00000000..26280182 --- /dev/null +++ b/kernel/syscalls/lseek.c @@ -0,0 +1,58 @@ +/* + * fiwix/kernel/syscalls/lseek.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_lseek(unsigned int ufd, __off_t offset, unsigned int whence) +{ + struct inode *i; + __off_t new_offset; + +#ifdef __DEBUG__ + printk("(pid %d) sys_lseek(%d, %d, %d)", current->pid, ufd, offset, whence); +#endif /*__DEBUG__ */ + + CHECK_UFD(ufd); + + i = fd_table[current->fd[ufd]].inode; + switch(whence) { + case SEEK_SET: + new_offset = offset; + break; + case SEEK_CUR: + new_offset = fd_table[current->fd[ufd]].offset + offset; + break; + case SEEK_END: + new_offset = i->i_size + offset; + break; + default: + return -EINVAL; + } + if((int)new_offset < 0) { + return -EINVAL; + } + if(i->fsop && i->fsop->lseek) { + fd_table[current->fd[ufd]].offset = new_offset; + new_offset = i->fsop->lseek(i, new_offset); + } else { + return -EPERM; + } + +#ifdef __DEBUG__ + printk(" -> returning %d\n", new_offset); +#endif /*__DEBUG__ */ + + return new_offset; +} diff --git a/kernel/syscalls/lstat.c b/kernel/syscalls/lstat.c new file mode 100644 index 00000000..d1f9cc42 --- /dev/null +++ b/kernel/syscalls/lstat.c @@ -0,0 +1,51 @@ +/* + * fiwix/kernel/syscalls/lstat.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_lstat(const char *filename, struct old_stat *statbuf) +{ + struct inode *i; + char *tmp_name; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_lstat('%s', 0x%08x) -> returning structure\n", current->pid, filename, (unsigned int )statbuf); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_WRITE, statbuf, sizeof(struct old_stat)))) { + return errno; + } + if((errno = malloc_name(filename, &tmp_name)) < 0) { + return errno; + } + if((errno = namei(tmp_name, &i, NULL, !FOLLOW_LINKS))) { + free_name(tmp_name); + return errno; + } + statbuf->st_dev = i->dev; + statbuf->st_ino = i->inode; + statbuf->st_mode = i->i_mode; + statbuf->st_nlink = i->i_nlink; + statbuf->st_uid = i->i_uid; + statbuf->st_gid = i->i_gid; + statbuf->st_rdev = i->rdev; + statbuf->st_size = i->i_size; + statbuf->st_atime = i->i_atime; + statbuf->st_mtime = i->i_mtime; + statbuf->st_ctime = i->i_ctime; + iput(i); + free_name(tmp_name); + return 0; +} diff --git a/kernel/syscalls/mkdir.c b/kernel/syscalls/mkdir.c new file mode 100644 index 00000000..dd98a9f0 --- /dev/null +++ b/kernel/syscalls/mkdir.c @@ -0,0 +1,66 @@ +/* + * fiwix/kernel/syscalls/mkdir.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_mkdir(const char *dirname, __mode_t mode) +{ + struct inode *i, *dir; + char *tmp_dirname, *basename; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_mkdir('%s', %o)\n", current->pid, dirname, mode); +#endif /*__DEBUG__ */ + + if((errno = malloc_name(dirname, &tmp_dirname)) < 0) { + return errno; + } + basename = remove_trailing_slash(tmp_dirname); + if((errno = namei(basename, &i, &dir, !FOLLOW_LINKS))) { + if(!dir) { + free_name(tmp_dirname); + return errno; + } + } + if(!errno) { + iput(i); + iput(dir); + free_name(tmp_dirname); + return -EEXIST; + } + if(IS_RDONLY_FS(dir)) { + iput(dir); + free_name(tmp_dirname); + return -EROFS; + } + + if(check_permission(TO_EXEC | TO_WRITE, dir) < 0) { + iput(dir); + free_name(tmp_dirname); + return -EACCES; + } + + basename = get_basename(basename); + if(dir->fsop && dir->fsop->mkdir) { + errno = dir->fsop->mkdir(dir, basename, mode); + } else { + errno = -EPERM; + } + iput(dir); + free_name(tmp_dirname); + return errno; +} diff --git a/kernel/syscalls/mknod.c b/kernel/syscalls/mknod.c new file mode 100644 index 00000000..d837921d --- /dev/null +++ b/kernel/syscalls/mknod.c @@ -0,0 +1,71 @@ +/* + * fiwix/kernel/syscalls/mknod.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_mknod(const char *pathname, __mode_t mode, __dev_t dev) +{ + struct inode *i, *dir; + char *tmp_name, *basename; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_mknod('%s', %d, %x)\n", current->pid, pathname, mode, dev); +#endif /*__DEBUG__ */ + + if(!S_ISCHR(mode) && !S_ISBLK(mode) && !S_ISFIFO(mode)) { + return -EINVAL; + } + if(!S_ISFIFO(mode) && !IS_SUPERUSER) { + return -EPERM; + } + + if((errno = malloc_name(pathname, &tmp_name)) < 0) { + return errno; + } + basename = get_basename(tmp_name); + if((errno = namei(tmp_name, &i, &dir, !FOLLOW_LINKS))) { + if(!dir) { + free_name(tmp_name); + return errno; + } + } + if(!errno) { + iput(i); + iput(dir); + free_name(tmp_name); + return -EEXIST; + } + if(IS_RDONLY_FS(dir)) { + iput(dir); + free_name(tmp_name); + return -EROFS; + } + if(check_permission(TO_EXEC | TO_WRITE, dir) < 0) { + iput(dir); + free_name(tmp_name); + return -EACCES; + } + + if(dir->fsop && dir->fsop->mknod) { + errno = dir->fsop->mknod(dir, basename, mode, dev); + } else { + errno = -EPERM; + } + iput(dir); + free_name(tmp_name); + return errno; +} diff --git a/kernel/syscalls/mount.c b/kernel/syscalls/mount.c new file mode 100644 index 00000000..51172cc9 --- /dev/null +++ b/kernel/syscalls/mount.c @@ -0,0 +1,221 @@ +/* + * fiwix/kernel/syscalls/mount.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_mount(const char *source, const char *target, const char *fstype, unsigned long int flags, const void *data) +{ + struct inode *i_source, *i_target; + struct mount *mt; + struct filesystems *fs; + struct vma *vma; + char *tmp_source, *tmp_target, *tmp_fstype; + __dev_t dev; + int len, errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_mount(%s, %s, %s, 0x%08x, 0x%08x\n", current->pid, source, target, (int)fstype ? fstype : "", flags, data); +#endif /* __DEBUG__ */ + + if(!IS_SUPERUSER) { + return -EPERM; + } + + if((errno = malloc_name(target, &tmp_target)) < 0) { + return errno; + } + if((errno = namei(tmp_target, &i_target, NULL, FOLLOW_LINKS))) { + free_name(tmp_target); + return errno; + } + if(!S_ISDIR(i_target->i_mode)) { + iput(i_target); + free_name(tmp_target); + return -ENOTDIR; + } + if((flags & MS_MGC_VAL) == MS_MGC_VAL) { + flags &= ~MS_MGC_MSK; + } + + if(flags & MS_REMOUNT) { + if(!(mt = get_mount_point(i_target))) { + iput(i_target); + free_name(tmp_target); + return -EINVAL; + } + fs = mt->fs; + if(fs->fsop && fs->fsop->remount_fs) { + if((errno = fs->fsop->remount_fs(&mt->sb, flags))) { + iput(i_target); + free_name(tmp_target); + return errno; + } + } else { + iput(i_target); + free_name(tmp_target); + return -EINVAL; + } + + /* switching from RW to RO */ + if(flags & MS_RDONLY && !(mt->sb.flags & MS_RDONLY)) { + dev = mt->dev; + /* + * FIXME: if there are files opened in RW mode then + * we can't continue and must return -EBUSY. + */ + if(fs->fsop && fs->fsop->release_superblock) { + fs->fsop->release_superblock(&mt->sb); + } + sync_superblocks(dev); + sync_inodes(dev); + sync_buffers(dev); + } + + mt->sb.flags &= ~MS_RDONLY; + mt->sb.flags |= (flags & MS_RDONLY); + iput(i_target); + free_name(tmp_target); + return 0; + } + + if(i_target->mount_point) { + iput(i_target); + free_name(tmp_target); + return -EBUSY; + } + + /* check the validity of fstype */ + if(!(vma = find_vma_region((unsigned int)fstype))) { + iput(i_target); + free_name(tmp_target); + return -EFAULT; + } + if(!(vma->prot & PROT_READ)) { + iput(i_target); + free_name(tmp_target); + return -EFAULT; + } + len = MIN(vma->end - (unsigned int)fstype, PAGE_SIZE - 1); + if(!(tmp_fstype = (char *)kmalloc())) { + iput(i_target); + free_name(tmp_target); + return -ENOMEM; + } + memcpy_b(tmp_fstype, fstype, len); + + if(!(fs = get_filesystem(fstype))) { + iput(i_target); + free_name(tmp_target); + free_name(tmp_fstype); + return -ENODEV; + } + dev = fs->fsop->fsdev; + + if((errno = malloc_name(source, &tmp_source)) < 0) { + iput(i_target); + free_name(tmp_target); + free_name(tmp_fstype); + return errno; + } + if(fs->fsop->flags == FSOP_REQUIRES_DEV) { + if((errno = namei(tmp_source, &i_source, NULL, FOLLOW_LINKS))) { + iput(i_target); + free_name(tmp_target); + free_name(tmp_fstype); + free_name(tmp_source); + return errno; + } + if(!S_ISBLK(i_source->i_mode)) { + iput(i_target); + iput(i_source); + free_name(tmp_target); + free_name(tmp_fstype); + free_name(tmp_source); + return -ENOTBLK; + } + if(i_source->fsop && i_source->fsop->open) { + if((errno = i_source->fsop->open(i_source, NULL))) { + iput(i_target); + iput(i_source); + free_name(tmp_target); + free_name(tmp_fstype); + free_name(tmp_source); + return errno; + } + } else { + iput(i_target); + iput(i_source); + free_name(tmp_target); + free_name(tmp_fstype); + free_name(tmp_source); + return -EINVAL; + } + dev = i_source->rdev; + } + + if(!(mt = get_free_mount_point(dev))) { + if(fs->fsop->flags == FSOP_REQUIRES_DEV) { + i_source->fsop->close(i_source, NULL); + iput(i_source); + } + iput(i_target); + free_name(tmp_target); + free_name(tmp_fstype); + free_name(tmp_source); + return -EBUSY; + } + + mt->sb.flags = flags; + if(fs->fsop && fs->fsop->read_superblock) { + if((errno = fs->fsop->read_superblock(dev, &mt->sb))) { + i_source->fsop->close(i_source, NULL); + if(fs->fsop->flags == FSOP_REQUIRES_DEV) { + iput(i_source); + } + iput(i_target); + release_mount_point(mt); + free_name(tmp_target); + free_name(tmp_fstype); + free_name(tmp_source); + return errno; + } + } else { + if(fs->fsop->flags == FSOP_REQUIRES_DEV) { + iput(i_source); + } + iput(i_target); + release_mount_point(mt); + free_name(tmp_target); + free_name(tmp_fstype); + free_name(tmp_source); + return -EINVAL; + } + + mt->dev = dev; + strncpy(mt->devname, tmp_source, DEVNAME_MAX); + strcpy(mt->dirname, tmp_target); + mt->sb.dir = i_target; + mt->fs = fs; + i_target->mount_point = mt->sb.root; + free_name(tmp_target); + free_name(tmp_fstype); + free_name(tmp_source); + return 0; +} diff --git a/kernel/syscalls/mprotect.c b/kernel/syscalls/mprotect.c new file mode 100644 index 00000000..847d2540 --- /dev/null +++ b/kernel/syscalls/mprotect.c @@ -0,0 +1,49 @@ +/* + * fiwix/kernel/syscalls/mprotect.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_mprotect(unsigned int addr, __size_t length, int prot) +{ + struct vma *vma; + +#ifdef __DEBUG__ + printk("(pid %d) sys_mprotect(0x%08x, %d, %d)\n", current->pid, addr, length, prot); +#endif /*__DEBUG__ */ + + if((addr & ~PAGE_MASK) || length < 0) { + return -EINVAL; + } + if(prot > (PROT_READ | PROT_WRITE | PROT_EXEC)) { + return -EINVAL; + } + if(!(vma = find_vma_region(addr))) { + return -ENOMEM; + } + length = PAGE_ALIGN(length); + if((addr + length) > vma->end) { + return -ENOMEM; + } + if(vma->inode && (vma->flags & MAP_SHARED)) { + if(prot & PROT_WRITE) { + if(!(vma->o_mode & (O_WRONLY | O_RDWR))) { + return -EACCES; + } + } + } + + return do_mprotect(vma, addr, length, prot); +} diff --git a/kernel/syscalls/munmap.c b/kernel/syscalls/munmap.c new file mode 100644 index 00000000..145e2bf6 --- /dev/null +++ b/kernel/syscalls/munmap.c @@ -0,0 +1,22 @@ +/* + * fiwix/kernel/syscalls/munmap.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_munmap(unsigned int addr, __size_t length) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_munmap(0x%08x, %d)\n", current->pid, addr, length); +#endif /*__DEBUG__ */ + return do_munmap(addr, length); +} diff --git a/kernel/syscalls/nanosleep.c b/kernel/syscalls/nanosleep.c new file mode 100644 index 00000000..539ae89a --- /dev/null +++ b/kernel/syscalls/nanosleep.c @@ -0,0 +1,50 @@ +/* + * fiwix/kernel/syscalls/nanosleep.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_nanosleep(const struct timespec *req, struct timespec *rem) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_nanosleep(0x%08x, 0x%08x)\n", current->pid, (unsigned int)req, (unsigned int)rem); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_READ, req, sizeof(struct timespec)))) { + return errno; + } + if(req->tv_sec < 0 || req->tv_nsec >= 1000000000L || req->tv_nsec < 0) { + return -EINVAL; + } + + current->timeout = (req->tv_sec * HZ) + (req->tv_nsec * HZ / 1000000000L); + if(current->timeout) { + sleep(&sys_nanosleep, PROC_INTERRUPTIBLE); + if(current->timeout) { + if(rem) { + if((errno = check_user_area(VERIFY_WRITE, rem, sizeof(struct timespec)))) { + return errno; + } + rem->tv_sec = current->timeout / HZ; + rem->tv_nsec = (current->timeout % HZ) * 1000000000L / HZ; + } + return -EINTR; + } + } + return 0; +} diff --git a/kernel/syscalls/newfstat.c b/kernel/syscalls/newfstat.c new file mode 100644 index 00000000..a13ffbda --- /dev/null +++ b/kernel/syscalls/newfstat.c @@ -0,0 +1,56 @@ +/* + * fiwix/kernel/syscalls/newfstat.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_newfstat(unsigned int ufd, struct new_stat *statbuf) +{ + struct inode *i; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_newfstat(%d, 0x%08x) -> returning structure\n", current->pid, ufd, (unsigned int )statbuf); +#endif /*__DEBUG__ */ + + CHECK_UFD(ufd); + if((errno = check_user_area(VERIFY_WRITE, statbuf, sizeof(struct new_stat)))) { + return errno; + } + i = fd_table[current->fd[ufd]].inode; + statbuf->st_dev = i->dev; + statbuf->__pad1 = 0; + statbuf->st_ino = i->inode; + statbuf->st_mode = i->i_mode; + statbuf->st_nlink = i->i_nlink; + statbuf->st_uid = i->i_uid; + statbuf->st_gid = i->i_gid; + statbuf->st_rdev = i->rdev; + statbuf->__pad2 = 0; + statbuf->st_size = i->i_size; + statbuf->st_blksize = i->sb->s_blocksize; + statbuf->st_blocks = i->i_blocks; + if(!i->i_blocks) { + statbuf->st_blocks = (i->i_size / i->sb->s_blocksize * 2); + statbuf->st_blocks++; + } + statbuf->st_atime = i->i_atime; + statbuf->__unused1 = 0; + statbuf->st_mtime = i->i_mtime; + statbuf->__unused2 = 0; + statbuf->st_ctime = i->i_ctime; + statbuf->__unused3 = 0; + statbuf->__unused4 = 0; + statbuf->__unused5 = 0; + return 0; +} diff --git a/kernel/syscalls/newlstat.c b/kernel/syscalls/newlstat.c new file mode 100644 index 00000000..94165387 --- /dev/null +++ b/kernel/syscalls/newlstat.c @@ -0,0 +1,64 @@ +/* + * fiwix/kernel/syscalls/newlstat.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_newlstat(const char *filename, struct new_stat *statbuf) +{ + struct inode *i; + char *tmp_name; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_newlstat('%s', 0x%08x) -> returning structure\n", current->pid, filename, (unsigned int )statbuf); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_WRITE, statbuf, sizeof(struct new_stat)))) { + return errno; + } + if((errno = malloc_name(filename, &tmp_name)) < 0) { + return errno; + } + if((errno = namei(tmp_name, &i, NULL, !FOLLOW_LINKS))) { + free_name(tmp_name); + return errno; + } + statbuf->st_dev = i->dev; + statbuf->__pad1 = 0; + statbuf->st_ino = i->inode; + statbuf->st_mode = i->i_mode; + statbuf->st_nlink = i->i_nlink; + statbuf->st_uid = i->i_uid; + statbuf->st_gid = i->i_gid; + statbuf->st_rdev = i->rdev; + statbuf->__pad2 = 0; + statbuf->st_size = i->i_size; + statbuf->st_blksize = i->sb->s_blocksize; + statbuf->st_blocks = i->i_blocks; + if(!i->i_blocks) { + statbuf->st_blocks = (i->i_size / i->sb->s_blocksize) * 2; + statbuf->st_blocks++; + } + statbuf->st_atime = i->i_atime; + statbuf->__unused1 = 0; + statbuf->st_mtime = i->i_mtime; + statbuf->__unused2 = 0; + statbuf->st_ctime = i->i_ctime; + statbuf->__unused3 = 0; + statbuf->__unused4 = 0; + statbuf->__unused5 = 0; + iput(i); + free_name(tmp_name); + return 0; +} diff --git a/kernel/syscalls/newstat.c b/kernel/syscalls/newstat.c new file mode 100644 index 00000000..0f484809 --- /dev/null +++ b/kernel/syscalls/newstat.c @@ -0,0 +1,64 @@ +/* + * fiwix/kernel/syscalls/newstat.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_newstat(const char *filename, struct new_stat *statbuf) +{ + struct inode *i; + char *tmp_name; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_newstat('%s', 0x%08x) -> returning structure\n", current->pid, filename, (unsigned int )statbuf); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_WRITE, statbuf, sizeof(struct new_stat)))) { + return errno; + } + if((errno = malloc_name(filename, &tmp_name)) < 0) { + return errno; + } + if((errno = namei(tmp_name, &i, NULL, FOLLOW_LINKS))) { + free_name(tmp_name); + return errno; + } + statbuf->st_dev = i->dev; + statbuf->__pad1 = 0; + statbuf->st_ino = i->inode; + statbuf->st_mode = i->i_mode; + statbuf->st_nlink = i->i_nlink; + statbuf->st_uid = i->i_uid; + statbuf->st_gid = i->i_gid; + statbuf->st_rdev = i->rdev; + statbuf->__pad2 = 0; + statbuf->st_size = i->i_size; + statbuf->st_blksize = i->sb->s_blocksize; + statbuf->st_blocks = i->i_blocks; + if(!i->i_blocks) { + statbuf->st_blocks = (i->i_size / i->sb->s_blocksize) * 2; + statbuf->st_blocks++; + } + statbuf->st_atime = i->i_atime; + statbuf->__unused1 = 0; + statbuf->st_mtime = i->i_mtime; + statbuf->__unused2 = 0; + statbuf->st_ctime = i->i_ctime; + statbuf->__unused3 = 0; + statbuf->__unused4 = 0; + statbuf->__unused5 = 0; + iput(i); + free_name(tmp_name); + return 0; +} diff --git a/kernel/syscalls/newuname.c b/kernel/syscalls/newuname.c new file mode 100644 index 00000000..d8cc4cc3 --- /dev/null +++ b/kernel/syscalls/newuname.c @@ -0,0 +1,34 @@ +/* + * fiwix/kernel/syscalls/newuname.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_newuname(struct new_utsname *uname) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_newuname(0x%08x)\n", current->pid, (int)uname); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_WRITE, uname, sizeof(struct new_utsname)))) { + return errno; + } + if(!uname) { + return -EFAULT; + } + memcpy_b(uname, &sys_utsname, sizeof(struct new_utsname)); + return 0; +} diff --git a/kernel/syscalls/old_mmap.c b/kernel/syscalls/old_mmap.c new file mode 100644 index 00000000..2ea2d557 --- /dev/null +++ b/kernel/syscalls/old_mmap.c @@ -0,0 +1,52 @@ +/* + * fiwix/kernel/syscalls/old_mmap.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int old_mmap(struct mmap *mmap) +{ + unsigned int page; + struct inode *i; + char flags; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) old_mmap(0x%08x, %d, 0x%02x, 0x%02x, %d, 0x%08x) -> ", current->pid, mmap->start, mmap->length, mmap->prot, mmap->flags, mmap->fd, mmap->offset); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_READ, mmap, sizeof(struct mmap)))) { + return errno; + } + if(!mmap->length) { + return -EINVAL; + } + + i = NULL; + flags = 0; + if(!(mmap->flags & MAP_ANONYMOUS)) { + CHECK_UFD(mmap->fd); + if(!(i = fd_table[current->fd[mmap->fd]].inode)) { + return -EBADF; + } + flags = fd_table[current->fd[mmap->fd]].flags & O_ACCMODE; + } + page = do_mmap(i, mmap->start, mmap->length, mmap->prot, mmap->flags, mmap->offset, P_MMAP, flags); +#ifdef __DEBUG__ + printk("0x%08x\n", page); +#endif /*__DEBUG__ */ + return page; +} diff --git a/kernel/syscalls/old_select.c b/kernel/syscalls/old_select.c new file mode 100644 index 00000000..ebc05cd3 --- /dev/null +++ b/kernel/syscalls/old_select.c @@ -0,0 +1,42 @@ +/* + * fiwix/kernel/syscalls/old_select.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int old_select(unsigned long int *params) +{ + int nfds; + fd_set *readfds; + fd_set *writefds; + fd_set *exceptfds; + struct timeval *timeout; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) old_select(0x%08x)\n", current->pid, (int)params); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_READ, (void *)params, sizeof(unsigned int) * 5))) { + return errno; + } + nfds = *(int *)params; + readfds = *(fd_set **)(params + 1); + writefds = *(fd_set **)(params + 2); + exceptfds = *(fd_set **)(params + 3); + timeout = *(struct timeval **)(params + 4); + + return sys_select(nfds, readfds, writefds, exceptfds, timeout); +} diff --git a/kernel/syscalls/olduname.c b/kernel/syscalls/olduname.c new file mode 100644 index 00000000..e1beadda --- /dev/null +++ b/kernel/syscalls/olduname.c @@ -0,0 +1,39 @@ +/* + * fiwix/kernel/syscalls/olduname.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_olduname(struct oldold_utsname *uname) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_olduname(0x%0x)", current->pid, uname); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_WRITE, uname, sizeof(struct oldold_utsname)))) { + return errno; + } + memcpy_b(&uname->sysname, &sys_utsname.sysname, _OLD_UTSNAME_LENGTH); + memset_b(&uname->sysname + _OLD_UTSNAME_LENGTH, NULL, 1); + memcpy_b(&uname->nodename, &sys_utsname.nodename, _OLD_UTSNAME_LENGTH); + memset_b(&uname->nodename + _OLD_UTSNAME_LENGTH, NULL, 1); + memcpy_b(&uname->release, &sys_utsname.release, _OLD_UTSNAME_LENGTH); + memset_b(&uname->release + _OLD_UTSNAME_LENGTH, NULL, 1); + memcpy_b(&uname->version, &sys_utsname.version, _OLD_UTSNAME_LENGTH); + memset_b(&uname->version + _OLD_UTSNAME_LENGTH, NULL, 1); + memcpy_b(&uname->machine, &sys_utsname.machine, _OLD_UTSNAME_LENGTH); + memset_b(&uname->machine + _OLD_UTSNAME_LENGTH, NULL, 1); + return 0; +} diff --git a/kernel/syscalls/open.c b/kernel/syscalls/open.c new file mode 100644 index 00000000..6a0c8067 --- /dev/null +++ b/kernel/syscalls/open.c @@ -0,0 +1,143 @@ +/* + * fiwix/kernel/syscalls/open.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include + +int sys_open(const char *filename, int flags, __mode_t mode) +{ + int fd, ufd; + struct inode *i, *dir; + char *tmp_name, *basename; + int errno, follow_links, perms; + +#ifdef __DEBUG__ + printk("(pid %d) sys_open('%s', %o, %o)\n", current->pid, filename, flags, mode); +#endif /*__DEBUG__ */ + + if((errno = malloc_name(filename, &tmp_name)) < 0) { + return errno; + } + + basename = get_basename(tmp_name); + follow_links = flags & O_NOFOLLOW ? !FOLLOW_LINKS : FOLLOW_LINKS; + if((errno = namei(tmp_name, &i, &dir, follow_links))) { + if(!dir) { + free_name(tmp_name); + if(flags & O_CREAT) { + return -ENOENT; + } + return errno; + } + } + +#ifdef __DEBUG__ + printk("\t(inode = %d)\n", i ? i->inode : -1); +#endif /*__DEBUG__ */ + + if(flags & O_CREAT) { + if(!errno && (flags & O_EXCL)) { + iput(i); + iput(dir); + free_name(tmp_name); + return -EEXIST; + } + if(check_permission(TO_EXEC | TO_WRITE, dir) < 0) { + iput(i); + iput(dir); + free_name(tmp_name); + return -EACCES; + } + if(errno) { /* assumes -ENOENT */ + if(dir->fsop && dir->fsop->create) { + errno = dir->fsop->create(dir, basename, mode, &i); + if(errno) { + iput(dir); + free_name(tmp_name); + return errno; + } + } else { + iput(dir); + free_name(tmp_name); + return -EACCES; + } + } + } else { + if(errno) { + iput(dir); + free_name(tmp_name); + return errno; + } + if(S_ISDIR(i->i_mode) && (flags & (O_RDWR | O_WRONLY | O_TRUNC))) { + iput(i); + iput(dir); + free_name(tmp_name); + return -EISDIR; + } + mode = 0; + } + + if((flags & O_ACCMODE) == O_RDONLY) { + perms = TO_READ; + } else if((flags & O_ACCMODE) == O_WRONLY) { + perms = TO_WRITE; + } else { + perms = TO_READ | TO_WRITE; + } + if((errno = check_permission(perms, i))) { + iput(i); + iput(dir); + free_name(tmp_name); + return errno; + } + if((fd = get_new_fd(i)) < 0) { + iput(i); + iput(dir); + free_name(tmp_name); + return fd; + } + if((ufd = get_new_user_fd(0)) < 0) { + release_fd(fd); + iput(i); + iput(dir); + free_name(tmp_name); + return ufd; + } + +#ifdef __DEBUG__ + printk("\t(ufd = %d)\n", ufd); +#endif /*__DEBUG__ */ + + fd_table[fd].flags = flags; + current->fd[ufd] = fd; + if(i->fsop && i->fsop->open) { + if((errno = i->fsop->open(i, &fd_table[fd])) < 0) { + release_fd(fd); + release_user_fd(ufd); + iput(i); + iput(dir); + free_name(tmp_name); + return errno; + } + iput(dir); + free_name(tmp_name); + return ufd; + } + + printk("WARNING: %s(): file '%s' (inode %d) without the open() method!\n", __FUNCTION__, tmp_name, i->inode); + release_fd(fd); + release_user_fd(ufd); + iput(i); + iput(dir); + free_name(tmp_name); + return -EINVAL; +} diff --git a/kernel/syscalls/pause.c b/kernel/syscalls/pause.c new file mode 100644 index 00000000..4aa536ad --- /dev/null +++ b/kernel/syscalls/pause.c @@ -0,0 +1,30 @@ +/* + * fiwix/kernel/syscalls/pause.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_pause(void) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_pause()\n", current->pid); +#endif /*__DEBUG__ */ + + for(;;) { + if(sleep(&sys_pause, PROC_INTERRUPTIBLE)) { + break; + } + } + return -EINTR; +} diff --git a/kernel/syscalls/personality.c b/kernel/syscalls/personality.c new file mode 100644 index 00000000..f0a1723c --- /dev/null +++ b/kernel/syscalls/personality.c @@ -0,0 +1,19 @@ +/* + * fiwix/kernel/syscalls/personality.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_personality(unsigned long int persona) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_personality(%d) -> %d\n", current->pid, persona, persona); +#endif /*__DEBUG__ */ + return persona; +} diff --git a/kernel/syscalls/pipe.c b/kernel/syscalls/pipe.c new file mode 100644 index 00000000..04a6c76a --- /dev/null +++ b/kernel/syscalls/pipe.c @@ -0,0 +1,71 @@ +/* + * fiwix/kernel/syscalls/pipe.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +int sys_pipe(int pipefd[2]) +{ + int rfd, rufd; + int wfd, wufd; + struct filesystems *fs; + struct inode *i; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_pipe()", current->pid); +#endif /*__DEBUG__ */ + + if(!(fs = get_filesystem("pipefs"))) { + printk("WARNING: %s(): pipefs filesystem is not registered!\n", __FUNCTION__); + return -EINVAL; + } + if((errno = check_user_area(VERIFY_WRITE, pipefd, sizeof(int) * 2))) { + return errno; + } + if(!(i = ialloc(&fs->mt->sb))) { + return -EINVAL; + } + if((rfd = get_new_fd(i)) < 0) { + iput(i); + return -ENFILE; + } + if((wfd = get_new_fd(i)) < 0) { + release_fd(rfd); + iput(i); + return -ENFILE; + } + if((rufd = get_new_user_fd(0)) < 0) { + release_fd(rfd); + release_fd(wfd); + iput(i); + return -EMFILE; + } + if((wufd = get_new_user_fd(0)) < 0) { + release_fd(rfd); + release_fd(wfd); + release_user_fd(rufd); + iput(i); + return -EMFILE; + } + + pipefd[0] = rufd; + pipefd[1] = wufd; + current->fd[rufd] = rfd; + current->fd[wufd] = wfd; + fd_table[rfd].flags = O_RDONLY; + fd_table[wfd].flags = O_WRONLY; + +#ifdef __DEBUG__ + printk(" -> inode=%d, rufd=%d wufd=%d (rfd=%d wfd=%d)\n", i->inode, rufd, wufd, rfd, wfd); +#endif /*__DEBUG__ */ + + return 0; +} diff --git a/kernel/syscalls/read.c b/kernel/syscalls/read.c new file mode 100644 index 00000000..8f94aedc --- /dev/null +++ b/kernel/syscalls/read.c @@ -0,0 +1,49 @@ +/* + * fiwix/kernel/syscalls/read.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_read(unsigned int ufd, char *buf, int count) +{ + struct inode *i; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_read(%d, 0x%08x, %d) -> ", current->pid, ufd, buf, count); +#endif /*__DEBUG__ */ + + CHECK_UFD(ufd); + if((errno = check_user_area(VERIFY_WRITE, buf, count))) { + return errno; + } + if(fd_table[current->fd[ufd]].flags & O_WRONLY) { + return -EBADF; + } + if(!count) { + return 0; + } + if(count < 0) { + return -EINVAL; + } + + i = fd_table[current->fd[ufd]].inode; + if(i->fsop && i->fsop->read) { + errno = i->fsop->read(i, &fd_table[current->fd[ufd]], buf, count); +#ifdef __DEBUG__ + printk("%d\n", errno); +#endif /*__DEBUG__ */ + return errno; + } + return -EINVAL; +} diff --git a/kernel/syscalls/readlink.c b/kernel/syscalls/readlink.c new file mode 100644 index 00000000..2baf4af8 --- /dev/null +++ b/kernel/syscalls/readlink.c @@ -0,0 +1,57 @@ +/* + * fiwix/kernel/syscalls/readlink.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_readlink(const char *filename, char *buffer, __size_t bufsize) +{ + struct inode *i; + char *tmp_name; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_readlink(%s, 0x%08x, %d)\n", current->pid, filename, (unsigned int)buffer, bufsize); +#endif /*__DEBUG__ */ + + if(bufsize <= 0) { + return -EINVAL; + } + if((errno = check_user_area(VERIFY_WRITE, buffer, bufsize))) { + return errno; + } + if((errno = malloc_name(filename, &tmp_name)) < 0) { + return errno; + } + if((errno = namei(tmp_name, &i, NULL, !FOLLOW_LINKS))) { + free_name(tmp_name); + return errno; + } + if(!S_ISLNK(i->i_mode)) { + iput(i); + free_name(tmp_name); + return -EINVAL; + } + + if(i->fsop && i->fsop->readlink) { + errno = i->fsop->readlink(i, buffer, bufsize); + iput(i); + free_name(tmp_name); + return errno; + } + iput(i); + free_name(tmp_name); + return -EINVAL; +} diff --git a/kernel/syscalls/reboot.c b/kernel/syscalls/reboot.c new file mode 100644 index 00000000..f8e91f6d --- /dev/null +++ b/kernel/syscalls/reboot.c @@ -0,0 +1,49 @@ +/* + * fiwix/kernel/syscalls/reboot.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_reboot(int magic1, int magic2, int flag) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_reboot(0x%08x, %d, 0x%08x)\n", current->pid, magic1, magic2, flag); +#endif /*__DEBUG__ */ + + if(!IS_SUPERUSER) { + return -EPERM; + } + if((magic1 != BMAGIC_1) || (magic2 != BMAGIC_2)) { + return -EINVAL; + } + switch(flag) { + case BMAGIC_SOFT: + ctrl_alt_del = 0; + break; + case BMAGIC_HARD: + ctrl_alt_del = 1; + break; + case BMAGIC_REBOOT: + reboot(); + break; + case BMAGIC_HALT: + sys_kill(-1, SIGKILL); + stop_kernel(); + break; + default: + return -EINVAL; + } + return 0; +} diff --git a/kernel/syscalls/rename.c b/kernel/syscalls/rename.c new file mode 100644 index 00000000..92ae697b --- /dev/null +++ b/kernel/syscalls/rename.c @@ -0,0 +1,117 @@ +/* + * fiwix/kernel/syscalls/rename.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_rename(const char *oldpath, const char *newpath) +{ + struct inode *i, *dir, *i_new, *dir_new; + char *tmp_oldpath, *tmp_newpath; + char *oldbasename, *newbasename; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_rename('%s', '%s')\n", current->pid, oldpath, newpath); +#endif /*__DEBUG__ */ + + if((errno = malloc_name(oldpath, &tmp_oldpath)) < 0) { + return errno; + } + if((errno = namei(tmp_oldpath, &i, &dir, !FOLLOW_LINKS))) { + if(dir) { + iput(dir); + } + free_name(tmp_oldpath); + return errno; + } + if(IS_RDONLY_FS(i)) { + iput(i); + iput(dir); + free_name(tmp_oldpath); + return -EROFS; + } + + if((errno = malloc_name(newpath, &tmp_newpath)) < 0) { + iput(i); + iput(dir); + free_name(tmp_oldpath); + return errno; + } + newbasename = remove_trailing_slash(tmp_newpath); + if((errno = namei(newbasename, &i_new, &dir_new, !FOLLOW_LINKS))) { + if(!dir_new) { + iput(i); + iput(dir); + free_name(tmp_oldpath); + free_name(tmp_newpath); + return errno; + } + } + if(dir->dev != dir_new->dev) { + errno = -EXDEV; + goto end; + } + newbasename = get_basename(newbasename); + if((newbasename[0] == '.' && newbasename[1] == NULL) || (newbasename[0] == '.' && newbasename[1] == '.' && newbasename[2] == NULL)) { + errno = -EINVAL; + goto end; + } + + oldbasename = get_basename(tmp_oldpath); + oldbasename = remove_trailing_slash(oldbasename); + if((oldbasename[0] == '.' && oldbasename[1] == NULL) || (oldbasename[0] == '.' && oldbasename[1] == '.' && oldbasename[2] == NULL)) { + errno = -EINVAL; + goto end; + } + + if(i_new) { + if(S_ISREG(i->i_mode)) { + if(S_ISDIR(i_new->i_mode)) { + errno = -EISDIR; + goto end; + } + } + if(S_ISDIR(i->i_mode)) { + if(!S_ISDIR(i_new->i_mode)) { + errno = -ENOTDIR; + goto end; + } + } + if(i->inode == i_new->inode) { + errno = 0; + goto end; + } + } + + if(check_permission(TO_EXEC | TO_WRITE, dir_new) < 0) { + errno = -EACCES; + goto end; + } + + if(dir_new->fsop && dir_new->fsop->rename) { + errno = dir_new->fsop->rename(i, dir, i_new, dir_new, oldbasename, newbasename); + } else { + errno = -EPERM; + } + +end: + iput(i); + iput(dir); + iput(i_new); + iput(dir_new); + free_name(tmp_oldpath); + free_name(tmp_newpath); + return errno; +} diff --git a/kernel/syscalls/rmdir.c b/kernel/syscalls/rmdir.c new file mode 100644 index 00000000..2cf19c70 --- /dev/null +++ b/kernel/syscalls/rmdir.c @@ -0,0 +1,87 @@ +/* + * fiwix/kernel/syscalls/rmdir.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_rmdir(const char *dirname) +{ + struct inode *i, *dir; + char *tmp_dirname; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_rmdir(%s)\n", current->pid, dirname); +#endif /*__DEBUG__ */ + + if((errno = malloc_name(dirname, &tmp_dirname)) < 0) { + return errno; + } + if((errno = namei(tmp_dirname, &i, &dir, !FOLLOW_LINKS))) { + if(dir) { + iput(dir); + } + free_name(tmp_dirname); + return errno; + } + if(!S_ISDIR(i->i_mode)) { + iput(i); + iput(dir); + free_name(tmp_dirname); + return -ENOTDIR; + } + if(i == current->root || i->mount_point || i->count > 1) { + iput(i); + iput(dir); + free_name(tmp_dirname); + return -EBUSY; + } + if(IS_RDONLY_FS(i)) { + iput(i); + iput(dir); + free_name(tmp_dirname); + return -EROFS; + } + if(i == dir) { + iput(i); + iput(dir); + free_name(tmp_dirname); + return -EPERM; + } + if(check_permission(TO_EXEC | TO_WRITE, dir) < 0) { + iput(i); + iput(dir); + free_name(tmp_dirname); + return -EACCES; + } + + /* check sticky permission bit */ + if(dir->i_mode & S_ISVTX) { + if(check_user_permission(i)) { + iput(i); + iput(dir); + free_name(tmp_dirname); + return -EPERM; + } + } + + if(i->fsop && i->fsop->rmdir) { + errno = i->fsop->rmdir(dir, i); + } else { + errno = -EPERM; + } + iput(i); + iput(dir); + free_name(tmp_dirname); + return errno; +} diff --git a/kernel/syscalls/select.c b/kernel/syscalls/select.c new file mode 100644 index 00000000..6e4e330c --- /dev/null +++ b/kernel/syscalls/select.c @@ -0,0 +1,166 @@ +/* + * fiwix/kernel/syscalls/select.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int check_fds(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds) +{ + int n, bit; + unsigned long int set; + + n = bit = 0; + while(bit < nfds) { + bit = n * __NFDBITS; + set = rfds->fds_bits[n] | wfds->fds_bits[n] | efds->fds_bits[n]; + while(set) { + if(__FD_ISSET(bit, rfds)) { + CHECK_UFD(bit); + } + set >>= 1; + bit++; + } + n++; + } + + return 0; +} + +static int do_check(struct inode *i, int flag) +{ + if(i->fsop && i->fsop->select) { + if(i->fsop->select(i, flag)) { + return 1; + } + } + + return 0; +} + +int do_select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, fd_set *res_rfds, fd_set *res_wfds, fd_set *res_efds) +{ + int n, count; + struct inode *i; + + count = 0; + for(;;) { + for(n = 0; n < nfds; n++) { + if(!current->fd[n]) { + continue; + } + i = fd_table[current->fd[n]].inode; + if(__FD_ISSET(n, rfds)) { + if(do_check(i, SEL_R)) { + __FD_SET(n, res_rfds); + count++; + } + } + if(__FD_ISSET(n, wfds)) { + if(do_check(i, SEL_W)) { + __FD_SET(n, res_wfds); + count++; + } + } + if(__FD_ISSET(n, efds)) { + if(do_check(i, SEL_E)) { + __FD_SET(n, res_efds); + count++; + } + } + } + + if(count || !current->timeout || current->sigpending & ~current->sigblocked) { + break; + } + sleep(&do_select, PROC_INTERRUPTIBLE); + } + + return count; +} + +int sys_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) +{ + unsigned long int t; + fd_set rfds, wfds, efds; + fd_set res_rfds, res_wfds, res_efds; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_select(%d, 0x%08x, 0x%08x, 0x%08x, 0x%08x [%d])\n", current->pid, nfds, (int)readfds, (int)writefds, (int)exceptfds, (int)timeout, (int)timeout ? tv2ticks(timeout): 0); +#endif /*__DEBUG__ */ + + if(nfds < 0) { + return -EINVAL; + } + if(nfds > MIN(__FD_SETSIZE, NR_OPENS)) { + nfds = MIN(__FD_SETSIZE, NR_OPENS); + } + + if(readfds) { + if((errno = check_user_area(VERIFY_WRITE, readfds, sizeof(fd_set)))) { + return errno; + } + memcpy_b(&rfds, readfds, sizeof(fd_set)); + } else { + __FD_ZERO(&rfds); + } + if(writefds) { + if((errno = check_user_area(VERIFY_WRITE, writefds, sizeof(fd_set)))) { + return errno; + } + memcpy_b(&wfds, writefds, sizeof(fd_set)); + } else { + __FD_ZERO(&wfds); + } + if(exceptfds) { + if((errno = check_user_area(VERIFY_WRITE, exceptfds, sizeof(fd_set)))) { + return errno; + } + memcpy_b(&efds, exceptfds, sizeof(fd_set)); + } else { + __FD_ZERO(&efds); + } + + /* check the validity of all fds */ + if((errno = check_fds(nfds, &rfds, &wfds, &efds)) < 0) { + return errno; + } + + if(timeout) { + t = tv2ticks(timeout); + } else { + t = INFINITE_WAIT; + } + + __FD_ZERO(&res_rfds); + __FD_ZERO(&res_wfds); + __FD_ZERO(&res_efds); + + current->timeout = t; + if((errno = do_select(nfds, &rfds, &wfds, &efds, &res_rfds, &res_wfds, &res_efds)) < 0) { + return errno; + } + current->timeout = 0; + + if(readfds) { + memcpy_b(readfds, &res_rfds, sizeof(fd_set)); + } + if(writefds) { + memcpy_b(writefds, &res_wfds, sizeof(fd_set)); + } + if(exceptfds) { + memcpy_b(exceptfds, &res_efds, sizeof(fd_set)); + } + return errno; +} diff --git a/kernel/syscalls/setdomainname.c b/kernel/syscalls/setdomainname.c new file mode 100644 index 00000000..cb14ac5b --- /dev/null +++ b/kernel/syscalls/setdomainname.c @@ -0,0 +1,38 @@ +/* + * fiwix/kernel/syscalls/setdomainname.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_setdomainname(const char *name, int length) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_setdomainname('%s', %d)\n", current->pid, name, length); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_READ, name, length))) { + return errno; + } + if(!IS_SUPERUSER) { + return -EPERM; + } + if(length < 0 || length > _UTSNAME_LENGTH) { + return -EINVAL; + } + memcpy_b(&sys_utsname.domainname, name, length); + sys_utsname.domainname[length] = NULL; + return 0; +} diff --git a/kernel/syscalls/setfsgid.c b/kernel/syscalls/setfsgid.c new file mode 100644 index 00000000..6f5d527b --- /dev/null +++ b/kernel/syscalls/setfsgid.c @@ -0,0 +1,21 @@ +/* + * fiwix/kernel/syscalls/setfsgid.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_setfsgid(__gid_t fsgid) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_setfsgid(%d) -> %d\n", current->pid, fsgid); +#endif /*__DEBUG__ */ + return 0; +} diff --git a/kernel/syscalls/setfsuid.c b/kernel/syscalls/setfsuid.c new file mode 100644 index 00000000..b678610b --- /dev/null +++ b/kernel/syscalls/setfsuid.c @@ -0,0 +1,21 @@ +/* + * fiwix/kernel/syscalls/setfsuid.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_setfsuid(__uid_t fsuid) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_setfsuid(%d) -> %d\n", current->pid, fsuid); +#endif /*__DEBUG__ */ + return 0; +} diff --git a/kernel/syscalls/setgid.c b/kernel/syscalls/setgid.c new file mode 100644 index 00000000..a0abd3e7 --- /dev/null +++ b/kernel/syscalls/setgid.c @@ -0,0 +1,32 @@ +/* + * fiwix/kernel/syscalls/setgid.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_setgid(__gid_t gid) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_setgid(%d)\n", current->pid, gid); +#endif /*__DEBUG__ */ + + if(IS_SUPERUSER) { + current->gid = current->egid = current->sgid = gid; + } else { + if((current->gid == gid) || (current->sgid == gid)) { + current->egid = gid; + } else { + return -EPERM; + } + } + return 0; +} diff --git a/kernel/syscalls/setgroups.c b/kernel/syscalls/setgroups.c new file mode 100644 index 00000000..76bf7720 --- /dev/null +++ b/kernel/syscalls/setgroups.c @@ -0,0 +1,41 @@ +/* + * fiwix/kernel/syscalls/setgroups.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_setgroups(__ssize_t size, const __gid_t *list) +{ + int n, errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_setgroups(%d, 0x%08x)\n", current->pid, size, (unsigned int)list); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_READ, list, sizeof(__gid_t)))) { + return errno; + } + if(!IS_SUPERUSER) { + return -EPERM; + } + if(size > NGROUPS_MAX) { + return -EINVAL; + } + for(n = 0; n < NGROUPS_MAX; n++) { + current->groups[n] = -1; + if(n < size) { + current->groups[n] = list[n]; + } + } + return 0; +} diff --git a/kernel/syscalls/sethostname.c b/kernel/syscalls/sethostname.c new file mode 100644 index 00000000..7a5406ac --- /dev/null +++ b/kernel/syscalls/sethostname.c @@ -0,0 +1,42 @@ +/* + * fiwix/kernel/syscalls/sethostname.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_sethostname(const char *name, int length) +{ + int errno; + char *tmp_name; + +#ifdef __DEBUG__ + printk("(pid %d) sys_sethostname('%s', %d)\n", current->pid, name, length); +#endif /*__DEBUG__ */ + + if((errno = malloc_name(name, &tmp_name)) < 0) { + return errno; + } + if(!IS_SUPERUSER) { + free_name(tmp_name); + return -EPERM; + } + if(length < 0 || length > _UTSNAME_LENGTH) { + free_name(tmp_name); + return -EINVAL; + } + memcpy_b(&sys_utsname.nodename, tmp_name, length); + sys_utsname.nodename[length] = NULL; + free_name(tmp_name); + return 0; +} diff --git a/kernel/syscalls/setitimer.c b/kernel/syscalls/setitimer.c new file mode 100644 index 00000000..41b38d56 --- /dev/null +++ b/kernel/syscalls/setitimer.c @@ -0,0 +1,31 @@ +/* + * fiwix/kernel/syscalls/setitimer.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_setitimer(%d, 0x%08x, 0x%08x) -> \n", current->pid, which, (unsigned int)new_value, (unsigned int)old_value); +#endif /*__DEBUG__ */ + + if((unsigned int)old_value) { + if((errno = check_user_area(VERIFY_WRITE, old_value, sizeof(struct itimerval)))) { + return errno; + } + } + + return setitimer(which, new_value, old_value); +} diff --git a/kernel/syscalls/setpgid.c b/kernel/syscalls/setpgid.c new file mode 100644 index 00000000..88b9d98a --- /dev/null +++ b/kernel/syscalls/setpgid.c @@ -0,0 +1,67 @@ +/* + * fiwix/kernel/syscalls/setpgid.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_setpgid(__pid_t pid, __pid_t pgid) +{ + struct proc *p; + +#ifdef __DEBUG__ + printk("(pid %d) sys_setpgid(%d, %d)", current->pid, pid, pgid); +#endif /*__DEBUG__ */ + + if(pid < 0 || pgid < 0) { + return -EINVAL; + } + if(!pid) { + pid = current->pid; + } + + p = get_proc_by_pid(pid); + if(!pgid) { + pgid = p->pid; + } + + if(p != current && (p->state == PROC_UNUSED || p->ppid != current->pid)) { + return -ESRCH; + } + if(p->sid == p->pid || p->sid != current->sid) { + return -EPERM; + } + + { + struct proc *p; + + FOR_EACH_PROCESS(p) { + if(p->state != PROC_UNUSED) { + if(p->pgid == pgid && p->sid != current->sid) { + return -EPERM; + } + } + } + } + + if(p != current && p->flags & PF_PEXEC) { + return -EACCES; + } + + p->pgid = pgid; + +#ifdef __DEBUG__ + printk(" -> 0\n"); +#endif /*__DEBUG__ */ + + return 0; +} diff --git a/kernel/syscalls/setregid.c b/kernel/syscalls/setregid.c new file mode 100644 index 00000000..f13a6e18 --- /dev/null +++ b/kernel/syscalls/setregid.c @@ -0,0 +1,49 @@ +/* + * fiwix/kernel/syscalls/setregid.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_setregid(__gid_t gid, __gid_t egid) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_setregid(%d, %d) -> ", current->pid, gid, egid); +#endif /*__DEBUG__ */ + + if(IS_SUPERUSER) { + if(egid >= 0) { + if(gid >= 0 || (current->egid >= 0 && current->gid != egid)) { + current->sgid = egid; + } + current->egid = egid; + } + if(gid >= 0) { + current->gid = gid; + } + } else { + if(egid >= 0 && (current->gid == egid || current->egid == egid || current->sgid == egid)) { + if(gid >= 0 || (current->egid >= 0 && current->gid != egid)) { + current->sgid = egid; + } + current->egid = egid; + } else { + return -EPERM; + } + if(gid >= 0 && (current->gid == gid || current->egid == gid)) { + current->gid = gid; + } else { + return -EPERM; + } + } + + return 0; +} diff --git a/kernel/syscalls/setreuid.c b/kernel/syscalls/setreuid.c new file mode 100644 index 00000000..38b570d1 --- /dev/null +++ b/kernel/syscalls/setreuid.c @@ -0,0 +1,49 @@ +/* + * fiwix/kernel/syscalls/setreuid.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_setreuid(__uid_t uid, __uid_t euid) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_setreuid(%d, %d) -> ", current->pid, uid, euid); +#endif /*__DEBUG__ */ + + if(IS_SUPERUSER) { + if(euid >= 0) { + if(uid >= 0 || (current->euid >= 0 && current->uid != euid)) { + current->suid = euid; + } + current->euid = euid; + } + if(uid >= 0) { + current->uid = uid; + } + } else { + if(euid >= 0 && (current->uid == euid || current->euid == euid || current->suid == euid)) { + if(uid >= 0 || (current->euid >= 0 && current->uid != euid)) { + current->suid = euid; + } + current->euid = euid; + } else { + return -EPERM; + } + if(uid >= 0 && (current->uid == uid || current->euid == uid)) { + current->uid = uid; + } else { + return -EPERM; + } + } + + return 0; +} diff --git a/kernel/syscalls/setrlimit.c b/kernel/syscalls/setrlimit.c new file mode 100644 index 00000000..5aff0eb7 --- /dev/null +++ b/kernel/syscalls/setrlimit.c @@ -0,0 +1,42 @@ +/* + * fiwix/kernel/syscalls/setrlimit.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_setrlimit(int resource, const struct rlimit *rlim) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_setrlimit(%d, 0x%08x)\n", current->pid, resource, (unsigned int)rlim); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_READ, rlim, sizeof(struct rlimit)))) { + return errno; + } + if(resource < 0 || resource >= RLIM_NLIMITS) { + return -EINVAL; + } + if(rlim->rlim_cur > rlim->rlim_max) { + return -EINVAL; + } + if(!IS_SUPERUSER) { + if(rlim->rlim_max > current->rlim[resource].rlim_max) { + return -EPERM; + } + } + memcpy_b(¤t->rlim[resource], rlim, sizeof(struct rlimit)); + return 0; +} diff --git a/kernel/syscalls/setsid.c b/kernel/syscalls/setsid.c new file mode 100644 index 00000000..1e1230ad --- /dev/null +++ b/kernel/syscalls/setsid.c @@ -0,0 +1,36 @@ +/* + * fiwix/kernel/syscalls/setsid.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_setsid(void) +{ + struct proc *p; + +#ifdef __DEBUG__ + printk("(pid %d) sys_setsid()\n", current->pid); +#endif /*__DEBUG__ */ + + if(PG_LEADER(current)) { + return -EPERM; + } + FOR_EACH_PROCESS(p) { /* POSIX ANSI/IEEE Std 1003.1-1996 4.3.2 */ + if(p != current && p->pgid == current->pid) { + return -EPERM; + } + } + + current->sid = current->pgid = current->pid; + current->ctty = NULL; + return current->sid; +} diff --git a/kernel/syscalls/settimeofday.c b/kernel/syscalls/settimeofday.c new file mode 100644 index 00000000..cefeb1b2 --- /dev/null +++ b/kernel/syscalls/settimeofday.c @@ -0,0 +1,46 @@ +/* + * fiwix/kernel/syscalls/settimeofday.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_settimeofday(const struct timeval *tv, const struct timezone *tz) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_settimeofday()\n", current->pid); +#endif /*__DEBUG__ */ + + if(!IS_SUPERUSER) { + return -EPERM; + } + + if(tv) { + if((errno = check_user_area(VERIFY_READ, tv, sizeof(struct timeval)))) { + return errno; + } + CURRENT_TIME = tv->tv_sec; + set_system_time(CURRENT_TIME); + } + if(tz) { + if((errno = check_user_area(VERIFY_READ, tz, sizeof(struct timezone)))) { + return errno; + } + kstat.tz_minuteswest = tz->tz_minuteswest; + kstat.tz_dsttime = tz->tz_dsttime; + } + return 0; +} diff --git a/kernel/syscalls/setuid.c b/kernel/syscalls/setuid.c new file mode 100644 index 00000000..959f9f26 --- /dev/null +++ b/kernel/syscalls/setuid.c @@ -0,0 +1,31 @@ +/* + * fiwix/kernel/syscalls/setuid.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_setuid(__uid_t uid) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_setuid(%d)\n", current->pid, uid); +#endif /*__DEBUG__ */ + + if(IS_SUPERUSER) { + current->uid = current->suid = uid; + } else { + if((current->uid != uid) && (current->suid != uid)) { + return -EPERM; + } + } + current->euid = uid; + return 0; +} diff --git a/kernel/syscalls/sgetmask.c b/kernel/syscalls/sgetmask.c new file mode 100644 index 00000000..2f9c61ea --- /dev/null +++ b/kernel/syscalls/sgetmask.c @@ -0,0 +1,20 @@ +/* + * fiwix/kernel/syscalls/sgetmask.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_sgetmask(void) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_sgetmask() -> \n", current->pid); +#endif /*__DEBUG__ */ + return current->sigblocked; +} diff --git a/kernel/syscalls/sigaction.c b/kernel/syscalls/sigaction.c new file mode 100644 index 00000000..11eadcc0 --- /dev/null +++ b/kernel/syscalls/sigaction.c @@ -0,0 +1,54 @@ +/* + * fiwix/kernel/syscalls/sigaction.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_sigaction(__sigset_t signum, const struct sigaction *newaction, struct sigaction *oldaction) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_sigaction(%d, 0x%08x, 0x%08x)\n", current->pid, signum, (unsigned int)newaction, (unsigned int)oldaction); +#endif /*__DEBUG__ */ + + if(signum < 1 || signum > NSIG) { + return -EINVAL; + } + if(signum == SIGKILL || signum == SIGSTOP) { + return -EINVAL; + } + if(oldaction) { + if((errno = check_user_area(VERIFY_WRITE, oldaction, sizeof(struct sigaction)))) { + return errno; + } + *oldaction = current->sigaction[signum - 1]; + } + if(newaction) { + if((errno = check_user_area(VERIFY_READ, newaction, sizeof(struct sigaction)))) { + return errno; + } + current->sigaction[signum - 1] = *newaction; + if(current->sigaction[signum - 1].sa_handler == SIG_IGN) { + if(signum != SIGCHLD) { + current->sigpending &= SIG_MASK(signum); + } + } + if(current->sigaction[signum - 1].sa_handler == SIG_DFL) { + if(signum != SIGCHLD) { + current->sigpending &= SIG_MASK(signum); + } + } + } + return 0; +} diff --git a/kernel/syscalls/signal.c b/kernel/syscalls/signal.c new file mode 100644 index 00000000..2903013b --- /dev/null +++ b/kernel/syscalls/signal.c @@ -0,0 +1,56 @@ +/* + * fiwix/kernel/syscalls/signal.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +unsigned int sys_signal(__sigset_t signum, void(* sighandler)(int)) +{ + struct sigaction s; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_signal()\n", current->pid); +#endif /*__DEBUG__ */ + + if(signum < 1 || signum > NSIG) { + return -EINVAL; + } + if(signum == SIGKILL || signum == SIGSTOP) { + return -EINVAL; + } + if(sighandler != SIG_DFL && sighandler != SIG_IGN) { + if((errno = check_user_area(VERIFY_READ, sighandler, sizeof(void)))) { + return errno; + } + } + + memset_b(&s, 0, sizeof(struct sigaction)); + s.sa_handler = sighandler; + s.sa_mask = 0; + s.sa_flags = SA_RESETHAND; + sighandler = current->sigaction[signum - 1].sa_handler; + current->sigaction[signum - 1] = s; + if(current->sigaction[signum - 1].sa_handler == SIG_IGN) { + if(signum != SIGCHLD) { + current->sigpending &= SIG_MASK(signum); + } + } + if(current->sigaction[signum - 1].sa_handler == SIG_DFL) { + if(signum != SIGCHLD) { + current->sigpending &= SIG_MASK(signum); + } + } + return (unsigned int)sighandler; +} diff --git a/kernel/syscalls/sigpending.c b/kernel/syscalls/sigpending.c new file mode 100644 index 00000000..5cf0683d --- /dev/null +++ b/kernel/syscalls/sigpending.c @@ -0,0 +1,30 @@ +/* + * fiwix/kernel/syscalls/sigpending.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_sigpending(__sigset_t *set) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_sigpending(0x%08x) -> ", current->pid, set); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_WRITE, set, sizeof(__sigset_t)))) { + return errno; + } + memcpy_b(set, ¤t->sigpending, sizeof(__sigset_t)); + return 0; +} diff --git a/kernel/syscalls/sigprocmask.c b/kernel/syscalls/sigprocmask.c new file mode 100644 index 00000000..fd016b2b --- /dev/null +++ b/kernel/syscalls/sigprocmask.c @@ -0,0 +1,51 @@ +/* + * fiwix/kernel/syscalls/sigprocmask.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_sigprocmask(int how, const __sigset_t *set, __sigset_t *oldset) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_sigprocmask(%d, 0x%08x, 0x%08x)\n", current->pid, how, set, oldset); +#endif /*__DEBUG__ */ + + if(oldset) { + if((errno = check_user_area(VERIFY_WRITE, oldset, sizeof(__sigset_t)))) { + return errno; + } + *oldset = current->sigblocked; + } + + if(set) { + if((errno = check_user_area(VERIFY_READ, set, sizeof(__sigset_t)))) { + return errno; + } + switch(how) { + case SIG_BLOCK: + current->sigblocked |= (*set & SIG_BLOCKABLE); + break; + case SIG_UNBLOCK: + current->sigblocked &= ~(*set & SIG_BLOCKABLE); + break; + case SIG_SETMASK: + current->sigblocked = (*set & SIG_BLOCKABLE); + break; + default: + return -EINVAL; + } + } + return 0; +} diff --git a/kernel/syscalls/sigreturn.c b/kernel/syscalls/sigreturn.c new file mode 100644 index 00000000..bf44cc03 --- /dev/null +++ b/kernel/syscalls/sigreturn.c @@ -0,0 +1,31 @@ +/* + * fiwix/kernel/syscalls/sigreturn.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_sigreturn(unsigned int signum, int arg2, int arg3, int arg4, int arg5, struct sigcontext *sc) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_sigreturn(0x%08x)\n", current->pid, signum); +#endif /*__DEBUG__ */ + + current->sigblocked &= ~current->sigexecuting; + current->sigexecuting = 0; + memcpy_b(sc, ¤t->sc[signum - 1], sizeof(struct sigcontext)); + + /* + * We return here the value that the syscall was returning when it was + * interrupted by a signal. + */ + return current->sc[signum - 1].eax; +} diff --git a/kernel/syscalls/sigsuspend.c b/kernel/syscalls/sigsuspend.c new file mode 100644 index 00000000..e1e71764 --- /dev/null +++ b/kernel/syscalls/sigsuspend.c @@ -0,0 +1,44 @@ +/* + * fiwix/kernel/syscalls/sigsuspend.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_sigsuspend(__sigset_t *mask) +{ + __sigset_t old_mask; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_sigsuspend(0x%08x) -> ", current->pid, mask); +#endif /*__DEBUG__ */ + + old_mask = current->sigblocked; + if(mask) { + if((errno = check_user_area(VERIFY_READ, mask, sizeof(__sigset_t)))) { + return errno; + } + current->sigblocked = (int)*mask & SIG_BLOCKABLE; + } else { + current->sigblocked = 0 & SIG_BLOCKABLE; + } + sys_pause(); + current->sigblocked = old_mask; + +#ifdef __DEBUG__ + printk("-EINTR\n"); +#endif /*__DEBUG__ */ + + return -EINTR; +} diff --git a/kernel/syscalls/socketcall.c b/kernel/syscalls/socketcall.c new file mode 100644 index 00000000..38c7b77a --- /dev/null +++ b/kernel/syscalls/socketcall.c @@ -0,0 +1,24 @@ +/* + * fiwix/kernel/syscalls/socketcall.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_socketcall(int call, unsigned long int *args) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_socketcall(%d, 0x%08x) -> ENOENT\n", current->pid, call, args); +#endif /*__DEBUG__ */ + + /* FIXME: to be implemented */ + + return -ENOENT; +} diff --git a/kernel/syscalls/ssetmask.c b/kernel/syscalls/ssetmask.c new file mode 100644 index 00000000..b3ba9396 --- /dev/null +++ b/kernel/syscalls/ssetmask.c @@ -0,0 +1,26 @@ +/* + * fiwix/kernel/syscalls/ssetmask.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_ssetmask(int newmask) +{ + int oldmask; + +#ifdef __DEBUG__ + printk("(pid %d) sys_ssetmask(0x%08x) -> \n", current->pid, newmask); +#endif /*__DEBUG__ */ + + oldmask = current->sigblocked; + current->sigblocked = newmask & SIG_BLOCKABLE; + return oldmask; +} diff --git a/kernel/syscalls/stat.c b/kernel/syscalls/stat.c new file mode 100644 index 00000000..4fb18441 --- /dev/null +++ b/kernel/syscalls/stat.c @@ -0,0 +1,52 @@ +/* + * fiwix/kernel/syscalls/stat.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_stat(const char *filename, struct old_stat *statbuf) +{ + struct inode *i; + char *tmp_name; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_stat(%s, 0x%08x) -> returning structure\n", current->pid, filename, (unsigned int )statbuf); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_WRITE, statbuf, sizeof(struct old_stat)))) { + return errno; + } + if((errno = malloc_name(filename, &tmp_name)) < 0) { + return errno; + } + if((errno = namei(tmp_name, &i, NULL, FOLLOW_LINKS))) { + free_name(tmp_name); + return errno; + } + statbuf->st_dev = i->dev; + statbuf->st_ino = i->inode; + statbuf->st_mode = i->i_mode; + statbuf->st_nlink = i->i_nlink; + statbuf->st_uid = i->i_uid; + statbuf->st_gid = i->i_gid; + statbuf->st_rdev = i->rdev; + statbuf->st_size = i->i_size; + statbuf->st_atime = i->i_atime; + statbuf->st_mtime = i->i_mtime; + statbuf->st_ctime = i->i_ctime; + iput(i); + free_name(tmp_name); + return 0; +} diff --git a/kernel/syscalls/statfs.c b/kernel/syscalls/statfs.c new file mode 100644 index 00000000..8aacdde6 --- /dev/null +++ b/kernel/syscalls/statfs.c @@ -0,0 +1,47 @@ +/* + * fiwix/kernel/syscalls/statfs.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_statfs(const char *filename, struct statfs *statfsbuf) +{ + struct inode *i; + char *tmp_name; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_statfs('%s', 0x%08x)\n", current->pid, filename, (unsigned int)statfsbuf); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_WRITE, statfsbuf, sizeof(struct statfs)))) { + return errno; + } + if((errno = malloc_name(filename, &tmp_name)) < 0) { + return errno; + } + if((errno = namei(tmp_name, &i, NULL, FOLLOW_LINKS))) { + free_name(tmp_name); + return errno; + } + if(i->sb && i->sb->fsop && i->sb->fsop->statfs) { + i->sb->fsop->statfs(i->sb, statfsbuf); + iput(i); + free_name(tmp_name); + return 0; + } + iput(i); + free_name(tmp_name); + return -ENOSYS; +} diff --git a/kernel/syscalls/stime.c b/kernel/syscalls/stime.c new file mode 100644 index 00000000..66a471df --- /dev/null +++ b/kernel/syscalls/stime.c @@ -0,0 +1,35 @@ +/* + * fiwix/kernel/syscalls/stime.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_stime(__time_t *t) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_stime(0x%08x)\n", current->pid, (unsigned int)t); +#endif /*__DEBUG__ */ + + if(!IS_SUPERUSER) { + return -EPERM; + } + if((errno = check_user_area(VERIFY_READ, t, sizeof(__time_t)))) { + return errno; + } + + set_system_time(*t); + return 0; +} diff --git a/kernel/syscalls/symlink.c b/kernel/syscalls/symlink.c new file mode 100644 index 00000000..146168f7 --- /dev/null +++ b/kernel/syscalls/symlink.c @@ -0,0 +1,73 @@ +/* + * fiwix/kernel/syscalls/symlink.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_symlink(const char *oldpath, const char *newpath) +{ + struct inode *i, *dir; + char *tmp_oldpath, *tmp_newpath, *basename; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_symlink('%s', '%s')\n", current->pid, oldpath, newpath); +#endif /*__DEBUG__ */ + + if((errno = malloc_name(oldpath, &tmp_oldpath)) < 0) { + return errno; + } + if((errno = malloc_name(newpath, &tmp_newpath)) < 0) { + free_name(tmp_oldpath); + return errno; + } + basename = get_basename(tmp_newpath); + if((errno = namei(tmp_newpath, &i, &dir, !FOLLOW_LINKS))) { + if(!dir) { + free_name(tmp_oldpath); + free_name(tmp_newpath); + return errno; + } + } + if(!errno) { + iput(i); + iput(dir); + free_name(tmp_oldpath); + free_name(tmp_newpath); + return -EEXIST; + } + if(IS_RDONLY_FS(dir)) { + iput(dir); + free_name(tmp_oldpath); + free_name(tmp_newpath); + return -EROFS; + } + + if(check_permission(TO_EXEC | TO_WRITE, dir) < 0) { + iput(dir); + free_name(tmp_oldpath); + free_name(tmp_newpath); + return -EACCES; + } + + if(dir->fsop && dir->fsop->symlink) { + errno = dir->fsop->symlink(dir, basename, tmp_oldpath); + } else { + errno = -EPERM; + } + iput(dir); + free_name(tmp_oldpath); + free_name(tmp_newpath); + return errno; +} diff --git a/kernel/syscalls/sync.c b/kernel/syscalls/sync.c new file mode 100644 index 00000000..b587d526 --- /dev/null +++ b/kernel/syscalls/sync.c @@ -0,0 +1,27 @@ +/* + * fiwix/kernel/syscalls/sync.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +void sys_sync(void) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_sync()\n", current->pid); +#endif /*__DEBUG__ */ + + sync_superblocks(0); /* in all devices */ + sync_inodes(0); /* in all devices */ + sync_buffers(0); /* in all devices */ + return; +} diff --git a/kernel/syscalls/sysinfo.c b/kernel/syscalls/sysinfo.c new file mode 100644 index 00000000..7e9450e8 --- /dev/null +++ b/kernel/syscalls/sysinfo.c @@ -0,0 +1,52 @@ +/* + * fiwix/kernel/syscalls/sysinfo.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_sysinfo(struct sysinfo *info) +{ + struct sysinfo tmp_info; + struct proc *p; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_sysinfo(0x%08x)\n ", current->pid, (unsigned int)info); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_WRITE, info, sizeof(struct sysinfo)))) { + return errno; + } + memset_b(&tmp_info, NULL, sizeof(struct sysinfo)); + tmp_info.loads[0] = avenrun[0] << (SI_LOAD_SHIFT - FSHIFT); + tmp_info.loads[1] = avenrun[1] << (SI_LOAD_SHIFT - FSHIFT); + tmp_info.loads[2] = avenrun[2] << (SI_LOAD_SHIFT - FSHIFT); + tmp_info.uptime = kstat.uptime; + tmp_info.totalram = kstat.total_mem_pages << PAGE_SHIFT; + tmp_info.freeram = kstat.free_pages << PAGE_SHIFT; + tmp_info.sharedram = 0; + tmp_info.bufferram = kstat.buffers * 1024; + tmp_info.totalswap = 0; + tmp_info.freeswap = 0; + FOR_EACH_PROCESS(p) { + if(p->state) { + tmp_info.procs++; + } + } + + memcpy_b(info, &tmp_info, sizeof(struct sysinfo)); + return 0; +} diff --git a/kernel/syscalls/time.c b/kernel/syscalls/time.c new file mode 100644 index 00000000..ecd775eb --- /dev/null +++ b/kernel/syscalls/time.c @@ -0,0 +1,37 @@ +/* + * fiwix/kernel/syscalls/time.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_time(__time_t *tloc) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_time() -> ", current->pid); +#endif /*__DEBUG__ */ + + if(tloc) { + if((errno = check_user_area(VERIFY_WRITE, tloc, sizeof(__time_t)))) { + return errno; + } + *tloc = CURRENT_TIME; + } + +#ifdef __DEBUG__ + printk("%d\n", CURRENT_TIME); +#endif /*__DEBUG__ */ + + return CURRENT_TIME; +} diff --git a/kernel/syscalls/times.c b/kernel/syscalls/times.c new file mode 100644 index 00000000..0836ff07 --- /dev/null +++ b/kernel/syscalls/times.c @@ -0,0 +1,37 @@ +/* + * fiwix/kernel/syscalls/times.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_times(struct tms *buf) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_times(0x%08x) -> ", (unsigned int )buf); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_WRITE, buf, sizeof(struct tms)))) { + return errno; + } + if(buf) { + buf->tms_utime = tv2ticks(¤t->usage.ru_utime); + buf->tms_stime = tv2ticks(¤t->usage.ru_stime); + buf->tms_cutime = tv2ticks(¤t->cusage.ru_utime); + buf->tms_cstime = tv2ticks(¤t->cusage.ru_stime); + } + + return kstat.ticks; +} diff --git a/kernel/syscalls/truncate.c b/kernel/syscalls/truncate.c new file mode 100644 index 00000000..427018bb --- /dev/null +++ b/kernel/syscalls/truncate.c @@ -0,0 +1,66 @@ +/* + * fiwix/kernel/syscalls/truncate.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_truncate(const char *path, __off_t length) +{ + struct inode *i; + char *tmp_name; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_truncate(%s, %d)\n", current->pid, path, length); +#endif /*__DEBUG__ */ + + if((errno = malloc_name(path, &tmp_name)) < 0) { + return errno; + } + if((errno = namei(tmp_name, &i, NULL, FOLLOW_LINKS))) { + free_name(tmp_name); + return errno; + } + if(S_ISDIR(i->i_mode)) { + iput(i); + free_name(tmp_name); + return -EISDIR; + } + if(IS_RDONLY_FS(i)) { + iput(i); + free_name(tmp_name); + return -EROFS; + } + if(check_permission(TO_WRITE, i) < 0) { + iput(i); + free_name(tmp_name); + return -EACCES; + } + if(length == i->i_size) { + iput(i); + free_name(tmp_name); + return 0; + } + + errno = 0; + if(i->fsop && i->fsop->truncate) { + inode_lock(i); + errno = i->fsop->truncate(i, length); + inode_unlock(i); + } + iput(i); + free_name(tmp_name); + return errno; +} diff --git a/kernel/syscalls/umask.c b/kernel/syscalls/umask.c new file mode 100644 index 00000000..a62e532c --- /dev/null +++ b/kernel/syscalls/umask.c @@ -0,0 +1,27 @@ +/* + * fiwix/kernel/syscalls/umask.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_umask(__mode_t mask) +{ + __mode_t old_umask; + +#ifdef __DEBUG__ + printk("(pid %d) sys_umask(%d)\n", current->pid, mask); +#endif /*__DEBUG__ */ + + old_umask = current->umask; + current->umask = mask & (S_IRWXU | S_IRWXG | S_IRWXO); + return old_umask; +} diff --git a/kernel/syscalls/umount.c b/kernel/syscalls/umount.c new file mode 100644 index 00000000..262e11af --- /dev/null +++ b/kernel/syscalls/umount.c @@ -0,0 +1,22 @@ +/* + * fiwix/kernel/syscalls/umount.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include + +#ifdef __DEBUG__ +#include +#endif /*__DEBUG__ */ + +int sys_umount(const char *target) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_umount(%s)\n", current->pid, target); +#endif /*__DEBUG__ */ + + return sys_umount2(target, 0); +} diff --git a/kernel/syscalls/umount2.c b/kernel/syscalls/umount2.c new file mode 100644 index 00000000..bfddeb27 --- /dev/null +++ b/kernel/syscalls/umount2.c @@ -0,0 +1,115 @@ +/* + * fiwix/kernel/syscalls/umount2.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct resource umount_resource = { NULL, NULL }; + +int sys_umount2(const char *target, int flags) +{ + struct inode *i_target; + struct mount *mt = NULL; + struct filesystems *fs; + struct device *d; + struct inode dummy_i; + struct superblock *sb; + char *tmp_target; + __dev_t dev; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_umount2(%s, 0x%08x)\n", current->pid, target, flags); +#endif /*__DEBUG__ */ + + if(!IS_SUPERUSER) { + return -EPERM; + } + if((errno = malloc_name(target, &tmp_target)) < 0) { + return errno; + } + if((errno = namei(tmp_target, &i_target, NULL, FOLLOW_LINKS))) { + free_name(tmp_target); + return errno; + } + if(!S_ISBLK(i_target->i_mode) && !S_ISDIR(i_target->i_mode)) { + iput(i_target); + free_name(tmp_target); + return -EINVAL; + } + + if(!(mt = get_mount_point(i_target))) { + iput(i_target); + free_name(tmp_target); + return -EINVAL; + } + if(S_ISBLK(i_target->i_mode)) { + dev = i_target->rdev; + } else { + dev = i_target->sb->dev; + } + + if(!(sb = get_superblock(dev))) { + printk("WARNING: %s(): unable to get superblock from device %d,%d\n", __FUNCTION__, MAJOR(dev), MINOR(dev)); + iput(i_target); + free_name(tmp_target); + return -EINVAL; + } + + /* + * We must free now the inode in order to avoid having its 'count' to 2 + * when calling check_fs_busy(), specially if sys_umount() was called + * using the mount-point instead of the device. + */ + iput(i_target); + free_name(tmp_target); + + if(check_fs_busy(dev, sb->root)) { + return -EBUSY; + } + + lock_resource(&umount_resource); + + fs = mt->fs; + if(fs->fsop && fs->fsop->release_superblock) { + fs->fsop->release_superblock(sb); + } + if(sb->fsop->flags & FSOP_REQUIRES_DEV) { + if(!(d = get_device(BLK_DEV, MAJOR(dev)))) { + printk("WARNING: %s(): block device %d,%d not registered!\n", __FUNCTION__, MAJOR(dev), MINOR(dev)); + unlock_resource(&umount_resource); + return -EINVAL; + } + memset_b(&dummy_i, 0, sizeof(struct inode)); + dummy_i.dev = dummy_i.rdev = dev; + if(d && d->fsop && d->fsop->close) { + d->fsop->close(&dummy_i, NULL); + } + } + + sb->dir->mount_point = NULL; + iput(sb->root); + iput(sb->dir); + + sync_superblocks(dev); + sync_inodes(dev); + sync_buffers(dev); + invalidate_buffers(dev); + invalidate_inodes(dev); + + release_mount_point(mt); + unlock_resource(&umount_resource); + return 0; +} diff --git a/kernel/syscalls/uname.c b/kernel/syscalls/uname.c new file mode 100644 index 00000000..3e939d71 --- /dev/null +++ b/kernel/syscalls/uname.c @@ -0,0 +1,34 @@ +/* + * fiwix/kernel/syscalls/uname.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_uname(struct old_utsname *uname) +{ + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_uname(0x%08x) -> returning ", current->pid, (unsigned int)uname); +#endif /*__DEBUG__ */ + + if((errno = check_user_area(VERIFY_WRITE, uname, sizeof(struct old_utsname)))) { + return errno; + } + memcpy_b(&uname->sysname, &sys_utsname.sysname, sizeof(sys_utsname.sysname)); + memcpy_b(&uname->nodename, &sys_utsname.nodename, sizeof(sys_utsname.nodename)); + memcpy_b(&uname->release, &sys_utsname.release, sizeof(sys_utsname.release)); + memcpy_b(&uname->version, &sys_utsname.version, sizeof(sys_utsname.version)); + memcpy_b(&uname->machine, &sys_utsname.machine, sizeof(sys_utsname.machine)); + return 0; +} diff --git a/kernel/syscalls/unlink.c b/kernel/syscalls/unlink.c new file mode 100644 index 00000000..167414ce --- /dev/null +++ b/kernel/syscalls/unlink.c @@ -0,0 +1,78 @@ +/* + * fiwix/kernel/syscalls/unlink.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_unlink(const char *filename) +{ + struct inode *i, *dir; + char *tmp_name, *basename; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_unlink('%s')\n", current->pid, filename); +#endif /*__DEBUG__ */ + + if((errno = malloc_name(filename, &tmp_name)) < 0) { + return errno; + } + if((errno = namei(tmp_name, &i, &dir, !FOLLOW_LINKS))) { + if(dir) { + iput(dir); + } + free_name(tmp_name); + return errno; + } + if(S_ISDIR(i->i_mode)) { + iput(i); + iput(dir); + free_name(tmp_name); + return -EPERM; /* Linux returns -EISDIR */ + } + if(IS_RDONLY_FS(i)) { + iput(i); + iput(dir); + free_name(tmp_name); + return -EROFS; + } + if(check_permission(TO_EXEC | TO_WRITE, dir) < 0) { + iput(i); + iput(dir); + free_name(tmp_name); + return -EACCES; + } + + /* check sticky permission bit */ + if(dir->i_mode & S_ISVTX) { + if(check_user_permission(i)) { + iput(i); + iput(dir); + free_name(tmp_name); + return -EPERM; + } + } + + basename = get_basename(filename); + if(dir->fsop && dir->fsop->unlink) { + errno = dir->fsop->unlink(dir, i, basename); + } else { + errno = -EPERM; + } + iput(i); + iput(dir); + free_name(tmp_name); + return errno; +} diff --git a/kernel/syscalls/ustat.c b/kernel/syscalls/ustat.c new file mode 100644 index 00000000..ebd65d6d --- /dev/null +++ b/kernel/syscalls/ustat.c @@ -0,0 +1,44 @@ +/* + * fiwix/kernel/syscalls/ustat.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_ustat(__dev_t dev, struct ustat *ubuf) +{ + struct superblock *sb; + struct statfs statfsbuf; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_ustat(%d, 0x%08x)\n", current->pid, dev, (int)ubuf); +#endif /*__DEBUG__ */ + if((errno = check_user_area(VERIFY_WRITE, ubuf, sizeof(struct ustat)))) { + return errno; + } + if(!(sb = get_superblock(dev))) { + return -EINVAL; + } + if(sb->fsop && sb->fsop->statfs) { + sb->fsop->statfs(sb, &statfsbuf); + memset_b(ubuf, NULL, sizeof(struct ustat)); + ubuf->f_tfree = statfsbuf.f_bfree; + ubuf->f_tinode = statfsbuf.f_ffree; + return 0; + } + return -ENOSYS; +} diff --git a/kernel/syscalls/utime.c b/kernel/syscalls/utime.c new file mode 100644 index 00000000..a1e4563c --- /dev/null +++ b/kernel/syscalls/utime.c @@ -0,0 +1,72 @@ +/* + * fiwix/kernel/syscalls/utime.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_utime(const char *filename, struct utimbuf *times) +{ + struct inode *i; + char *tmp_name; + int errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_utime('%s', 0x%08x)\n", current->pid, filename, (int)times); +#endif /*__DEBUG__ */ + + if((errno = malloc_name(filename, &tmp_name)) < 0) { + return errno; + } + if((errno = namei(tmp_name, &i, NULL, FOLLOW_LINKS))) { + free_name(tmp_name); + return errno; + } + + if(IS_RDONLY_FS(i)) { + iput(i); + free_name(tmp_name); + return -EROFS; + } + + if(!times) { + if(check_user_permission(i) || check_permission(TO_WRITE, i)) { + iput(i); + free_name(tmp_name); + return -EACCES; + } + i->i_atime = CURRENT_TIME; + i->i_mtime = CURRENT_TIME; + } else { + if((errno = check_user_area(VERIFY_READ, times, sizeof(struct utimbuf)))) { + iput(i); + free_name(tmp_name); + return errno; + } + if(check_user_permission(i)) { + iput(i); + free_name(tmp_name); + return -EPERM; + } + i->i_atime = times->actime; + i->i_mtime = times->modtime; + } + + i->i_ctime = CURRENT_TIME; + i->dirty = 1; + iput(i); + free_name(tmp_name); + return 0; +} diff --git a/kernel/syscalls/wait4.c b/kernel/syscalls/wait4.c new file mode 100644 index 00000000..1713946e --- /dev/null +++ b/kernel/syscalls/wait4.c @@ -0,0 +1,98 @@ +/* + * fiwix/kernel/syscalls/wait4.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_wait4(__pid_t pid, int *status, int options, struct rusage *ru) +{ + struct proc *p; + int flag, signum, errno; + +#ifdef __DEBUG__ + printk("(pid %d) sys_wait4(%d, status, %d)\n", current->pid, pid, options); +#endif /*__DEBUG__ */ + + if(ru) { + if((errno = check_user_area(VERIFY_WRITE, ru, sizeof(struct rusage)))) { + return errno; + } + } + while(current->children) { + flag = 0; + FOR_EACH_PROCESS(p) { + if(p->ppid != current->pid) { + continue; + } + if(pid > 0) { + if(p->pid == pid) { + flag = 1; + } + } + if(!pid) { + if(p->pgid == current->pgid) { + flag = 1; + } + } + if(pid < -1) { + if(p->pgid == -pid) { + flag = 1; + } + } + if(pid == -1) { + flag = 1; + } + if(flag) { + if(p->state == PROC_STOPPED) { + if(!p->exit_code) { + continue; + } + if(status) { + *status = (p->exit_code << 8) | 0x7F; + } + p->exit_code = 0; + if(ru) { + get_rusage(p, ru); + } + return p->pid; + } + if(p->state == PROC_ZOMBIE) { + add_rusage(p); + if(status) { + *status = p->exit_code; + } + if(ru) { + get_rusage(p, ru); + } + return remove_zombie(p); + } + } + flag = 0; + } + if(options & WNOHANG) { + if(flag) { + return 0; + } + break; + } + if((signum = sleep(&sys_wait4, PROC_INTERRUPTIBLE))) { + return signum; + } + current->sigpending &= SIG_MASK(SIGCHLD); + } + return -ECHILD; +} diff --git a/kernel/syscalls/waitpid.c b/kernel/syscalls/waitpid.c new file mode 100644 index 00000000..69883f45 --- /dev/null +++ b/kernel/syscalls/waitpid.c @@ -0,0 +1,23 @@ +/* + * fiwix/kernel/syscalls/waitpid.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_waitpid(__pid_t pid, int *status, int options) +{ +#ifdef __DEBUG__ + printk("(pid %d) sys_waitpid(%d, 0x%08x, %d)\n", current->pid, pid, *status, options); +#endif /*__DEBUG__ */ + return sys_wait4(pid, status, options, NULL); +} diff --git a/kernel/syscalls/write.c b/kernel/syscalls/write.c new file mode 100644 index 00000000..969f952a --- /dev/null +++ b/kernel/syscalls/write.c @@ -0,0 +1,49 @@ +/* + * fiwix/kernel/syscalls/write.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +#ifdef __DEBUG__ +#include +#include +#endif /*__DEBUG__ */ + +int sys_write(unsigned int ufd, const char *buf, int count) +{ + struct inode *i; + int errno; + +#ifdef __DEBUG__ +/* printk("(pid %d) sys_write(%d, '%s', %d)\n", current->pid, ufd, buf, count);*/ + printk("(pid %d) sys_write(%d, 0x%08x, %d) -> ", current->pid, ufd, buf, count); +#endif /*__DEBUG__ */ + + CHECK_UFD(ufd); + if((errno = check_user_area(VERIFY_READ, buf, count))) { + return errno; + } + if(fd_table[current->fd[ufd]].flags & O_RDONLY) { + return -EBADF; + } + if(!count) { + return 0; + } + if(count < 0) { + return -EINVAL; + } + i = fd_table[current->fd[ufd]].inode; + if(i->fsop && i->fsop->write) { + errno = i->fsop->write(i, &fd_table[current->fd[ufd]], buf, count); +#ifdef __DEBUG__ + printk("%d\n", errno); +#endif /*__DEBUG__ */ + return errno; + } + return -EINVAL; +} diff --git a/kernel/timer.c b/kernel/timer.c new file mode 100644 index 00000000..b5b58a76 --- /dev/null +++ b/kernel/timer.c @@ -0,0 +1,451 @@ +/* + * fiwix/kernel/timer.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * timer.c implements a callout table using a singly linked list. + * + * head + * +---------+ ----------+ ... ----------+ + * |data|next| |data|next| ... |data|next| + * | | --> | | --> ... | | / | + * +---------+ ----------+ ... ----------+ + * (callout) (callout) (callout) + */ + +struct callout callout_pool[NR_CALLOUTS]; +struct callout *callout_pool_head; +struct callout *callout_head; + +static char month[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +unsigned int avenrun[3] = { 0, 0, 0 }; + +static unsigned int count_active_procs(void) +{ + int counter; + struct proc *p; + + counter = 0; + FOR_EACH_PROCESS(p) { + if(p->state == PROC_RUNNING) { + counter += FIXED_1; + } + } + return counter; +} + +static void calc_load(void) +{ + unsigned int active_procs; + static int count = LOAD_FREQ; + + if(count-- > 0) { + return; + } + + count = LOAD_FREQ; + active_procs = count_active_procs(); + CALC_LOAD(avenrun[0], EXP_1, active_procs); + CALC_LOAD(avenrun[1], EXP_5, active_procs); + CALC_LOAD(avenrun[2], EXP_15, active_procs); +} + +static struct callout *get_free_callout(void) +{ + struct callout *new; + + new = NULL; + if(callout_pool_head) { + new = callout_pool_head; + callout_pool_head = callout_pool_head->next; + new->next = NULL; + } + return new; +} + +static void put_free_callout(struct callout *old) +{ + old->next = callout_pool_head; + callout_pool_head = old; +} + +static void do_del_callout(struct callout *c) +{ + struct callout **tmp; + + if(callout_head) { + tmp = &callout_head; + while(*tmp) { + if((*tmp) == c) { + if((*tmp)->next != NULL) { + *tmp = (*tmp)->next; + (*tmp)->expires += c->expires; + } else { + *tmp = NULL; + } + put_free_callout(c); + break; + } + tmp = &(*tmp)->next; + } + } + return; +} + +void add_callout(struct callout_req *creq, unsigned int ticks) +{ + unsigned long int flags; + struct callout *c, **tmp; + + del_callout(creq); + SAVE_FLAGS(flags); CLI(); + + if(!(c = get_free_callout())) { + printk("WARNING: %s(): no more callout slots!\n", __FUNCTION__); + RESTORE_FLAGS(flags); + return; + } + + /* setup the new callout */ + memset_b(c, NULL, sizeof(struct callout)); + c->expires = ticks; + c->fn = creq->fn; + c->arg = creq->arg; + + if(!callout_head) { + callout_head = c; + } else { + tmp = &callout_head; + while(*tmp) { + if((*tmp)->expires > c->expires) { + (*tmp)->expires -= c->expires; + c->next = *tmp; + break; + } + c->expires -= (*tmp)->expires; + tmp = &(*tmp)->next; + } + *tmp = c; + } + RESTORE_FLAGS(flags); + return; +} + +void del_callout(struct callout_req *creq) +{ + unsigned long int flags; + struct callout *c; + + SAVE_FLAGS(flags); CLI(); + c = callout_head; + while(c) { + if(c->fn == creq->fn && c->arg == creq->arg) { + do_del_callout(c); + break; + } + c = c->next; + } + RESTORE_FLAGS(flags); + return; +} + +void do_timer(struct sigcontext *sc) +{ + if((++kstat.ticks % HZ) == 0) { + CURRENT_TIME++; + kstat.uptime++; + } + + add_bh(timer_bh); + + /* FIXME: put this in 'timer_bh' */ + if(sc->cs == KERNEL_CS) { + current->usage.ru_stime.tv_usec += TICK; + if(current->usage.ru_stime.tv_usec >= 1000000) { + current->usage.ru_stime.tv_sec++; + current->usage.ru_stime.tv_usec -= 1000000; + } + if(current->pid != IDLE) { + kstat.cpu_system++; + } + } else { + current->usage.ru_utime.tv_usec += TICK; + if(current->usage.ru_utime.tv_usec >= 1000000) { + current->usage.ru_utime.tv_sec++; + current->usage.ru_utime.tv_usec -= 1000000; + } + if(current->pid != IDLE) { + kstat.cpu_user++; + } + if(current->it_virt_value > 0) { + current->it_virt_value--; + if(!current->it_virt_value) { + current->it_virt_value = current->it_virt_interval; + send_sig(current, SIGVTALRM); + } + } + } +} + +unsigned long int tv2ticks(const struct timeval *tv) +{ + return((tv->tv_sec * HZ) + tv->tv_usec * HZ / 1000000); +} + +void ticks2tv(long int ticks, struct timeval *tv) +{ + tv->tv_sec = ticks / HZ; + tv->tv_usec = (ticks % HZ) * 1000000 / HZ; + return; +} + +int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value) +{ + switch(which) { + case ITIMER_REAL: + if((unsigned int)old_value) { + ticks2tv(current->it_real_interval, &old_value->it_interval); + ticks2tv(current->it_real_value, &old_value->it_value); + } + current->it_real_interval = tv2ticks(&new_value->it_interval); + current->it_real_value = tv2ticks(&new_value->it_value); + break; + case ITIMER_VIRTUAL: + if((unsigned int)old_value) { + ticks2tv(current->it_virt_interval, &old_value->it_interval); + ticks2tv(current->it_virt_value, &old_value->it_value); + } + current->it_virt_interval = tv2ticks(&new_value->it_interval); + current->it_virt_value = tv2ticks(&new_value->it_value); + break; + case ITIMER_PROF: + if((unsigned int)old_value) { + ticks2tv(current->it_prof_interval, &old_value->it_interval); + ticks2tv(current->it_prof_value, &old_value->it_value); + } + current->it_prof_interval = tv2ticks(&new_value->it_interval); + current->it_prof_value = tv2ticks(&new_value->it_value); + break; + default: + return -EINVAL; + } + + return 0; +} + +unsigned long int mktime(struct mt *mt) +{ + int n, total_days; + unsigned long int seconds; + + total_days = 0; + + for(n = UNIX_EPOCH; n < mt->mt_year; n++) { + total_days += DAYS_PER_YEAR(n); + } + for(n = 0; n < (mt->mt_month - 1); n++) { + total_days += month[n]; + if(n == 1) { + total_days += LEAP_YEAR(mt->mt_year) ? 1 : 0; + } + } + + total_days += (mt->mt_day - 1); + seconds = total_days * SECS_PER_DAY; + seconds += mt->mt_hour * SECS_PER_HOUR; + seconds += mt->mt_min * SECS_PER_MIN; + seconds += mt->mt_sec; + return seconds; +} + +void timer_bh(void) +{ + struct proc *p; + + if(current->usage.ru_utime.tv_sec + current->usage.ru_stime.tv_sec > current->rlim[RLIMIT_CPU].rlim_cur) { + send_sig(current, SIGXCPU); + } + + if(current->it_prof_value > 0) { + current->it_prof_value--; + if(!current->it_prof_value) { + current->it_prof_value = current->it_prof_interval; + send_sig(current, SIGPROF); + } + } + + calc_load(); + FOR_EACH_PROCESS(p) { + if(!p->state) { + continue; + } + if(p->timeout > 0 && p->timeout < INFINITE_WAIT) { + p->timeout--; + if(!p->timeout) { + wakeup_proc(p); + } + } + if(p->it_real_value > 0) { + p->it_real_value--; + if(!p->it_real_value) { + p->it_real_value = p->it_real_interval; + send_sig(p, SIGALRM); + } + } + } + + /* callouts */ + if(callout_head) { + if(callout_head->expires > 0) { + callout_head->expires--; + if(!callout_head->expires) { + add_bh(callouts_bh); + } + } else { + printk("%s(): callout losing ticks.\n", __FUNCTION__); + add_bh(callouts_bh); + } + } + + if(current->pid > IDLE && --current->cpu_count <= 0) { + current->cpu_count = 0; + need_resched = 1; + } +} + +void callouts_bh(void) +{ + struct callout *c; + void (*fn)(unsigned int); + unsigned int arg; + + while(callout_head) { + if(callout_head->expires) { + break; + } + if(lock_area(AREA_CALLOUT)) { + continue; + } + fn = callout_head->fn; + arg = callout_head->arg; + c = callout_head; + callout_head = callout_head->next; + put_free_callout(c); + unlock_area(AREA_CALLOUT); + fn(arg); + } +} + +void get_system_time(void) +{ + short int cmos_century; + struct mt mt; + + /* read date and time from CMOS */ + mt.mt_sec = cmos_read_date(CMOS_SEC); + mt.mt_min = cmos_read_date(CMOS_MIN); + mt.mt_hour = cmos_read_date(CMOS_HOUR); + mt.mt_day = cmos_read_date(CMOS_DAY); + mt.mt_month = cmos_read_date(CMOS_MONTH); + mt.mt_year = cmos_read_date(CMOS_YEAR); + cmos_century = cmos_read_date(CMOS_CENTURY); + mt.mt_year += cmos_century * 100; + + kstat.boot_time = CURRENT_TIME = mktime(&mt); +} + +void set_system_time(__time_t t) +{ + int sec, spm, min, hour, d, m, y; + + sec = t; + y = 1970; + while(sec >= (DAYS_PER_YEAR(y) * SECS_PER_DAY)) { + sec -= (DAYS_PER_YEAR(y) * SECS_PER_DAY); + y++; + } + + m = 0; + while(sec > month[m] * SECS_PER_DAY) { + spm = month[m] * SECS_PER_DAY; + if(m == 1) { + spm = LEAP_YEAR(y) ? spm + SECS_PER_DAY : spm; + } + sec -= spm; + m++; + } + m++; + + d = 1; + while(sec >= SECS_PER_DAY) { + sec -= SECS_PER_DAY; + d++; + } + + hour = 0; + while(sec >= SECS_PER_HOUR) { + sec -= SECS_PER_HOUR; + hour++; + } + + min = 0; + while(sec >= SECS_PER_MIN) { + sec -= SECS_PER_MIN; + min++; + } + + /* write date and time to CMOS */ + cmos_write_date(CMOS_SEC, sec); + cmos_write_date(CMOS_MIN, min); + cmos_write_date(CMOS_HOUR, hour); + cmos_write_date(CMOS_DAY, d); + cmos_write_date(CMOS_MONTH, m); + cmos_write_date(CMOS_YEAR, y % 100); + cmos_write_date(CMOS_CENTURY, (y - (y % 100)) / 100); + + CURRENT_TIME = t; +} + +void timer_init(void) +{ + int n; + struct callout *c; + + pit_init(HZ); + memset_b(callout_pool, NULL, sizeof(callout_pool)); + + /* callout free list initialization */ + callout_pool_head = NULL; + n = NR_CALLOUTS; + while(n--) { + c = &callout_pool[n]; + put_free_callout(c); + } + callout_head = NULL; + + printk("clock - %d type=PIT Hz=%d\n", TIMER_IRQ, HZ); + if(!register_irq(TIMER_IRQ, "timer", do_timer)) { + enable_irq(TIMER_IRQ); + } +} diff --git a/kernel/traps.c b/kernel/traps.c new file mode 100644 index 00000000..5fbfcbb4 --- /dev/null +++ b/kernel/traps.c @@ -0,0 +1,341 @@ +/* + * fiwix/kernel/traps.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * PS/2 System Control Port B + * --------------------------------------- + * bit 7 -> system board RAM parity check + * bit 6 -> channel check + * bit 5 -> timer 2 (speaker time) output + * bit 4 -> refresh request (toggle) + * bit 3 -> channel check status + * bit 2 -> parity check status + * bit 1 -> speaker data status + * bit 0 -> timer 2 gate to speaker status + */ +#define PS2_SYSCTRL_B 0x61 /* PS/2 system control port B (read) */ + +struct traps traps_table[NR_EXCEPTIONS] = { + { "Divide Error", do_divide_error, 0 }, + { "Debug", do_debug, 0 }, + { "NMI Interrupt", do_nmi_interrupt, 0 }, + { "Breakpoint", do_breakpoint, 0 }, + { "Overflow" , do_overflow, 0 }, + { "BOUND Range Exceeded", do_bound, 0 }, + { "Invalid Opcode", do_invalid_opcode, 0 }, + { "Device Not Available (No Math Coprocessor)", do_no_math_coprocessor, 0 }, + { "Double Fault", do_double_fault, 1 }, + { "Coprocessor Segment Overrun", do_coprocessor_segment_overrun, 0 }, + { "Invalid TSS", do_invalid_tss, 1 }, + { "Segment Not Present", do_segment_not_present, 1 }, + { "Stack-Segment Fault", do_stack_segment_fault, 1 }, + { "General Protection", do_general_protection, 1 }, + { "Page Fault", do_page_fault, 1 }, + { "Intel reserved", do_reserved, 0 }, + { "x87 FPU Floating-Point Error", do_floating_point_error, 0 }, + { "Alignment Check", do_alignment_check, 1 }, + { "Machine Check", do_machine_check, 0 }, + { "SIMD Floating-Point Exception", do_simd_fault, 0 }, + { "Intel reserved", do_reserved, 0 }, + { "Intel reserved", do_reserved, 0 }, + { "Intel reserved", do_reserved, 0 }, + { "Intel reserved", do_reserved, 0 }, + { "Intel reserved", do_reserved, 0 }, + { "Intel reserved", do_reserved, 0 }, + { "Intel reserved", do_reserved, 0 }, + { "Intel reserved", do_reserved, 0 }, + { "Intel reserved", do_reserved, 0 }, + { "Intel reserved", do_reserved, 0 }, + { "Intel reserved", do_reserved, 0 }, + { "Intel reserved", do_reserved, 0 } +}; + +void do_divide_error(unsigned int trap, struct sigcontext *sc) +{ + if(dump_registers(trap, sc)) { + PANIC(""); + } + send_sig(current, SIGFPE); + return; +} + +void do_debug(unsigned int trap, struct sigcontext *sc) +{ + if(dump_registers(trap, sc)) { + PANIC(""); + } + send_sig(current, SIGTRAP); + return; +} + +void do_nmi_interrupt(unsigned int trap, struct sigcontext *sc) +{ + unsigned char error; + + error = inport_b(PS2_SYSCTRL_B); + + printk("NMI received: ", error); + switch(error) { + case 0x80: + printk("parity check occurred. Defective RAM chips?\n"); + break; + default: + printk("unknown error 0x%x\n", error); + break; + } + + if(dump_registers(trap, sc)) { + PANIC(""); + } + send_sig(current, SIGSEGV); + return; +} + +void do_breakpoint(unsigned int trap, struct sigcontext *sc) +{ + if(dump_registers(trap, sc)) { + PANIC(""); + } + send_sig(current, SIGTRAP); + return; +} + +void do_overflow(unsigned int trap, struct sigcontext *sc) +{ + if(dump_registers(trap, sc)) { + PANIC(""); + } + send_sig(current, SIGSEGV); + return; +} + +void do_bound(unsigned int trap, struct sigcontext *sc) +{ + if(dump_registers(trap, sc)) { + PANIC(""); + } + send_sig(current, SIGSEGV); + return; +} + +void do_invalid_opcode(unsigned int trap, struct sigcontext *sc) +{ + if(dump_registers(trap, sc)) { + PANIC(""); + } + send_sig(current, SIGILL); + return; +} + +void do_no_math_coprocessor(unsigned int trap, struct sigcontext *sc) +{ + /* floating-point emulation would go here */ + + if(dump_registers(trap, sc)) { + PANIC("No coprocessor/emulation found.\n"); + } + send_sig(current, SIGILL); + return; +} + +void do_double_fault(unsigned int trap, struct sigcontext *sc) +{ + if(dump_registers(trap, sc)) { + PANIC(""); + } + send_sig(current, SIGSEGV); + return; +} + +void do_coprocessor_segment_overrun(unsigned int trap, struct sigcontext *sc) +{ + if(dump_registers(trap, sc)) { + PANIC(""); + } + send_sig(current, SIGFPE); + return; +} + +void do_invalid_tss(unsigned int trap, struct sigcontext *sc) +{ + if(dump_registers(trap, sc)) { + PANIC(""); + } + send_sig(current, SIGSEGV); + return; +} + +void do_segment_not_present(unsigned int trap, struct sigcontext *sc) +{ + if(dump_registers(trap, sc)) { + PANIC(""); + } + send_sig(current, SIGBUS); + return; +} + +void do_stack_segment_fault(unsigned int trap, struct sigcontext *sc) +{ + if(dump_registers(trap, sc)) { + PANIC(""); + } + send_sig(current, SIGBUS); + return; +} + +void do_general_protection(unsigned int trap, struct sigcontext *sc) +{ + if(dump_registers(trap, sc)) { + PANIC(""); + } + send_sig(current, SIGSEGV); + return; +} + +/* do_page_fault() resides in mm/fault.c */ + +void do_reserved(unsigned int trap, struct sigcontext *sc) +{ + if(dump_registers(trap, sc)) { + PANIC(""); + } + send_sig(current, SIGSEGV); + return; +} + +void do_floating_point_error(unsigned int trap, struct sigcontext *sc) +{ + if(dump_registers(trap, sc)) { + PANIC(""); + } + send_sig(current, SIGFPE); + return; +} + +void do_alignment_check(unsigned int trap, struct sigcontext *sc) +{ + if(dump_registers(trap, sc)) { + PANIC(""); + } + send_sig(current, SIGSEGV); + return; +} + +void do_machine_check(unsigned int trap, struct sigcontext *sc) +{ + if(dump_registers(trap, sc)) { + PANIC(""); + } + send_sig(current, SIGSEGV); + return; +} + +void do_simd_fault(unsigned int trap, struct sigcontext *sc) +{ + if(dump_registers(trap, sc)) { + PANIC(""); + } + send_sig(current, SIGSEGV); + return; +} + +void trap_handler(unsigned int trap, struct sigcontext sc) +{ + traps_table[trap].handler(trap, &sc); + + /* avoids confusion with -RESTART return value */ + sc.err = -sc.err; +} + +static const char * elf_lookup_symbol(unsigned int addr) +{ + Elf32_Sym *sym; + unsigned int n; + + sym = (Elf32_Sym *)symtab->sh_addr; + for(n = 0; n < symtab->sh_size / sizeof(Elf32_Sym); n++, sym++) { + if(ELF32_ST_TYPE(sym->st_info) != STT_FUNC) { + continue; + } + if(addr >= sym->st_value && addr < (sym->st_value + sym->st_size)) { + return (const char *)strtab->sh_addr + sym->st_name; + } + } + return NULL; +} + +int dump_registers(unsigned int trap, struct sigcontext *sc) +{ + unsigned int cr2, addr, n; + unsigned int *sp; + const char *str; + + printk("\n"); + if(trap == 14) { /* Page Fault */ + GET_CR2(cr2); + printk("%s at 0x%08x (%s) with error code 0x%08x (0b%b)\n", traps_table[trap].name, cr2, sc->err & PFAULT_W ? "writing" : "reading", sc->err, sc->err); + } else { + printk("EXCEPTION: %s", traps_table[trap].name); + if(traps_table[trap].errcode) { + printk(": error code 0x%08x (0b%b)", sc->err, sc->err); + } + printk("\n"); + } + + printk("Process '%s' with pid %d", current->argv0, current->pid); + if(sc->cs == KERNEL_CS) { + printk(" in '%s()'.", elf_lookup_symbol(sc->eip)); + } + printk("\n"); + + printk(" cs: 0x%08x\teip: 0x%08x\tefl: 0x%08x\t ss: 0x%08x\tesp: 0x%08x\n", sc->cs, sc->eip, sc->eflags, sc->oldss, sc->oldesp); + printk("eax: 0x%08x\tebx: 0x%08x\tecx: 0x%08x\tedx: 0x%08x\n", sc->eax, sc->ebx, sc->ecx, sc->edx); + printk("esi: 0x%08x\tedi: 0x%08x\tesp: 0x%08x\tebp: 0x%08x\n", sc->esi, sc->edi, sc->esp, sc->ebp); + printk(" ds: 0x%08x\t es: 0x%08x\t fs: 0x%08x\t gs: 0x%08x\n", sc->ds, sc->es, sc->fs, sc->gs); + + if(sc->cs == KERNEL_CS) { + printk("Stack:\n"); + GET_ESP(sp); + sp = (unsigned int *)sp; + for(n = 1; n <= 32; n++) { + printk(" %08x", *sp); + sp++; + if(!(n % 8)) { + printk("\n"); + } + } + printk("Backtrace:\n"); + GET_ESP(sp); + sp = (unsigned int *)sp; + for(n = 0; n < 128; n++) { + addr = *sp; + str = elf_lookup_symbol(addr); + if(str) { + printk("<0x%08x> %s()\n", addr, str); + } + sp++; + } + } + + /* panics if the exception has been in kernel mode */ + if(current->flags & PF_KPROC || sc->cs == KERNEL_CS) { + return 1; + } + + return 0; +} diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 00000000..c404454f --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,19 @@ +# fiwix/lib/Makefile +# +# Copyright 2018, Jordi Sanfeliu. All rights reserved. +# Distributed under the terms of the Fiwix License. +# + +.S.o: + $(CC) -traditional -I$(INCLUDE) -c -o $@ $< +.c.o: + $(CC) $(CFLAGS) -c -o $@ $< + +OBJS = ctype.o strings.o printk.o + +lib: $(OBJS) + $(LD) $(LDFLAGS) -r $(OBJS) -o lib.o + +clean: + rm -f *.o + diff --git a/lib/ctype.c b/lib/ctype.c new file mode 100644 index 00000000..a76493b0 --- /dev/null +++ b/lib/ctype.c @@ -0,0 +1,140 @@ +/* + * fiwix/lib/ctype.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include + +unsigned char _ctype[] = { + 0, + _C, /* ^@ 0x00 (NUL '\0') */ + _C, /* ^A 0x01 (SOH) */ + _C, /* ^B 0x02 (STX) */ + _C, /* ^C 0x03 (ETX) */ + _C, /* ^D 0x04 (EOT) */ + _C, /* ^E 0x05 (ENQ) */ + _C, /* ^F 0x06 (ACK) */ + _C, /* ^G 0x07 (BEL '\a') */ + _C, /* ^H 0x08 (BS '\b') */ + _C | _S, /* ^I 0x09 (HT '\t') */ + _C | _S, /* ^J 0x0A (LF '\n') */ + _C | _S, /* ^K 0x0B (VT '\v') */ + _C | _S, /* ^L 0x0C (FF '\f') */ + _C | _S, /* ^M 0x0D (CR '\r') */ + _C, /* ^N 0x0E (SO) */ + _C, /* ^O 0x0F (SI) */ + _C, /* ^P 0x10 (DLE) */ + _C, /* ^Q 0x11 (DC1) */ + _C, /* ^R 0x12 (DC2) */ + _C, /* ^S 0x13 (DC3) */ + _C, /* ^T 0x14 (DC4) */ + _C, /* ^U 0x15 (NAK) */ + _C, /* ^V 0x16 (SYN) */ + _C, /* ^W 0x17 (ETB) */ + _C, /* ^X 0x18 (CAN) */ + _C, /* ^Y 0x19 (EM) */ + _C, /* ^Z 0x1A (SUB) */ + _C, /* ^[ 0x1B (ESC) */ + _C, /* ^\ 0x1C (FS) */ + _C, /* ^] 0x1D (GS) */ + _C, /* ^^ 0x1E (RS) */ + _C, /* ^_ 0x1F (US) */ + _S, /* ' ' 0x20 */ + _P, /* '!' 0x21 */ + _P, /* '"' 0x22 */ + _P, /* '#' 0x23 */ + _P, /* '$' 0x24 */ + _P, /* '%' 0x25 */ + _P, /* '&' 0x26 */ + _P, /* ''' 0x27 */ + _P, /* '(' 0x28 */ + _P, /* ')' 0x29 */ + _P, /* '*' 0x2A */ + _P, /* '+' 0x2B */ + _P, /* ',' 0x2C */ + _P, /* '-' 0x2D */ + _P, /* '.' 0x2E */ + _P, /* '/' 0x2F */ + _N, /* '0' 0x30 */ + _N, /* '1' 0x31 */ + _N, /* '2' 0x32 */ + _N, /* '3' 0x33 */ + _N, /* '4' 0x34 */ + _N, /* '5' 0x35 */ + _N, /* '6' 0x36 */ + _N, /* '7' 0x37 */ + _N, /* '8' 0x38 */ + _N, /* '9' 0x39 */ + _P, /* ':' 0x3A */ + _P, /* ';' 0x3B */ + _P, /* '<' 0x3C */ + _P, /* '=' 0x3D */ + _P, /* '>' 0x3E */ + _P, /* '?' 0x3F */ + _P, /* '@' 0x40 */ + _U | _X, /* 'A' 0x41 */ + _U | _X, /* 'B' 0x42 */ + _U | _X, /* 'C' 0x43 */ + _U | _X, /* 'D' 0x44 */ + _U | _X, /* 'E' 0x45 */ + _U | _X, /* 'F' 0x46 */ + _U, /* 'G' 0x47 */ + _U, /* 'H' 0x48 */ + _U, /* 'I' 0x49 */ + _U, /* 'J' 0x4A */ + _U, /* 'K' 0x4B */ + _U, /* 'L' 0x4C */ + _U, /* 'M' 0x4D */ + _U, /* 'N' 0x4E */ + _U, /* 'O' 0x4F */ + _U, /* 'P' 0x50 */ + _U, /* 'Q' 0x51 */ + _U, /* 'R' 0x52 */ + _U, /* 'S' 0x53 */ + _U, /* 'T' 0x54 */ + _U, /* 'U' 0x55 */ + _U, /* 'V' 0x56 */ + _U, /* 'W' 0x57 */ + _U, /* 'X' 0x58 */ + _U, /* 'Y' 0x59 */ + _U, /* 'Z' 0x5A */ + _P, /* '[' 0x5B */ + _P, /* '\' 0x5C */ + _P, /* ']' 0x5D */ + _P, /* '^' 0x5E */ + _P, /* '_' 0x5F */ + _P, /* '`' 0x60 */ + _L | _X, /* 'a' 0x61 */ + _L | _X, /* 'b' 0x62 */ + _L | _X, /* 'c' 0x63 */ + _L | _X, /* 'd' 0x64 */ + _L | _X, /* 'e' 0x65 */ + _L | _X, /* 'f' 0x66 */ + _L, /* 'g' 0x67 */ + _L, /* 'h' 0x68 */ + _L, /* 'i' 0x69 */ + _L, /* 'j' 0x6A */ + _L, /* 'k' 0x6B */ + _L, /* 'l' 0x6C */ + _L, /* 'm' 0x6D */ + _L, /* 'n' 0x6E */ + _L, /* 'o' 0x6F */ + _L, /* 'p' 0x70 */ + _L, /* 'q' 0x71 */ + _L, /* 'r' 0x72 */ + _L, /* 's' 0x73 */ + _L, /* 't' 0x74 */ + _L, /* 'u' 0x75 */ + _L, /* 'v' 0x76 */ + _L, /* 'w' 0x77 */ + _L, /* 'x' 0x78 */ + _L, /* 'y' 0x79 */ + _L, /* 'z' 0x7A */ + _P, /* '{' 0x7B */ + _P, /* '|' 0x7C */ + _P, /* '}' 0x7D */ + _P, /* '~' 0x7E */ + _C, /* DEL 0x7F */ +}; diff --git a/lib/printk.c b/lib/printk.c new file mode 100644 index 00000000..d9443df5 --- /dev/null +++ b/lib/printk.c @@ -0,0 +1,369 @@ +/* + * fiwix/lib/printk.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +#define LOG_BUF_LEN 4096 +#define MAX_BUF 1024 /* printk() and sprintk() size limit */ + +static char log_buf[LOG_BUF_LEN]; +static unsigned int log_start; + +static void puts(char *buffer) +{ + struct tty *tty; + unsigned short int count; + char *b; + + /* for special debugging purposes only (X11, SVGAlib, ...) + { + struct inode dummy_i; + memset_b(&dummy_i, 0, sizeof(struct inode)); + dummy_i.dev = dummy_i.rdev = 0x0600; // /dev/lp0 + lp_write(&dummy_i, NULL, buffer, strlen(buffer)); + } + */ + + tty = get_tty(_syscondev); + count = strlen(buffer); + b = buffer; + + while(count--) { + if(!tty) { + if(log_start < LOG_BUF_LEN) { + log_buf[log_start++] = *(b++); + } + } else { + tty_queue_putchar(tty, &tty->write_q, *(b++)); + + /* kernel messages must be shown immediately */ + tty->output(tty); + } + } +} + +/* + * format identifiers + * -------------------------------------------------------- + * %d decimal conversion + * %u unsigned decimal conversion + * %x hexadecimal conversion (lower case) + * %X hexadecimal conversion (upper case) + * %b binary conversion + * %o octal conversion + * %c character + * %s string + * + * flags + * -------------------------------------------------------- + * 0 result is padded with zeros (e.g.: '%06d') + * (maximum value is 32) + * blank result is padded with spaces (e.g.: '% 6d') + * (maximum value is 32) + * - the numeric result is left-justified + * (default is right-justified) + */ +static void do_printk(char *buffer, const char *format, va_list args) +{ + char sw_neg, in_identifier, n_pad, lf; + char ch_pad, basecase, c; + char str[] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL + }; + char nullstr[7] = { '<', 'N', 'U', 'L', 'L', '>', '\0' }; + char *ptr_s, *p; + int num, count; + char simplechar; + unsigned int unum, digit; + + sw_neg = in_identifier = n_pad = lf = 0; + count = 0; + basecase = 'A'; + ch_pad = ' '; + p = NULL; + + /* assumes buffer has a maximum size of MAX_BUF */ + while((c = *(format++)) && count < MAX_BUF) { + if((c != '%') && !in_identifier) { + *(buffer++) = c; + memset_b(str, NULL, 32); + } else { + in_identifier = 1; + switch(c = *(format)) { + case 'd': + num = va_arg(args, int); + if(num < 0) { + num *= -1; + sw_neg = 1; + } + ptr_s = str; + do { + *(ptr_s++) = '0' + (num % 10); + } while(num /= 10); + if(lf) { + p = ptr_s; + } else { + while(*ptr_s) { + ptr_s++; + } + } + if(sw_neg) { + sw_neg = 0; + *(ptr_s++) = '-'; + } + do { + *(buffer++) = *(--ptr_s); + count++; + } while(ptr_s != str && count < MAX_BUF); + if(lf) { + while(*p && count < MAX_BUF) { + *(buffer++) = *(p++); + count++; + } + } + format++; + ch_pad = ' '; + n_pad = 0; + in_identifier = 0; + lf = 0; + break; + + case 'u': + unum = va_arg(args, unsigned int); + ptr_s = str; + do { + *(ptr_s++) = '0' + (unum % 10); + } while(unum /= 10); + if(lf) { + p = ptr_s; + } else { + while(*ptr_s) { + ptr_s++; + } + } + do { + *(buffer++) = *(--ptr_s); + count++; + } while(ptr_s != str && count < MAX_BUF); + if(lf) { + while(*p && count < MAX_BUF) { + *(buffer++) = *(p++); + count++; + } + } + format++; + ch_pad = ' '; + n_pad = 0; + in_identifier = 0; + lf = 0; + break; + + case 'x': + basecase = 'a'; + case 'X': + unum = va_arg(args, unsigned int); + ptr_s = str; + do { + *(ptr_s++) = (digit = (unum & 0x0F)) > 9 ? basecase + digit - 10 : '0' + digit; + } while(unum /= 16); + if(lf) { + p = ptr_s; + } else { + while(*ptr_s) { + ptr_s++; + } + } + do { + *(buffer++) = *(--ptr_s); + count++; + } while(ptr_s != str && count < MAX_BUF); + if(lf) { + while(*p && count < MAX_BUF) { + *(buffer++) = *(p++); + count++; + } + } + format++; + ch_pad = ' '; + n_pad = 0; + in_identifier = 0; + lf = 0; + break; + + case 'b': + num = va_arg(args, unsigned int); + if(num < 0) { + num *= -1; + } + ptr_s = str; + do { + *(ptr_s++) = '0' + (num % 2); + } while(num /= 2); + if(lf) { + p = ptr_s; + } else { + while(*ptr_s) { + ptr_s++; + } + } + do { + *(buffer++) = *(--ptr_s); + count++; + } while(ptr_s != str && count < MAX_BUF); + if(lf) { + while(*p && count < MAX_BUF) { + *(buffer++) = *(p++); + count++; + } + } + format++; + ch_pad = ' '; + n_pad = 0; + in_identifier = 0; + lf = 0; + break; + + case 'o': + num = va_arg(args, unsigned int); + if(num < 0) { + num *= -1; + } + ptr_s = str; + do { + *(ptr_s++) = '0' + (num % 8); + } while(num /= 8); + if(lf) { + p = ptr_s; + } else { + while(*ptr_s) { + ptr_s++; + } + } + do { + *(buffer++) = *(--ptr_s); + count++; + } while(ptr_s != str && count < MAX_BUF); + if(lf) { + while(*p && count < MAX_BUF) { + *(buffer++) = *(p++); + count++; + } + } + format++; + ch_pad = ' '; + n_pad = 0; + in_identifier = 0; + lf = 0; + break; + + case 'c': + simplechar = va_arg(args, int); + *(buffer++) = simplechar; + format++; + in_identifier = 0; + lf = 0; + break; + + case 's': + num = 0; + ptr_s = va_arg(args, char *); + if(n_pad) { + num = n_pad - strlen(ptr_s); + if(num < 0) { + num *= -1; + } + } + /* if it's a NULL then show "" */ + if(ptr_s == NULL) { + ptr_s = (char *)nullstr; + } + while((c = *(ptr_s++)) && count < MAX_BUF) { + *(buffer++) = c; + count++; + } + while(num-- && count < MAX_BUF) { + *(buffer++) = ' '; + count++; + } + format++; + n_pad = 0; + in_identifier = 0; + lf = 0; + break; + + case ' ': + ch_pad = ' '; + break; + + case '0': + if(!n_pad) { + ch_pad = '0'; + } + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + n_pad = !n_pad ? c - '0': ((n_pad * 10) + (c - '0')); + n_pad = n_pad > 32 ? 32 : n_pad; + for(unum = 0; unum < n_pad; unum++) { + str[unum] = ch_pad; + } + break; + + case '-': + lf = 1; + break; + case '%': + *(buffer++) = c; + format++; + in_identifier = 0; + break; + } + } + count++; + } + *buffer = NULL; +} + +void register_console(void (*fn)(char *, unsigned int)) +{ + (*fn)(log_buf, log_start); +} + +void printk(const char *format, ...) +{ + va_list args; + char buffer[MAX_BUF]; + + va_start(args, format); + do_printk(buffer, format, args); + puts(buffer); + va_end(args); +} + +int sprintk(char *buffer, const char *format, ...) +{ + va_list args; + + va_start(args, format); + do_printk(buffer, format, args); + va_end(args); + return strlen(buffer); +} diff --git a/lib/strings.c b/lib/strings.c new file mode 100644 index 00000000..06f24d91 --- /dev/null +++ b/lib/strings.c @@ -0,0 +1,280 @@ +/* + * fiwix/lib/strings.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +/* convert from big-endian to little-endian (word swap) */ +void swap_asc_word(char *str, int len) +{ + int n, n2; + short int *ptr; + char *buf; + + if(!(buf = (void *)kmalloc())) { + return; + } + + ptr = (short int *)str; + + for(n = 0, n2 = 0; n < len; n++) { + buf[n2++] = *ptr >> 8; + buf[n2++] = *ptr & 0xFF; + ptr++; + } + for(n = len - 1; n > 0; n--) { + if(buf[n] == NULL || buf[n] == ' ') { + buf[n] = NULL; + } else { + break; + } + } + memcpy_b(str, buf, len); + kfree((unsigned int)buf); +} + +int strcmp(const char *str1, const char *str2) +{ + while(*str1) { + if(*str1 != *str2) { + return 1; + } + str1++; + str2++; + } + if(!(*str2)) { + return 0; + } + return 1; +} + +int strncmp(const char *str1, const char *str2, __ssize_t n) +{ + while(n > 0) { + if(*str1 != *str2) { + return 1; + } + str1++; + str2++; + n--; + } + return 0; +} + +char *strcpy(char *dest, const char *src) +{ + if(!dest || !src) { + return NULL; + } + + while(*src) { + *dest = *src; + dest++; + src++; + } + *dest = NULL; /* NULL-terminated */ + return dest; +} + +void strncpy(char *dest, const char *src, int len) +{ + if(!dest || !src) { + return; + } + + while((*src) && len) { + *dest = *src; + dest++; + src++; + len--; + } + *dest = NULL; /* NULL-terminated */ +} + +char *strcat(char *dest, const char *src) +{ + char *orig; + + orig = dest; + while(*dest) { + dest++; + } + while(*src) { + *dest = *src; + dest++; + src++; + } + *dest = NULL; + return orig; +} + +char *strncat(char *dest, const char *src, __ssize_t len) +{ + char *orig; + + orig = dest; + while(*dest) { + dest++; + } + while(*src && len) { + *dest = *src; + dest++; + src++; + len--; + } + *dest = NULL; + return orig; +} + +int strlen(const char *str) +{ + int n; + + n = 0; + while(str && *str) { + n++; + str++; + } + return n; +} + +char * get_basename(const char *path) +{ + char *basename; + char c; + + basename = NULL; + + while(path) { + while(*path == '/') { + path++; + } + if(*path != NULL) { + basename = (char *)path; + } + while((c = *(path++)) && (c != '/')); + if(!c) { + break; + } + } + return basename; +} + +char * remove_trailing_slash(char *path) +{ + char *p; + + p = path + (strlen(path) - 1); + while(p > path && *p == '/') { + *p = NULL; + p--; + } + return path; +} + +int is_dir(const char *path) +{ + while(*(path + 1)) { + path++; + } + if(*path == '/') { + return 1; + } + return 0; +} + +int atoi(const char *str) +{ + int n; + + n = 0; + while(IS_SPACE(*str)) { + str++; + } + while(IS_NUMERIC(*str)) { + n = (n * 10) + (*str++ - '0'); + } + return n; +} + +void memcpy_b(void *dest, const void *src, unsigned int count) +{ + unsigned char *d; + unsigned char *s; + + d = (unsigned char *)dest; + s = (unsigned char *)src; + while(count--) { + *d = *s; + d++; + s++; + } +} + +void memcpy_w(void *dest, const void *src, unsigned int count) +{ + unsigned short int *d; + unsigned short int *s; + + d = (unsigned short int *)dest; + s = (unsigned short int *)src; + while(count--) { + *d = *s; + d++; + s++; + } +} + +void memcpy_l(void *dest, const void *src, unsigned int count) +{ + unsigned int *d; + unsigned int *s; + + d = (unsigned int *)dest; + s = (unsigned int *)src; + while(count--) { + *d = *s; + d++; + s++; + } +} + +void memset_b(void *dest, unsigned char value, unsigned int count) +{ + unsigned char *d; + + d = (unsigned char *)dest; + while(count--) { + *d = value; + d++; + } +} + +void memset_w(void *dest, unsigned short int value, unsigned int count) +{ + unsigned short int *d; + + d = (unsigned short int *)dest; + while(count--) { + *d = value; + d++; + } +} + +void memset_l(void *dest, unsigned int value, unsigned int count) +{ + unsigned int *d; + + d = (unsigned int *)dest; + while(count--) { + *d = value; + d++; + } +} diff --git a/mm/Makefile b/mm/Makefile new file mode 100644 index 00000000..7d5dfc17 --- /dev/null +++ b/mm/Makefile @@ -0,0 +1,19 @@ +# fiwix/mm/Makefile +# +# Copyright 2018, Jordi Sanfeliu. All rights reserved. +# Distributed under the terms of the Fiwix License. +# + +.S.o: + $(CC) -traditional -I$(INCLUDE) -c -o $@ $< +.c.o: + $(CC) $(CFLAGS) -c -o $@ $< + +OBJS = bios_map.o memory.o page.o alloc.o fault.o mmap.o swapper.o + +mm: $(OBJS) + $(LD) $(LDFLAGS) -r $(OBJS) -o mm.o + +clean: + rm -f *.o + diff --git a/mm/alloc.c b/mm/alloc.c new file mode 100644 index 00000000..10f4c34d --- /dev/null +++ b/mm/alloc.c @@ -0,0 +1,35 @@ +/* + * fiwix/mm/alloc.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include + +/* + * The implementation of kernel memory allocation is extremely simple, it works + * with a granularity of PAGE_SIZE (4096 bytes). There is indeed a lot of room + * for improvements here. + */ +unsigned int kmalloc(void) +{ + struct page *pg; + unsigned int addr; + + if((pg = get_free_page())) { + addr = pg->page << PAGE_SHIFT; + return P2V(addr); + } + + /* out of memory! */ + return 0; +} + +void kfree(unsigned int addr) +{ + addr = V2P(addr); + release_page(addr >> PAGE_SHIFT); +} diff --git a/mm/bios_map.c b/mm/bios_map.c new file mode 100644 index 00000000..ce832757 --- /dev/null +++ b/mm/bios_map.c @@ -0,0 +1,90 @@ +/* + * fiwix/mm/bios_map.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include + +/* check if an specific address is available in the BIOS memory map */ +int addr_in_bios_map(unsigned int addr) +{ + int n; + struct bios_mem_map *bmm; + + bmm = &bios_mem_map[0]; + for(n = 0; n < NR_BIOS_MM_ENT; n++, bmm++) { + if(bmm->to && bmm->type == BIOS_MEM_AVAIL) { + if(addr >= bmm->from && addr < (bmm->to & PAGE_MASK)) { + return 1; + } + } + } + return 0; /* not in BIOS map or not available (reserved, ...) */ +} + +void bios_map_init(memory_map_t *bmmap_addr, unsigned long int bmmap_length) +{ + unsigned int n; + unsigned int mem_from, mem_to, len_low, len_high; + memory_map_t *bmmap; + char *bios_mem_type[] = { NULL, "available" , "reserved", + "ACPI Reclaim", "ACPI NVS", "unusable", + "disabled" }; + + bmmap = bmmap_addr; + if(bmmap) { + n = 0; + + while((unsigned int)bmmap < (unsigned int)bmmap_addr + bmmap_length) { + mem_from = (unsigned int)bmmap->base_addr_low; + len_low = (unsigned int)bmmap->length_low; + if((((mem_from >> 16) & 0xFFFF) + ((len_low >> 16) & 0xFFFF)) > 0xFFFF) { + mem_to = ~0; + len_high = (mem_from >> 16) + (len_low >> 16); + len_high >>= 16; + } else { + mem_to = mem_from + len_low; + len_high = 0; + } + printk("%s 0x%08X%08X-0x%08X%08X %s\n", + n ? " " : "memory", + bmmap->base_addr_high, bmmap->base_addr_low, + len_high, mem_from + len_low, + bios_mem_type[(int)bmmap->type]); + /* only memory addresses below 4GB are accepted */ + if(!bmmap->base_addr_high) { + if(n < NR_BIOS_MM_ENT && len_low) { + bios_mem_map[n].from = mem_from; + bios_mem_map[n].to = mem_to; + bios_mem_map[n].type = (int)bmmap->type; + n++; + } + } + bmmap = (memory_map_t *)((unsigned int)bmmap + bmmap->size + sizeof(bmmap->size)); + } + } else { + printk("WARNING: your BIOS has not provided a memory map.\n"); + bios_mem_map[0].from = 0; + bios_mem_map[0].to = _memsize * 1024; + bios_mem_map[0].type = BIOS_MEM_AVAIL; + bios_mem_map[1].from = 0x00100000; + bios_mem_map[1].to = (_extmemsize + 1024) * 1024; + bios_mem_map[1].type = BIOS_MEM_AVAIL; + } + kstat.physical_pages = (_extmemsize + 1024) >> 2; + + /* + * This truncates to 1GB since it's the maximum physical memory + * currently supported. + */ + if(kstat.physical_pages & (0x40000000 >> PAGE_SHIFT)) { + kstat.physical_pages &= (0x40000000 >> PAGE_SHIFT); + printk("WARNING: only up to 1GB of physical memory will be used.\n"); + } +} diff --git a/mm/fault.c b/mm/fault.c new file mode 100644 index 00000000..2a750370 --- /dev/null +++ b/mm/fault.c @@ -0,0 +1,261 @@ +/* + * fiwix/mm/fault.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* send the SIGSEGV signal to the ofending process */ +static void send_sigsegv(struct sigcontext *sc) +{ + dump_registers(14, sc); + printk("Memory map:\n"); + show_vma_regions(current); + send_sig(current, SIGSEGV); +} + +static int page_protection_violation(struct vma *vma, unsigned int cr2, struct sigcontext *sc) +{ + unsigned int *pgdir; + unsigned int *pgtbl; + unsigned int page, newpage; + unsigned int pde, pte; + struct page *pg; + + pde = GET_PGDIR(cr2); + pte = GET_PGTBL(cr2); + pgdir = (unsigned int *)P2V(current->tss.cr3); + pgtbl = (unsigned int *)P2V((pgdir[pde] & PAGE_MASK)); + page = (pgtbl[pte] & PAGE_MASK) >> PAGE_SHIFT; + + pg = &page_table[page]; + + /* Copy On Write feature */ + if(pg->count > 1) { + /* a page not marked as COW means it's read-only */ + if(!(pg->flags & PAGE_COW)) { + printk("Oops!, page %d NOT marked for COW.\n", pg->page); + send_sigsegv(sc); + return 0; + } + if(!(newpage = kmalloc())) { + printk("%s(): not enough memory!\n", __FUNCTION__); + return 1; + } + current->rss++; + memcpy_b((void *)newpage, (void *)P2V((page << PAGE_SHIFT)), PAGE_SIZE); + pgtbl[pte] = V2P(newpage) | PAGE_PRESENT | PAGE_RW | PAGE_USER; + kfree(P2V((page << PAGE_SHIFT))); + current->rss--; + invalidate_tlb(); + return 0; + } else { + /* last page of Copy On Write procedure */ + if(pg->count == 1) { + /* a page not marked as COW means it's read-only */ + if(!(pg->flags & PAGE_COW)) { + printk("Oops!, last page %d NOT marked for COW.\n", pg->page); + send_sigsegv(sc); + return 0; + } + pgtbl[pte] = (page << PAGE_SHIFT) | PAGE_PRESENT | PAGE_RW | PAGE_USER; + invalidate_tlb(); + return 0; + } + } + printk("WARNING: %s(): page %d with pg->count = 0!\n", __FUNCTION__, pg->page); + return 1; +} + +static int page_not_present(struct vma *vma, unsigned int cr2, struct sigcontext *sc) +{ + unsigned int page, file_offset; + struct page *pg; + + if(!vma) { + if(cr2 >= (sc->oldesp - 32)) { + if(!(vma = find_vma_region(KERNEL_BASE_ADDR - 1))) { + printk("WARNING: %s(): process %d doesn't have an stack region in vma!\n", __FUNCTION__, current->pid); + send_sigsegv(sc); + return 0; + } else { + /* assuming stack will never reach heap */ + vma->start = cr2; + vma->start = vma->start & PAGE_MASK; + } + } + } + + /* if still a non-valid vma is found then kill the process! */ + if(!vma || vma->prot == PROT_NONE) { + send_sigsegv(sc); + return 0; + } + + /* fill the page with its corresponding file content */ + if(vma->inode) { + file_offset = (cr2 & PAGE_MASK) - vma->start + vma->offset; + file_offset &= PAGE_MASK; + pg = NULL; + + if(!(vma->prot & PROT_WRITE) || vma->flags & MAP_SHARED) { + /* check if it's already in cache */ + if((pg = search_page_hash(vma->inode, file_offset))) { + if(!map_page(current, cr2, (unsigned int)V2P(pg->data), vma->prot)) { + printk("%s(): Oops, map_page() returned 0!\n", __FUNCTION__); + return 1; + } + page = (unsigned int)pg->data; + } + } + if(!pg) { + if(!(page = map_page(current, cr2, 0, vma->prot))) { + printk("%s(): Oops, map_page() returned 0!\n", __FUNCTION__); + return 1; + } + pg = &page_table[V2P(page) >> PAGE_SHIFT]; + if(bread_page(pg, vma->inode, file_offset, vma->prot, vma->flags)) { + unmap_page(page); + return 1; + } + current->usage.ru_majflt++; + } + } else { + current->usage.ru_minflt++; + page = 0; + } + + if(vma->flags & ZERO_PAGE) { + if(!page) { + if(!(page = map_page(current, cr2, 0, vma->prot))) { + printk("%s(): Oops, map_page() returned 0!\n", __FUNCTION__); + return 1; + } + } + memset_b((void *)(page & PAGE_MASK), NULL, PAGE_SIZE); + } + + return 0; +} + +/* + * Exception 0xE: Page Fault + * + * +------+------+------+------+------+------+ + * | user |kernel| PV | PF | read |write | + * +-------------+------+------+------+------+------+------+ + * |the page | U1 | K1| U1 K1| | U1 K1| K1| + * |has | U2 | K2| U2 | K2| K2| U2 K2| + * |a vma region | U3 | | | U3 | U3 | U3 | + * +-------------+------+------+------+------+------+------+ + * |the page | U1 | K1| U1 K1| K1| U1 K1| U1 K1| + * |doesn't have | U2 | | | U2 | U2 | U2 | + * |a vma region | | | | | | | + * +-------------+------+------+------+------+------+------+ + * + * U1 - vma + user + PV + read + * (vma page in user-mode, page-violation during read) + * U1.1) if flags match -> Demand paging + * U1.2) if flags don't match -> SIGSEV + * + * U2 - vma + user + PV + write + * (vma page in user-mode, page-violation during write) + * U2.1) if flags match -> Copy-On-Write + * U2.2) if flags don't match -> SIGSEGV + * + * U3 - vma + user + PF + (read | write) -> Demand paging + * (vma page in user-mode, page-fault during read or write) + * + * K1 - vma + kernel + PV + (read | write) -> PANIC + * (vma page in kernel-mode, page-violation during read or write) + * K2 - vma + kernel + PF + (read | write) -> Demand paging (mmap) + * (vma page in kernel-mode, page-fault during read or write) + * + * ---------------------------------------------------------------------------- + * + * U1 - !vma + user + PV + (read | write) -> SIGSEGV + * (!vma page in user-mode, page-violation during read or write) + * U2 - !vma + user + PF + (read | write) -> STACK grows + * (!vma page in user-mode, page-fault during read or write) + * + * K1 - !vma + kernel + (PV | PF) + (read | write) -> PANIC + * (!vma page in kernel-mode, page-fault or page-violation during read + * or write) + */ +void do_page_fault(unsigned int trap, struct sigcontext *sc) +{ + unsigned int cr2; + struct vma *vma; + + GET_CR2(cr2); + if((vma = find_vma_region(cr2))) { + + /* in user mode */ + if(sc->err & PFAULT_U) { + if(sc->err & PFAULT_V) { /* violation */ + if(sc->err & PFAULT_W) { + if((page_protection_violation(vma, cr2, sc))) { + send_sig(current, SIGKILL); + } + return; + } + send_sigsegv(sc); + } else { /* page not present */ + if((page_not_present(vma, cr2, sc))) { + send_sig(current, SIGKILL); + } + } + return; + + /* in kernel mode */ + } else { + /* + * WP bit marks the order: first check if the page is + * present, then check for protection violation. + */ + if(!(sc->err & PFAULT_V)) { /* page not present */ + if((page_not_present(vma, cr2, sc))) { + send_sig(current, SIGKILL); + printk("%s(): kernel was unable to read a page of process '%s' (pid %d).\n", __FUNCTION__, current->argv0, current->pid); + } + return; + } + if(sc->err & PFAULT_W) { /* copy-on-write? */ + if((page_protection_violation(vma, cr2, sc))) { + send_sig(current, SIGKILL); + printk("%s(): kernel was unable to write a page of process '%s' (pid %d).\n", __FUNCTION__, current->argv0, current->pid); + } + return; + } + } + } else { + /* in user mode */ + if(sc->err & PFAULT_U) { + if(sc->err & PFAULT_V) { /* violation */ + send_sigsegv(sc); + } else { /* stack? */ + if((page_not_present(vma, cr2, sc))) { + send_sig(current, SIGKILL); + } + } + return; + } + } + + dump_registers(trap, sc); + show_vma_regions(current); + PANIC("\n"); +} diff --git a/mm/memory.c b/mm/memory.c new file mode 100644 index 00000000..b48491af --- /dev/null +++ b/mm/memory.c @@ -0,0 +1,430 @@ +/* + * fiwix/mm/memory.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KERNEL_TEXT_SIZE ((int)_etext - (KERNEL_BASE_ADDR + KERNEL_ENTRY_ADDR)) +#define KERNEL_DATA_SIZE ((int)_edata - (int)_etext) +#define KERNEL_BSS_SIZE ((int)_end - (int)_edata) + +#define PGDIR_4MB_ADDR 0x90000 + +unsigned int *kpage_dir; +unsigned int *kpage_table; + +unsigned int _last_data_addr; + +unsigned int proc_table_size = 0; +struct proc *proc_table; + +unsigned int buffer_table_size = 0; +unsigned int buffer_hash_table_size = 0; +struct buffer *buffer_table; +struct buffer **buffer_hash_table; + +unsigned int inode_table_size = 0; +unsigned int inode_hash_table_size = 0; +struct inode *inode_table; +struct inode **inode_hash_table; + +unsigned int fd_table_size = 0; +struct fd *fd_table; + +unsigned int mount_table_size = 0; +struct mount *mount_table; + +struct ramdisk ramdisk_table[RAMDISK_MINORS]; + +unsigned int page_table_size = 0; +unsigned int page_hash_table_size = 0; +struct page *page_table; +struct page **page_hash_table; + +static void map_kaddr(unsigned int from, unsigned int to, int flags) +{ + unsigned int n; + unsigned int *pgtbl; + unsigned int pde, pte; + + for(n = from >> PAGE_SHIFT; n < (to >> PAGE_SHIFT); n++) { + pde = GET_PGDIR(n << PAGE_SHIFT); + pte = GET_PGTBL(n << PAGE_SHIFT); + if(!(kpage_dir[pde] & ~PAGE_MASK)) { + unsigned int addr; + addr = _last_data_addr; + _last_data_addr += PAGE_SIZE; + kpage_dir[pde] = addr | flags; + memset_b((void *)addr, NULL, PAGE_SIZE); + } + pgtbl = (unsigned int *)(kpage_dir[pde] & PAGE_MASK); + pgtbl[pte] = (kpage_table[(pde * 1024) + pte] & PAGE_MASK) | flags; + } +} + +void bss_init(void) +{ + memset_b((void *)((int)_edata), NULL, KERNEL_BSS_SIZE); +} + +/* + * This function creates a minimal Page Directory covering only the first 4MB + * of physical memory. Just enough to boot the kernel. + * (it returns the address to be used by the CR3 register) + */ +unsigned int setup_minmem(void) +{ + int n; + unsigned int addr; + short int pd, mb4; + + mb4 = 1; /* 4MB units */ + addr = KERNEL_BASE_ADDR + PGDIR_4MB_ADDR; + + kpage_dir = (unsigned int *)addr; + memset_b(kpage_dir, NULL, PAGE_SIZE); + + addr += PAGE_SIZE; + kpage_table = (unsigned int *)addr; + memset_b(kpage_table, NULL, PAGE_SIZE * mb4); + + for(n = 0; n < (1024 * mb4); n++) { + kpage_table[n] = (n << PAGE_SHIFT) | PAGE_PRESENT | PAGE_RW; + if(!(n % 1024)) { + pd = n / 1024; + kpage_dir[pd] = (unsigned int)(addr + (PAGE_SIZE * pd) + 0x40000000) | PAGE_PRESENT | PAGE_RW; + kpage_dir[GET_PGDIR(KERNEL_BASE_ADDR) + pd] = (unsigned int)(addr + (PAGE_SIZE * pd) + 0x40000000) | PAGE_PRESENT | PAGE_RW; + } + } + return (unsigned int)kpage_dir + 0x40000000; +} + +/* returns the mapped address of a virtual address */ +unsigned int get_mapped_addr(struct proc *p, unsigned int addr) +{ + unsigned int *pgdir, *pgtbl; + unsigned int pde, pte; + + pgdir = (unsigned int *)P2V(p->tss.cr3); + pde = GET_PGDIR(addr); + pte = GET_PGTBL(addr); + pgtbl = (unsigned int *)P2V((pgdir[pde] & PAGE_MASK)); + return pgtbl[pte]; +} + +int clone_pages(struct proc *child) +{ + unsigned int *src_pgdir, *dst_pgdir; + unsigned int *src_pgtbl, *dst_pgtbl; + unsigned int pde, pte; + unsigned int p_page, c_page; + unsigned int n, n2, pages; + struct page *pg; + struct vma *vma; + + src_pgdir = (unsigned int *)P2V(current->tss.cr3); + dst_pgdir = (unsigned int *)P2V(child->tss.cr3); + vma = current->vma; + + for(n = 0, pages = 0; n < VMA_REGIONS && vma->start; n++, vma++) { + for(n2 = vma->start; n2 < vma->end; n2 += PAGE_SIZE) { + pde = GET_PGDIR(n2); + pte = GET_PGTBL(n2); + if(src_pgdir[pde] & PAGE_PRESENT) { + src_pgtbl = (unsigned int *)P2V((src_pgdir[pde] & PAGE_MASK)); + if(!(dst_pgdir[pde] & PAGE_PRESENT)) { + if(!(c_page = kmalloc())) { + printk("%s(): returning 0!\n", __FUNCTION__); + return 0; + } + current->rss++; + pages++; + dst_pgdir[pde] = V2P(c_page) | PAGE_PRESENT | PAGE_RW | PAGE_USER; + memset_b((void *)c_page, NULL, PAGE_SIZE); + } + dst_pgtbl = (unsigned int *)P2V((dst_pgdir[pde] & PAGE_MASK)); + if(src_pgtbl[pte] & PAGE_PRESENT) { + p_page = src_pgtbl[pte] >> PAGE_SHIFT; + pg = &page_table[p_page]; + if(pg->flags & PAGE_RESERVED) { + continue; + } + src_pgtbl[pte] &= ~PAGE_RW; + /* mark as COW only writable pages */ + if(vma->prot & PROT_WRITE) { + pg->flags |= PAGE_COW; + } + dst_pgtbl[pte] = src_pgtbl[pte]; + if(!valid_page((dst_pgtbl[pte] & PAGE_MASK) >> PAGE_SHIFT)) { + PANIC("%s: missing page %d during copy-on-write process.\n", __FUNCTION__, (dst_pgtbl[pte] & PAGE_MASK) >> PAGE_SHIFT); + } + pg = &page_table[(dst_pgtbl[pte] & PAGE_MASK) >> PAGE_SHIFT]; + pg->count++; + } + } + } + } + return pages; +} + +int free_page_tables(struct proc *p) +{ + unsigned int *pgdir; + unsigned int n, count; + + pgdir = (unsigned int *)P2V(p->tss.cr3); + for(n = 0, count = 0; n < PD_ENTRIES; n++) { + if((pgdir[n] & (PAGE_PRESENT | PAGE_RW | PAGE_USER)) == (PAGE_PRESENT | PAGE_RW | PAGE_USER)) { + kfree(P2V(pgdir[n]) & PAGE_MASK); + pgdir[n] = NULL; + count++; + } + } + return count; +} + +unsigned int map_page(struct proc *p, unsigned int vaddr, unsigned int addr, unsigned int prot) +{ + unsigned int *pgdir, *pgtbl; + unsigned int vpage; + int pde, pte; + + pgdir = (unsigned int *)P2V(p->tss.cr3); + pde = GET_PGDIR(vaddr); + pte = GET_PGTBL(vaddr); + + if(!(pgdir[pde] & PAGE_PRESENT)) { /* allocating page table */ + if(!(vpage = kmalloc())) { + return 0; + } + p->rss++; + pgdir[pde] = V2P(vpage) | PAGE_PRESENT | PAGE_RW | PAGE_USER; + memset_b((void *)vpage, NULL, PAGE_SIZE); + } + pgtbl = (unsigned int *)P2V((pgdir[pde] & PAGE_MASK)); + if(!(pgtbl[pte] & PAGE_PRESENT)) { /* allocating page */ + if(!addr) { + if(!(addr = kmalloc())) { + return 0; + } + addr = V2P(addr); + p->rss++; + } + } + pgtbl[pte] = addr | PAGE_PRESENT | PAGE_USER; + if(prot & PROT_WRITE) { + pgtbl[pte] |= PAGE_RW; + } + return P2V(addr); +} + +int unmap_page(unsigned int vaddr) +{ + unsigned int *pgdir, *pgtbl; + unsigned int addr; + int pde, pte; + + pgdir = (unsigned int *)P2V(current->tss.cr3); + pde = GET_PGDIR(vaddr); + pte = GET_PGTBL(vaddr); + if(!(pgdir[pde] & PAGE_PRESENT)) { + printk("WARNING: %s(): trying to unmap an unallocated pde '0x%08x'\n", __FUNCTION__, vaddr); + return 1; + } + + pgtbl = (unsigned int *)P2V((pgdir[pde] & PAGE_MASK)); + if(!(pgtbl[pte] & PAGE_PRESENT)) { + printk("WARNING: %s(): trying to unmap an unallocated page '0x%08x'\n", __FUNCTION__, vaddr); + return 1; + } + + addr = pgtbl[pte] & PAGE_MASK; + pgtbl[pte] = NULL; + kfree(P2V(addr)); + current->rss--; + return 0; +} + +void mem_init(void) +{ + unsigned int pages, sizek; + unsigned int n; + unsigned int physical_page_tables; + unsigned int physical_memory; + + physical_page_tables = (kstat.physical_pages / 1024) + ((kstat.physical_pages % 1024) ? 1 : 0); + physical_memory = (kstat.physical_pages << PAGE_SHIFT); /* in bytes */ + + /* Page Directory will be aligned to the next page */ + _last_data_addr = PAGE_ALIGN(_last_data_addr); + kpage_dir = (unsigned int *)_last_data_addr; + memset_b(kpage_dir, NULL, PAGE_SIZE); + _last_data_addr += PAGE_SIZE; + + /* Page Tables */ + kpage_table = (unsigned int *)_last_data_addr; + memset_b(kpage_table, NULL, physical_page_tables * PAGE_SIZE); + _last_data_addr += physical_page_tables * PAGE_SIZE; + + /* Page Directory and Page Tables initialization */ + for(n = 0; n < kstat.physical_pages; n++) { + kpage_table[n] = (n << PAGE_SHIFT) | PAGE_PRESENT | PAGE_RW; + } + for(n = 0; n < physical_page_tables; n++) { + kpage_dir[GET_PGDIR(KERNEL_BASE_ADDR) + n] = (unsigned int)&kpage_table[n * 1024] | PAGE_PRESENT | PAGE_RW; + } + + map_kaddr(0xA0000, KERNEL_ENTRY_ADDR, PAGE_PRESENT | PAGE_RW); + map_kaddr(KERNEL_ENTRY_ADDR, _last_data_addr, PAGE_PRESENT | PAGE_RW); +/* printk("_last_data_addr = 0x%08x-0x%08x (kernel)\n", KERNEL_ENTRY_ADDR, _last_data_addr); */ + activate_kpage_dir(); + + /* since Page Directory is now activated we can use virtual addresses */ + _last_data_addr = P2V(_last_data_addr); + + + /* reserve memory space for proc_table[NR_PROCS] */ + proc_table_size = PAGE_ALIGN(sizeof(struct proc) * NR_PROCS); + if(!addr_in_bios_map(V2P(_last_data_addr) + proc_table_size)) { + PANIC("Not enough memory for proc_table.\n"); + } +/* printk("_last_data_addr = 0x%08x-0x%08x (proc_table)\n", _last_data_addr, _last_data_addr + proc_table_size); */ + proc_table = (struct proc *)_last_data_addr; + _last_data_addr += proc_table_size; + + + /* reserve memory space for buffer_table */ + buffer_table_size = (kstat.physical_pages * BUFFER_PERCENTAGE) / 100; + buffer_table_size *= sizeof(struct buffer); + pages = buffer_table_size >> PAGE_SHIFT; + buffer_table_size = !pages ? 4096 : pages << PAGE_SHIFT; +/* printk("_last_data_addr = 0x%08x-0x%08x (buffer_table)\n", _last_data_addr, _last_data_addr + buffer_table_size); */ + if(!addr_in_bios_map(V2P(_last_data_addr) + buffer_table_size)) { + PANIC("Not enough memory for buffer_table.\n"); + } + buffer_table = (struct buffer *)_last_data_addr; + _last_data_addr += buffer_table_size; + + + /* reserve memory space for buffer_hash_table */ + n = (buffer_table_size / sizeof(struct buffer) * BUFFER_HASH_PERCENTAGE) / 100; + n = MAX(n, 10); /* 10 buffer hashes as minimum */ + /* buffer_hash_table is an array of pointers */ + pages = ((n * sizeof(unsigned int)) / PAGE_SIZE) + 1; + buffer_hash_table_size = pages << PAGE_SHIFT; +/* printk("_last_data_addr = 0x%08x-0x%08x (buffer_hash_table)\n", _last_data_addr, _last_data_addr + buffer_hash_table_size); */ + if(!addr_in_bios_map(V2P(_last_data_addr) + buffer_hash_table_size)) { + PANIC("Not enough memory for buffer_hash_table.\n"); + } + buffer_hash_table = (struct buffer **)_last_data_addr; + _last_data_addr += buffer_hash_table_size; + + + /* reserve memory space for inode_table */ + sizek = physical_memory / 1024; /* this helps to avoid overflow */ + inode_table_size = (sizek * INODE_PERCENTAGE) / 100; + inode_table_size *= 1024; + pages = inode_table_size >> PAGE_SHIFT; + inode_table_size = pages << PAGE_SHIFT; +/* printk("_last_data_addr = 0x%08x-0x%08x (inode_table)\n", _last_data_addr, _last_data_addr + inode_table_size); */ + if(!addr_in_bios_map(V2P(_last_data_addr) + inode_table_size)) { + PANIC("Not enough memory for inode_table.\n"); + } + inode_table = (struct inode *)_last_data_addr; + _last_data_addr += inode_table_size; + + + /* reserve memory space for inode_hash_table */ + n = ((inode_table_size / sizeof(struct inode)) * INODE_HASH_PERCENTAGE) / 100; + n = MAX(n, 10); /* 10 inodes hashes as minimum */ + /* inode_hash_table is an array of pointers */ + pages = ((n * sizeof(unsigned int)) / PAGE_SIZE) + 1; + inode_hash_table_size = pages << PAGE_SHIFT; +/* printk("_last_data_addr = 0x%08x-0x%08x (inode_hash_table)\n", _last_data_addr, _last_data_addr + inode_hash_table_size); */ + if(!addr_in_bios_map(V2P(_last_data_addr) + inode_hash_table_size)) { + PANIC("Not enough memory for inode_hash_table.\n"); + } + inode_hash_table = (struct inode **)_last_data_addr; + _last_data_addr += inode_hash_table_size; + + + /* reserve memory space for fd_table[NR_OPENS] */ + fd_table_size = PAGE_ALIGN(sizeof(struct fd) * NR_OPENS); +/* printk("_last_data_addr = 0x%08x-0x%08x (fd_table)\n", _last_data_addr, _last_data_addr + fd_table_size); */ + if(!addr_in_bios_map(V2P(_last_data_addr) + fd_table_size)) { + PANIC("Not enough memory for fd_table.\n"); + } + fd_table = (struct fd *)_last_data_addr; + _last_data_addr += fd_table_size; + + + /* reserve memory space for mount_table[NR_MOUNT_POINTS] */ + mount_table_size = PAGE_ALIGN(sizeof(struct mount) * NR_MOUNT_POINTS); +/* printk("_last_data_addr = 0x%08x-0x%08x (mount_table)\n", _last_data_addr, _last_data_addr + mount_table_size); */ + if(!addr_in_bios_map(V2P(_last_data_addr) + mount_table_size)) { + PANIC("Not enough memory for mount_table.\n"); + } + mount_table = (struct mount *)_last_data_addr; + _last_data_addr += mount_table_size; + + + /* reserve memory space for RAMdisk(s) */ + if(!_noramdisk) { + if(!_ramdisksize) { + _ramdisksize = RAMDISK_SIZE; + } + if(!addr_in_bios_map(V2P(_last_data_addr) + (_ramdisksize * 1024))) { + printk("WARNING: RAMdisk device disabled (not enough physical memory).\n"); + _noramdisk = 1; + } else { + for(n = 0; n < RAMDISK_MINORS; n++) { +/* printk("_last_data_addr = 0x%08x-0x%08x (/dev/ram%d)\n", _last_data_addr, _last_data_addr + (_ramdisksize * 1024), n); */ + ramdisk_table[n].addr = (char *)_last_data_addr; + _last_data_addr += _ramdisksize * 1024; + } + } + } + + + /* the last one must be the page_table structure */ + page_hash_table_size = 1 * PAGE_SIZE; /* only 1 page size */ + if(!addr_in_bios_map(V2P(_last_data_addr) + page_hash_table_size)) { + PANIC("Not enough memory for page_hash_table.\n"); + } + page_hash_table = (struct page **)_last_data_addr; +/* printk("_last_data_addr = 0x%08x-0x%08x (page_hash_table)\n", _last_data_addr, _last_data_addr + page_hash_table_size); */ + _last_data_addr += page_hash_table_size; + + page_table_size = PAGE_ALIGN(kstat.physical_pages * sizeof(struct page)); + if(!addr_in_bios_map(V2P(_last_data_addr) + page_table_size)) { + PANIC("Not enough memory for page_table.\n"); + } + page_table = (struct page *)_last_data_addr; +/* printk("page_table_size = %d\n", page_table_size); */ +/* printk("_last_data_addr = 0x%08x-0x%08x (page_table)\n", _last_data_addr, _last_data_addr + page_table_size); */ + _last_data_addr += page_table_size; + + page_init(kstat.physical_pages); +} + +void mem_stats(void) +{ + printk("\n"); + printk("memory: total/available=%dKB/%dKB, kernel=%dKB, reserved=%dKB\n", kstat.physical_pages << 2, kstat.total_mem_pages << 2, kstat.kernel_reserved, kstat.physical_reserved); + printk("kernel: text=%dKB, data=%dKB, bss=%dKB, i/o buffers=%d (%dKB)\n", KERNEL_TEXT_SIZE / 1024, KERNEL_DATA_SIZE / 1024, KERNEL_BSS_SIZE / 1024, buffer_table_size / sizeof(struct buffer), (buffer_table_size + buffer_hash_table_size) / 1024); + printk("\tinodes=%d (%dKB)\n\n", inode_table_size / sizeof(struct inode), (inode_table_size + inode_hash_table_size) / 1024); +} diff --git a/mm/mmap.c b/mm/mmap.c new file mode 100644 index 00000000..13dfb068 --- /dev/null +++ b/mm/mmap.c @@ -0,0 +1,511 @@ +/* + * fiwix/mm/mmap.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void show_vma_regions(struct proc *p) +{ + __ino_t inode; + int major, minor; + char *section; + char r, w, x, f; + struct vma *vma; + unsigned int n; + int count; + + vma = p->vma; + printk("num address range flag offset dev inode mod section cnt\n"); + printk("---- --------------------- ---- ---------- ----- ---------- --- ------- ----\n"); + for(n = 0; n < VMA_REGIONS && vma->start; n++, vma++) { + r = vma->prot & PROT_READ ? 'r' : '-'; + w = vma->prot & PROT_WRITE ? 'w' : '-'; + x = vma->prot & PROT_EXEC ? 'x' : '-'; + if(vma->flags & MAP_SHARED) { + f = 's'; + } else if(vma->flags & MAP_PRIVATE) { + f = 'p'; + } else { + f = '-'; + } + switch(vma->s_type) { + case P_TEXT: section = "text "; + break; + case P_DATA: section = "data "; + break; + case P_BSS: section = "bss "; + break; + case P_HEAP: section = "heap "; + break; + case P_STACK: section = "stack"; + break; + case P_MMAP: section = "mmap "; + break; + default: + section = NULL; + break; + } + inode = major = minor = count = 0; + if(vma->inode) { + inode = vma->inode->inode; + major = MAJOR(vma->inode->dev); + minor = MINOR(vma->inode->dev); + count = vma->inode->count; + } + printk("[%02d] 0x%08x-0x%08x %c%c%c%c 0x%08x %02d:%02d %- 10u <%d> [%s] (%d)\n", n, vma->start, vma->end, r, w, x, f, vma->offset, major, minor, inode, vma->o_mode, section, count); + } + if(!n) { + printk("[no vma regions]\n"); + } +} + +static struct vma * get_new_vma_region(void) +{ + unsigned int n; + struct vma *vma; + + vma = current->vma; + + for(n = 0; n < VMA_REGIONS; n++, vma++) { + if(!vma->start && !vma->end) { + return vma; + } + } + return NULL; +} + +/* + * This sorts regions (in ascending order), merging equal regions and keeping + * the unused ones at the end of the array. + */ +static void sort_vma(void) +{ + unsigned int n, n2, needs_sort; + struct vma *vma, tmp; + + vma = current->vma; + + do { + needs_sort = 0; + for(n = 0, n2 = 1; n2 < VMA_REGIONS; n++, n2++) { + if(vma[n].end && vma[n2].start) { + if((vma[n].end == vma[n2].start) && + (vma[n].prot == vma[n2].prot) && + (vma[n].flags == vma[n2].flags) && + (vma[n].offset == vma[n2].offset) && + (vma[n].s_type == vma[n2].s_type) && + (vma[n].inode == vma[n2].inode)) { + vma[n].end = vma[n2].end; + memset_b(&vma[n2], NULL, sizeof(struct vma)); + needs_sort++; + } + } + if((vma[n2].start && (vma[n].start > vma[n2].start)) || (!vma[n].start && vma[n2].start)) { + memcpy_b(&tmp, &vma[n], sizeof(struct vma)); + memcpy_b(&vma[n], &vma[n2], sizeof(struct vma)); + memcpy_b(&vma[n2], &tmp, sizeof(struct vma)); + needs_sort++; + } + } + } while(needs_sort); +} + +/* + * This function removes all redundant entries. + * + * for example, if for any reason the map looks like this: + * [01] 0x0808e984-0x08092000 rw-p 0x00000000 0 + * [02] 0x0808f000-0x0808ffff rw-p 0x000c0000 4066 + * + * this function converts it to this: + * [01] 0x0808e984-0x0808f000 rw-p 0x00000000 0 + * [02] 0x0808f000-0x0808ffff rw-p 0x000c0000 4066 + * [03] 0x08090000-0x08092000 rw-p 0x00000000 0 + */ +static int optimize_vma(void) +{ + unsigned int n, needs_sort; + struct vma *vma, *prev, *new; + + for(;;) { + needs_sort = 0; + prev = new = NULL; + vma = current->vma; + for(n = 0; n < VMA_REGIONS && vma->start; n++, vma++) { + if(!prev) { + prev = vma; + continue; + } + if((vma->start < prev->end)) { + if(!(new = get_new_vma_region())) { + printk("WARNING: %s(): unable to get a free vma region.\n", __FUNCTION__); + return -ENOMEM; + } + new->start = vma->end; + new->end = prev->end; + new->prot = prev->prot; + new->flags = prev->flags; + new->offset = prev->offset; + new->s_type = prev->s_type; + new->inode = prev->inode; + new->o_mode = prev->o_mode; + prev->end = vma->start; + needs_sort++; + if(prev->start == prev->end) { + memset_b(prev, NULL, sizeof(struct vma)); + } + if(new->start == new->end) { + memset_b(new, NULL, sizeof(struct vma)); + } + break; + } + prev = vma; + } + if(!needs_sort) { + break; + } + sort_vma(); + } + + return 0; +} + +/* return the first free address that matches with the size of length */ +static unsigned int get_unmapped_vma_region(unsigned int length) +{ + unsigned int n, addr; + struct vma *vma; + + if(!length) { + return 0; + } + + addr = MMAP_START; + vma = current->vma; + + for(n = 0; n < VMA_REGIONS && vma->start; n++, vma++) { + if(vma->start < MMAP_START) { + continue; + } + if((addr + length) < vma->start) { + return PAGE_ALIGN(addr); + } + addr = PAGE_ALIGN(vma->end); + } + return 0; +} + +static void free_vma_pages(unsigned int start, __size_t length, struct vma *vma) +{ + unsigned int n, addr; + unsigned int *pgdir, *pgtbl; + unsigned int pde, pte, page; + struct page *pg; + + pgdir = (unsigned int *)P2V(current->tss.cr3); + pgtbl = NULL; + + for(n = 0; n < (length / PAGE_SIZE); n++) { + pde = GET_PGDIR(start + (n * PAGE_SIZE)); + pte = GET_PGTBL(start + (n * PAGE_SIZE)); + if(pgdir[pde] & PAGE_PRESENT) { + pgtbl = (unsigned int *)P2V((pgdir[pde] & PAGE_MASK)); + if(pgtbl[pte] & PAGE_PRESENT) { + /* make sure to not free reserved pages */ + page = pgtbl[pte] >> PAGE_SHIFT; + pg = &page_table[page]; + if(pg->flags & PAGE_RESERVED) { + continue; + } + + if(vma->prot & PROT_WRITE && vma->flags & MAP_SHARED) { + addr = start - vma->start + vma->offset; + write_page(pg, vma->inode, addr, length); + } + + kfree(P2V(pgtbl[pte]) & PAGE_MASK); + current->rss--; + pgtbl[pte] = NULL; + + /* check if a page table can be freed */ + for(pte = 0; pte < PT_ENTRIES; pte++) { + if(pgtbl[pte] & PAGE_MASK) { + break; + } + } + if(pte == PT_ENTRIES) { + kfree((unsigned int)pgtbl & PAGE_MASK); + current->rss--; + pgdir[pde] = NULL; + } + } + } + } +} + +static int free_vma_region(struct vma *vma, unsigned int start, __ssize_t length) +{ + struct vma *new; + + if(!(new = get_new_vma_region())) { + printk("WARNING: %s(): unable to get a free vma region.\n", __FUNCTION__); + return -ENOMEM; + } + + new->start = start + length; + new->end = vma->end; + new->prot = vma->prot; + new->flags = vma->flags; + new->offset = vma->offset; + new->s_type = vma->s_type; + new->inode = vma->inode; + new->o_mode = vma->o_mode; + + vma->end = start; + + if(vma->start == vma->end) { + if(vma->inode) { + iput(vma->inode); + } + memset_b(vma, NULL, sizeof(struct vma)); + } + if(new->start == new->end) { + memset_b(new, NULL, sizeof(struct vma)); + } + return 0; +} + +void release_binary(void) +{ + unsigned int n; + struct vma *vma; + + vma = current->vma; + + for(n = 0; n < VMA_REGIONS && vma->start; n++, vma++) { + free_vma_pages(vma->start, vma->end - vma->start, vma); + free_vma_region(vma, vma->start, vma->end - vma->start); + } + sort_vma(); + optimize_vma(); + invalidate_tlb(); +} + +struct vma * find_vma_region(unsigned int addr) +{ + unsigned int n; + struct vma *vma; + + if(!addr) { + return NULL; + } + + addr &= PAGE_MASK; + vma = current->vma; + + for(n = 0; n < VMA_REGIONS && vma->start; n++, vma++) { + if((addr >= vma->start) && (addr < vma->end)) { + return vma; + } + } + return NULL; +} + +int expand_heap(unsigned int new) +{ + unsigned int n; + struct vma *vma, *heap; + + vma = current->vma; + heap = NULL; + + for(n = 0; n < VMA_REGIONS && vma->start; n++, vma++) { + /* make sure the new heap won't overlap the next region */ + if(heap && new < vma->start) { + heap->end = new; + return 0; + } else { + heap = NULL; /* was a bad candidate */ + } + if(!heap && vma->s_type == P_HEAP) { + heap = vma; /* possible candidate */ + continue; + } + } + + /* out of memory! */ + return 1; +} + +int do_mmap(struct inode *i, unsigned int start, unsigned int length, unsigned int prot, unsigned int flags, unsigned int offset, char type, char mode) +{ + struct vma *vma; + int errno; + + if(!(length = PAGE_ALIGN(length))) { + return start; + } + + /* file mapping */ + if(i) { + if(!S_ISREG(i->i_mode) && !S_ISCHR(i->i_mode)) { + return -ENODEV; + } + + /* + * The file shall have been opened with read permission, + * regardless of the protection options specified. + * IEEE Std 1003.1, 2004 Edition. + */ + if(mode == O_WRONLY) { + return -EACCES; + } + switch(flags & MAP_TYPE) { + case MAP_SHARED: + if(prot & PROT_WRITE) { + if(!(mode & (O_WRONLY | O_RDWR))) { + return -EACCES; + } + } + break; + case MAP_PRIVATE: + break; + default: + return -EINVAL; + } + i->count++; + + /* anonymous mapping */ + } else { + if((flags & MAP_TYPE) != MAP_PRIVATE) { + return -EINVAL; + } + + /* anonymous objects must be filled with zeros */ + flags |= ZERO_PAGE; + } + + if(flags & MAP_FIXED) { + if(start & ~PAGE_MASK) { + return -EINVAL; + } + } else { + start = get_unmapped_vma_region(length); + if(!start) { + printk("WARNING: %s(): unable to get an unmapped vma region.\n", __FUNCTION__); + return -ENOMEM; + } + } + + if(!(vma = get_new_vma_region())) { + printk("WARNING: %s(): unable to get a free vma region.\n", __FUNCTION__); + return -ENOMEM; + } + + vma->start = start; + vma->end = start + length; + vma->prot = prot; + vma->flags = flags; + vma->offset = offset; + vma->s_type = type; + vma->inode = i; + vma->o_mode = mode; + + if(i && i->fsop->mmap) { + if((errno = i->fsop->mmap(i, vma))) { + int errno2; + + if((errno2 = free_vma_region(vma, start, length))) { + return errno2; + } + sort_vma(); + if((errno2 = optimize_vma())) { + return errno2; + } + return errno; + } + } + + sort_vma(); + if((errno = optimize_vma())) { + return errno; + } + return start; +} + +int do_munmap(unsigned int addr, __size_t length) +{ + struct vma *vma; + unsigned int size; + int errno; + + if((addr & ~PAGE_MASK) || length < 0) { + return -EINVAL; + } + + length = PAGE_ALIGN(length); + + while(length) { + if((vma = find_vma_region(addr))) { + if((addr + length) > vma->end) { + size = vma->end - addr; + } else { + size = length; + } + + free_vma_pages(addr, size, vma); + invalidate_tlb(); + if((errno = free_vma_region(vma, addr, size))) { + return errno; + } + sort_vma(); + if((errno = optimize_vma())) { + return errno; + } + length -= size; + addr += size; + } else { + break; + } + } + + return 0; +} + +int do_mprotect(struct vma *vma, unsigned int addr, __size_t length, int prot) +{ + struct vma *new; + int errno; + + if(!(new = get_new_vma_region())) { + printk("WARNING: %s(): unable to get a free vma region.\n", __FUNCTION__); + return -ENOMEM; + } + + new->start = addr; + new->end = addr + length; + new->prot = prot; + new->flags = vma->flags; + new->offset = vma->offset; + new->s_type = vma->s_type; + new->inode = vma->inode; + new->o_mode = vma->o_mode; + + sort_vma(); + if((errno = optimize_vma())) { + return errno; + } + return 0; +} diff --git a/mm/page.c b/mm/page.c new file mode 100644 index 00000000..adb8e45c --- /dev/null +++ b/mm/page.c @@ -0,0 +1,443 @@ +/* + * fiwix/mm/page.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +/* + * page.c implements a cache with a free list as a doubly circular linked + * list and a chained hash table with doubly linked lists. + * + * hash table + * +--------+ +--------------+ +--------------+ +--------------+ + * | index | |prev|data|next| |prev|data|next| |prev|data|next| + * | 0 --> | / | | ---> <--- | | ---> <--- | | / | + * +--------+ +--------------+ +--------------+ +--------------+ + * +--------+ +--------------+ +--------------+ +--------------+ + * | index | |prev|data|next| |prev|data|next| |prev|data|next| + * | 1 --> | / | | ---> <--- | | ---> <--- | | / | + * +--------+ +--------------+ +--------------+ +--------------+ + * (page) (page) (page) + * ... + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PAGE_HASH(inode, offset) (((__ino_t)(inode) ^ (__off_t)(offset)) % NR_PAGE_HASH) +#define NR_PAGES page_table_size / sizeof(struct page) +#define NR_PAGE_HASH page_hash_table_size / sizeof(unsigned int) + +struct page *page_table; /* page pool */ +struct page *page_head; /* page pool head */ +struct page **page_hash_table; + +static void insert_to_hash(struct page *pg) +{ + struct page **h; + int i; + + i = PAGE_HASH(pg->inode->inode, pg->offset); + h = &page_hash_table[i]; + + if(!*h) { + *h = pg; + (*h)->prev_hash = (*h)->next_hash = NULL; + } else { + pg->prev_hash = NULL; + pg->next_hash = *h; + (*h)->prev_hash = pg; + *h = pg; + } + kstat.cached += (PAGE_SIZE / 1024); +} + +static void remove_from_hash(struct page *pg) +{ + struct page **h; + int i; + + if(!pg->inode) { + return; + } + + i = PAGE_HASH(pg->inode->inode, pg->offset); + h = &page_hash_table[i]; + + while(*h) { + if(*h == pg) { + if((*h)->next_hash) { + (*h)->next_hash->prev_hash = (*h)->prev_hash; + } + if((*h)->prev_hash) { + (*h)->prev_hash->next_hash = (*h)->next_hash; + } + if(h == &page_hash_table[i]) { + *h = (*h)->next_hash; + } + kstat.cached -= (PAGE_SIZE / 1024); + break; + } + h = &(*h)->next_hash; + } +} + +static void remove_from_free_list(struct page *pg) +{ + pg->prev_free->next_free = pg->next_free; + pg->next_free->prev_free = pg->prev_free; + kstat.free_pages--; + if(pg == page_head) { + page_head = pg->next_free; + } +} + +void page_lock(struct page *pg) +{ + unsigned long int flags; + + for(;;) { + SAVE_FLAGS(flags); CLI(); + if(pg->locked) { + RESTORE_FLAGS(flags); + sleep(&pg, PROC_UNINTERRUPTIBLE); + } else { + break; + } + } + pg->locked = 1; + RESTORE_FLAGS(flags); +} + +void page_unlock(struct page *pg) +{ + unsigned long int flags; + + SAVE_FLAGS(flags); CLI(); + pg->locked = 0; + wakeup(pg); + RESTORE_FLAGS(flags); +} + +struct page * get_free_page(void) +{ + unsigned long int flags; + struct page *pg; + + /* if no more pages on free list */ + while(page_head == page_head->next_free) { + /* reclaim some memory from buffer cache */ + wakeup(&kswapd); + sleep(&get_free_page, PROC_UNINTERRUPTIBLE); + + if(page_head == page_head->next_free) { + /* definitely out of memory! (no more pages) */ + printk("%s(): pid %d ran out of memory. OOM killer needed!\n", __FUNCTION__, current->pid); + return NULL; + } + } + + SAVE_FLAGS(flags); CLI(); + + pg = page_head; + remove_from_free_list(pg); + remove_from_hash(pg); /* remove it from its old hash */ + pg->count = 1; + pg->inode = NULL; + pg->offset = 0; + + RESTORE_FLAGS(flags); + return pg; +} + +struct page * search_page_hash(struct inode *inode, __off_t offset) +{ + struct page *pg; + int i; + + i = PAGE_HASH(inode->inode, offset); + pg = page_hash_table[i]; + + while(pg) { + if(pg->inode == inode && pg->offset == offset) { + if(!pg->count) { + remove_from_free_list(pg); + } + pg->count++; + return pg; + } + pg = pg->next_hash; + } + + return NULL; +} + +void release_page(unsigned int page) +{ + unsigned long int flags; + struct page *pg; + + if(!valid_page(page)) { + PANIC("Unexpected inconsistency in hash_table. Missing page %d (0x%x).\n", page, page); + } + + pg = &page_table[page]; + if(--pg->count > 0) { + return; + } + + SAVE_FLAGS(flags); CLI(); + + if(!page_head) { + pg->prev_free = pg->next_free = pg; + page_head = pg; + } else { + pg->next_free = page_head; + pg->prev_free = page_head->prev_free; + page_head->prev_free->next_free = pg; + page_head->prev_free = pg; + } + + /* if page is not cached then place it at the head of the free list */ + if(!pg->inode) { + page_head = pg; + } + + kstat.free_pages++; + + RESTORE_FLAGS(flags); + wakeup(&get_free_page); +} + +int valid_page(unsigned int page) +{ + return (page >= 0 && page < NR_PAGES); +} + +void update_page_cache(struct inode *i, __off_t offset, const char *buf, int count) +{ + __off_t poffset; + struct page *pg; + int bytes; + + poffset = offset % PAGE_SIZE; + offset &= PAGE_MASK; + bytes = PAGE_SIZE - poffset; + + if(count) { + bytes = MIN(bytes, count); + if((pg = search_page_hash(i, offset))) { + page_lock(pg); + memcpy_b(pg->data + poffset, buf, bytes); + page_unlock(pg); + release_page(pg->page); + } + } +} + +int write_page(struct page *pg, struct inode *i, __off_t offset, unsigned int length) +{ + struct fd fd_table; + unsigned int size; + int errno; + + size = MIN(i->i_size, length); + fd_table.inode = i; + fd_table.flags = 0; + fd_table.count = 0; + fd_table.offset = offset; + if(i->fsop && i->fsop->write) { + errno = i->fsop->write(i, &fd_table, pg->data, size); + } else { + errno = -EINVAL; + } + + return errno; +} + +int bread_page(struct page *pg, struct inode *i, __off_t offset, char prot, char flags) +{ + __blk_t block; + __off_t size_read; + int blksize; + struct device *d; + struct buffer *buf; + + blksize = i->sb->s_blocksize; + size_read = 0; + + if(!(d = get_device(BLK_DEV, MAJOR(i->dev)))) { + printk("WARNING: %s: device major %d not found!\n", __FUNCTION__, MAJOR(i->dev)); + return 1; + } + if(!d->fsop || !d->fsop->read_block) { + printk("WARNING: %s: device %d,%d does not have the read_block() method!\n", __FUNCTION__, MAJOR(i->dev), MINOR(i->dev)); + return 1; + } + + pg->inode = i; + pg->offset = offset; + if(!(prot & PROT_WRITE) || flags & MAP_SHARED) { + while(size_read < PAGE_SIZE) { + if((block = bmap(i, offset, FOR_READING)) < 0) { + return 1; + } + if(block) { + /* does exist a buffer with recent data? */ + if(!(buf = get_dirty_buffer(i->dev, block, blksize))) { + if(d->fsop->read_block(i->dev, block, pg->data + size_read, blksize) < 0) { + return 1; + } + } else { + memcpy_b(pg->data + size_read, buf->data, blksize); + brelse(buf); + } + } else { + /* fill the hole with zeros */ + memset_b(pg->data + size_read, 0, blksize); + } + size_read += blksize; + offset += blksize; + } + /* cache all read-only and public (shared) pages */ + insert_to_hash(pg); + } else { + while(size_read < PAGE_SIZE) { + if((block = bmap(i, offset, FOR_READING)) < 0) { + return 1; + } + if(block) { + /* + * This feeds the buffer cache by reading only + * the writable pages which aren't included in + * the page cache. This will speed up things by + * keeping in buffer cache the writable pages + * with its original (disk) content (i.e. pages + * of the data section of an ELF). + */ + if(!(buf = bread(i->dev, block, blksize))) { + return 1; + } + memcpy_b(pg->data + size_read, buf->data, blksize); + brelse(buf); + } + size_read += blksize; + offset += blksize; + } + pg->inode = NULL; + pg->offset = 0; + } + return 0; +} + +int file_read(struct inode *i, struct fd *fd_table, char *buffer, __size_t count) +{ + __off_t total_read; + unsigned int page, poffset, bytes; + struct page *pg; + + inode_lock(i); + + if(fd_table->offset > i->i_size) { + fd_table->offset = i->i_size; + } + + total_read = 0; + + for(;;) { + count = (fd_table->offset + count > i->i_size) ? i->i_size - fd_table->offset : count; + if(!count) { + break; + } + + poffset = fd_table->offset % PAGE_SIZE; + if(!(pg = search_page_hash(i, fd_table->offset & PAGE_MASK))) { + if(!(page = kmalloc())) { + inode_unlock(i); + printk("%s(): returning -ENOMEM\n", __FUNCTION__); + return -ENOMEM; + } + page = V2P(page); + pg = &page_table[page >> PAGE_SHIFT]; + if(bread_page(pg, i, fd_table->offset & PAGE_MASK, 0, MAP_SHARED)) { + kfree((unsigned int)pg->data); + inode_unlock(i); + printk("%s(): returning -EIO\n", __FUNCTION__); + return -EIO; + } + } + + page_lock(pg); + bytes = PAGE_SIZE - poffset; + bytes = MIN(bytes, count); + memcpy_b(buffer + total_read, pg->data + poffset, bytes); + total_read += bytes; + count -= bytes; + poffset += bytes; + poffset %= PAGE_SIZE; + fd_table->offset += bytes; + page_unlock(pg); + kfree((unsigned int)pg->data); + } + + inode_unlock(i); + return total_read; +} + +void page_init(unsigned int pages) +{ + struct page *pg; + unsigned int n, addr; + + memset_b(page_table, NULL, page_table_size); + memset_b(page_hash_table, NULL, page_hash_table_size); + + for(n = 0; n < pages; n++) { + pg = &page_table[n]; + pg->page = n; + + addr = n << PAGE_SHIFT; + if(addr >= KERNEL_ENTRY_ADDR && addr < V2P(_last_data_addr)) { + pg->flags = PAGE_RESERVED; + kstat.kernel_reserved++; + continue; + } + + /* + * Some memory addresses are reserved, like the memory between + * 0xA0000 and 0xFFFFF and other addresses, mostly used by the + * VGA graphics adapter and BIOS. + */ + if(!addr_in_bios_map(addr)) { + pg->flags = PAGE_RESERVED; + kstat.physical_reserved++; + continue; + } + + pg->data = (char *)P2V(addr); + if(!page_head) { + pg->prev_free = pg->next_free = pg; + page_head = pg; + } else { + pg->next_free = page_head; + pg->prev_free = page_head->prev_free; + page_head->prev_free->next_free = pg; + page_head->prev_free = pg; + } + kstat.free_pages++; + } + kstat.total_mem_pages = kstat.free_pages; + kstat.kernel_reserved <<= 2; + kstat.physical_reserved <<= 2; +} diff --git a/mm/swapper.c b/mm/swapper.c new file mode 100644 index 00000000..cf138adc --- /dev/null +++ b/mm/swapper.c @@ -0,0 +1,62 @@ +/* + * fiwix/mm/swapper.c + * + * Copyright 2018, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* kswapd continues the kernel initialization */ +int kswapd(void) +{ + sprintk(current->argv0, "%s", "kswapd"); + + /* char devices */ + memdev_init(); + lp_init(); + + /* block devices */ + ramdisk_init(); + floppy_init(); + ide_init(); + + /* data structures */ + sleep_init(); + buffer_init(); + sched_init(); + mount_init(); + inode_init(); + fd_init(); + flock_init(); + + mem_stats(); + fs_init(); + mount_root(); + init_init(); + + for(;;) { + sleep(&kswapd, PROC_UNINTERRUPTIBLE); + if(reclaim_buffers()) { + continue; + } + printk("WARNING: %s(): out of memory and swapping is not implemented yet, sorry.\n", __FUNCTION__); + wakeup(&get_free_page); + } +}