Repository: mikaku/Fiwix Branch: master Commit: 58e1b84e14b1 Files: 407 Total size: 1.5 MB Directory structure: gitextract_xwvssabo/ ├── .gitignore ├── CODING ├── Changes ├── LICENSE ├── Makefile ├── README.md ├── THANKS ├── docs/ │ ├── devices.txt │ ├── initrd.txt │ ├── kernel-parameters.txt │ ├── kexec.txt │ ├── kmem_layout.txt │ ├── system-console.txt │ ├── tcc.txt │ └── video/ │ ├── font-lat9-8x14.txt │ ├── font-lat9-8x16.txt │ └── font-lat9-8x8.txt ├── drivers/ │ ├── block/ │ │ ├── Makefile │ │ ├── ata.c │ │ ├── ata_hd.c │ │ ├── ata_pci.c │ │ ├── atapi.c │ │ ├── atapi_cd.c │ │ ├── blk_queue.c │ │ ├── dma.c │ │ ├── floppy.c │ │ ├── part.c │ │ └── ramdisk.c │ ├── char/ │ │ ├── Makefile │ │ ├── charq.c │ │ ├── console.c │ │ ├── defkeymap.c │ │ ├── fb.c │ │ ├── keyboard.c │ │ ├── lp.c │ │ ├── memdev.c │ │ ├── ps2.c │ │ ├── psaux.c │ │ ├── pty.c │ │ ├── serial.c │ │ ├── sysrq.c │ │ ├── tty.c │ │ └── vt.c │ ├── pci/ │ │ ├── Makefile │ │ └── pci.c │ └── video/ │ ├── Makefile │ ├── bga.c │ ├── fbcon.c │ ├── font-lat9-8x14.c │ ├── font-lat9-8x16.c │ ├── font-lat9-8x8.c │ ├── fonts.c │ ├── vgacon.c │ └── video.c ├── fiwix.ld ├── fs/ │ ├── Makefile │ ├── buffer.c │ ├── devices.c │ ├── devpts/ │ │ ├── Makefile │ │ ├── dir.c │ │ ├── inode.c │ │ ├── namei.c │ │ └── super.c │ ├── elf.c │ ├── ext2/ │ │ ├── Makefile │ │ ├── bitmaps.c │ │ ├── dir.c │ │ ├── file.c │ │ ├── inode.c │ │ ├── namei.c │ │ ├── super.c │ │ └── symlink.c │ ├── fd.c │ ├── filesystems.c │ ├── inode.c │ ├── iso9660/ │ │ ├── Makefile │ │ ├── dir.c │ │ ├── file.c │ │ ├── inode.c │ │ ├── namei.c │ │ ├── rrip.c │ │ ├── super.c │ │ └── symlink.c │ ├── locks.c │ ├── minix/ │ │ ├── Makefile │ │ ├── bitmaps.c │ │ ├── dir.c │ │ ├── file.c │ │ ├── inode.c │ │ ├── namei.c │ │ ├── super.c │ │ ├── symlink.c │ │ ├── v1_inode.c │ │ └── v2_inode.c │ ├── namei.c │ ├── pipefs/ │ │ ├── Makefile │ │ ├── fifo.c │ │ ├── pipe.c │ │ └── super.c │ ├── procfs/ │ │ ├── Makefile │ │ ├── data.c │ │ ├── dir.c │ │ ├── file.c │ │ ├── inode.c │ │ ├── kmsg.c │ │ ├── namei.c │ │ ├── super.c │ │ ├── symlink.c │ │ └── tree.c │ ├── script.c │ ├── sockfs/ │ │ ├── Makefile │ │ ├── socket.c │ │ └── super.c │ └── super.c ├── include/ │ └── fiwix/ │ ├── asm.h │ ├── ata.h │ ├── ata_hd.h │ ├── ata_pci.h │ ├── atapi.h │ ├── atapi_cd.h │ ├── bga.h │ ├── bios.h │ ├── blk_queue.h │ ├── buffer.h │ ├── charq.h │ ├── cmos.h │ ├── config.h │ ├── console.h │ ├── cpu.h │ ├── ctype.h │ ├── devices.h │ ├── dirent.h │ ├── dma.h │ ├── errno.h │ ├── fb.h │ ├── fbcon.h │ ├── fcntl.h │ ├── fd.h │ ├── filesystems.h │ ├── floppy.h │ ├── font.h │ ├── fs.h │ ├── fs_devpts.h │ ├── fs_ext2.h │ ├── fs_iso9660.h │ ├── fs_minix.h │ ├── fs_pipe.h │ ├── fs_proc.h │ ├── fs_sock.h │ ├── i386elf.h │ ├── ioctl.h │ ├── ipc.h │ ├── irq.h │ ├── kd.h │ ├── kernel.h │ ├── kexec.h │ ├── keyboard.h │ ├── kparms.h │ ├── limits.h │ ├── linker.h │ ├── linuxboot.h │ ├── locks.h │ ├── lp.h │ ├── memdev.h │ ├── mm.h │ ├── mman.h │ ├── msg.h │ ├── multiboot1.h │ ├── net/ │ │ ├── ipv4.h │ │ ├── packet.h │ │ └── unix.h │ ├── net.h │ ├── netdevice.h │ ├── part.h │ ├── pci.h │ ├── pci_ids.h │ ├── pic.h │ ├── pit.h │ ├── process.h │ ├── ps2.h │ ├── psaux.h │ ├── pty.h │ ├── ramdisk.h │ ├── reboot.h │ ├── resource.h │ ├── sched.h │ ├── segments.h │ ├── sem.h │ ├── serial.h │ ├── shm.h │ ├── sigcontext.h │ ├── signal.h │ ├── sleep.h │ ├── socket.h │ ├── stat.h │ ├── statbuf.h │ ├── statfs.h │ ├── stdarg.h │ ├── stddef.h │ ├── stdio.h │ ├── string.h │ ├── syscalls.h │ ├── sysconsole.h │ ├── syslog.h │ ├── sysrq.h │ ├── system.h │ ├── termbits.h │ ├── termios.h │ ├── time.h │ ├── timeb.h │ ├── timer.h │ ├── times.h │ ├── traps.h │ ├── tty.h │ ├── types.h │ ├── unistd.h │ ├── ustat.h │ ├── utime.h │ ├── utsname.h │ ├── version.h │ ├── vgacon.h │ ├── video.h │ └── vt.h ├── kernel/ │ ├── Makefile │ ├── boot.S │ ├── cmos.c │ ├── core386.S │ ├── cpu.c │ ├── gdt.c │ ├── idt.c │ ├── init.c │ ├── irq.c │ ├── kexec.c │ ├── main.c │ ├── multiboot.c │ ├── pic.c │ ├── pit.c │ ├── process.c │ ├── sched.c │ ├── signal.c │ ├── sleep.c │ ├── syscalls/ │ │ ├── Makefile │ │ ├── access.c │ │ ├── alarm.c │ │ ├── brk.c │ │ ├── chdir.c │ │ ├── chmod.c │ │ ├── chown.c │ │ ├── chown32.c │ │ ├── chroot.c │ │ ├── close.c │ │ ├── creat.c │ │ ├── dup.c │ │ ├── dup2.c │ │ ├── execve.c │ │ ├── exit.c │ │ ├── fchdir.c │ │ ├── fchmod.c │ │ ├── fchown.c │ │ ├── fcntl.c │ │ ├── fcntl64.c │ │ ├── fdatasync.c │ │ ├── flock.c │ │ ├── fork.c │ │ ├── fstat.c │ │ ├── fstat64.c │ │ ├── fstatfs.c │ │ ├── fsync.c │ │ ├── ftime.c │ │ ├── ftruncate.c │ │ ├── ftruncate64.c │ │ ├── getcwd.c │ │ ├── getdents.c │ │ ├── getdents64.c │ │ ├── getegid.c │ │ ├── geteuid.c │ │ ├── getgid.c │ │ ├── getgroups.c │ │ ├── getitimer.c │ │ ├── getpgid.c │ │ ├── getpgrp.c │ │ ├── getpid.c │ │ ├── getppid.c │ │ ├── getrlimit.c │ │ ├── getrusage.c │ │ ├── getsid.c │ │ ├── gettimeofday.c │ │ ├── getuid.c │ │ ├── ioctl.c │ │ ├── ioperm.c │ │ ├── iopl.c │ │ ├── ipc.c │ │ ├── kill.c │ │ ├── lchown.c │ │ ├── link.c │ │ ├── llseek.c │ │ ├── lseek.c │ │ ├── lstat.c │ │ ├── lstat64.c │ │ ├── mkdir.c │ │ ├── mknod.c │ │ ├── mmap2.c │ │ ├── mount.c │ │ ├── mprotect.c │ │ ├── msgctl.c │ │ ├── msgget.c │ │ ├── msgrcv.c │ │ ├── msgsnd.c │ │ ├── munmap.c │ │ ├── nanosleep.c │ │ ├── newfstat.c │ │ ├── newlstat.c │ │ ├── newstat.c │ │ ├── newuname.c │ │ ├── old_mmap.c │ │ ├── old_select.c │ │ ├── olduname.c │ │ ├── open.c │ │ ├── pause.c │ │ ├── personality.c │ │ ├── pipe.c │ │ ├── read.c │ │ ├── readlink.c │ │ ├── readv.c │ │ ├── reboot.c │ │ ├── rename.c │ │ ├── rmdir.c │ │ ├── select.c │ │ ├── semctl.c │ │ ├── semget.c │ │ ├── semop.c │ │ ├── setdomainname.c │ │ ├── setfsgid.c │ │ ├── setfsuid.c │ │ ├── setgid.c │ │ ├── setgroups.c │ │ ├── sethostname.c │ │ ├── setitimer.c │ │ ├── setpgid.c │ │ ├── setregid.c │ │ ├── setreuid.c │ │ ├── setrlimit.c │ │ ├── setsid.c │ │ ├── settimeofday.c │ │ ├── setuid.c │ │ ├── sgetmask.c │ │ ├── shmat.c │ │ ├── shmctl.c │ │ ├── shmdt.c │ │ ├── shmget.c │ │ ├── sigaction.c │ │ ├── signal.c │ │ ├── sigpending.c │ │ ├── sigprocmask.c │ │ ├── sigreturn.c │ │ ├── sigsuspend.c │ │ ├── socketcall.c │ │ ├── ssetmask.c │ │ ├── stat.c │ │ ├── stat64.c │ │ ├── statfs.c │ │ ├── stime.c │ │ ├── symlink.c │ │ ├── sync.c │ │ ├── sysinfo.c │ │ ├── syslog.c │ │ ├── time.c │ │ ├── times.c │ │ ├── truncate.c │ │ ├── truncate64.c │ │ ├── umask.c │ │ ├── umount.c │ │ ├── umount2.c │ │ ├── uname.c │ │ ├── unlink.c │ │ ├── ustat.c │ │ ├── utime.c │ │ ├── utimes.c │ │ ├── wait4.c │ │ ├── waitpid.c │ │ ├── write.c │ │ └── writev.c │ ├── syscalls.c │ ├── timer.c │ └── traps.c ├── lib/ │ ├── Makefile │ ├── ctype.c │ ├── printk.c │ ├── string.c │ └── sysconsole.c ├── mm/ │ ├── Makefile │ ├── alloc.c │ ├── bios_map.c │ ├── buddy_low.c │ ├── fault.c │ ├── memory.c │ ├── mmap.c │ ├── page.c │ └── swapper.c ├── net/ │ ├── Makefile │ ├── core.c │ ├── domains.c │ ├── ipv4.c │ ├── packet.c │ ├── socket.c │ └── unix.c └── shell.nix ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.o /fiwix System.map.gz include/fiwix/custom_config.h include/fiwix/custom_limits.h include/fiwix/custom_kernel.h include/fiwix/custom_system.h ================================================ FILE: CODING ================================================ 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: - Keep lines length to a maximum of 80 characters (some exceptions accepted). - Use of snake_case (multi-word names are lower_case_with_underscores) for everything except for macro in #define directives. - No space after the name of a function in a call. For example, printk("hello") not printk ("hello"). - Optional space after keywords "if", "for", "while", "switch". For example, if(x) or if (x). - Space before braces (except in function declarations). 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*). - The pointer qualifier, '*', should be with the variable name rather than with the type. - Comments in code are always in C-Style (as in C89) /* ... */. - Only the comments that span multiple lines start always with a capital letter and the last sentence ends with a period. Also the open and close tags must go in separate lines. Just like this: /* * Comment line 1 * comment line 2 * comment line 3. */ - Comments in a single line must not start with a capital letter and must not end with a period. - Functions that take no arguments are declared as f(void), not f(). - The open parenthesis is always on the same line as the function name. - There is never a space between the function name and the open parenthesis. - There is never a space between the parentheses and the parameters. - The open curly brace is always at the next line of the function declaration. - Conditionals should always include curly braces, even if there is only a single statement within the conditional. - The parameters in function prototypes don't need to have a name to reference them by. - Case statements should be indented one from switch keyword indentation. Recommended for reading ------------------------------------------------------------------------------- - http://skull.piratehaven.org/~bapper/298c/cstyle.html ================================================ FILE: Changes ================================================ 1.8.0 - DD-MMM-2026 =================== - Added generic PCI Bus-Master DMA support on ATA disks. - Added support for tilde dead key. [#102] - Added the 'flags' entry in the 'kernel_params' structure to group all toggle- type parameters. - Added the new kernel parameter 'ide_nodma' to disable DMA in all ATA drives. - Added support to be able to right-justify strings in printk(). - Added support for some new functions in lib/string.c. - Changed the 'ps2_noreset' to a toggle parameter. - Changed the way how device drivers find PCI-type devices. - Moved ipc_init() and net_init() initialization into kswpad() to make sure the 'current' process stucture is completely setup. - Removed the file CREDITS because the LICENSE file already contains the list of contributors. - Removed the configuration option CONFIG_LAZY_USER_ADDR_CHECK. - Reimplemented atoi() to rely on strtol(). - Fixed the ATA disk initialitzation to reduce the number of messages like 'unexpected interrupt'. - Fixed the DMA functionality in ATA disks to use UDMA since the support for the Multiword DMA is not always assured in all cases. - Fixed ata_hd_init() to preserve the current value of Status in Bus-Master register while setting DMA. - Fixed ata_setup_dma() and ata_start_dma() to preserve the current register status before writing a new value. - Fixed to save the BIOS Data Area (256 bytes) to prevent it from being clobbered by the early use of the first memory page. - Fixed dump_registers() to be able to show the registres on an early kernel panic. - Fixed wakeup() to avoid flagging 'need_resched' on every iteration and thus saving extra context switchings. - Fixed ps2_init() to no use anymore the command 0xCA (to get the current interface) as it is not an standardized command. - Fixed the start_code and end_code values in '/proc//stat' file. - Fixed a bad vma address calculation in verify_address(). - Fixed verify_address() to not verify pointer addresses when calling system calls from the kernel. - Fixed strncmp() to actually compare only the first (at most) n bytes of str1 and str2. - Fixed the inode count in mmap() functions. - Fixed a possible infinite loop in do_munmap(). - Small fixes and improvements, code cleanup and cosmetic changes. 1.7.0 - 15-Nov-2025 =================== - Added support for UNIX98 pseudoterminals (pty). - Added support for the devpts filesystem. - Added support for system logging via sys_syslog() and /proc/kmsg. - Added the member s_blocksize_bits in the superblock struct to use bitwise shift instead of division in ext2_bmap() and ext2_truncate(). - Added the RSS column in the list of processes generated by the Magic SysRq key 't'. - Added the 'fd' structure as a new argument in ioctl() and select() methods. - Added a free data pointer called 'private_data', in 'fd' structure, which is mostly used by the tty driver for now. - Added support for the command TIOCINQ in tty_ioctl(). - Added the new kernel parameter 'ps2_noreset=' (which defaults to 0, disabled) to avoid reseting the PS/2 controller (specially useful on systems that don't has any PS/2 controller. - Added support for the '/proc/pci/ and '/proc/bus/pci/devices' files. - Added the support for the ioctls BLKSSZGET and BLKBSZGET on ATA and floppy drivers, and also added the missing BLKFLSBUF to the floppy driver. - Changed modulo operations by bitwise (where possible) to reduce dependency from libgcc. - Changed static array tty_table to dynamic. - Rewritten mostly the PCI driver. - Removed some flags from LDFLAGS in the main Makefile that prevented compile the kernel with newer GCC versions. [#97] - Removed the dir_write method from the ext2 and minix filesystems. - Removed a condition in do_sched() because it has no effect. - Removed an unneeded assignment to need_resched in do_exit() and reduced the abuse of using need_resched because it costs excessive context switches. - Renamed the functions *_dir_readdir() and *_dir_readdir64() to *_readdir() and *_readdir64(). - Renamed all *fd_table function arguments to avoid shadowing the global fd_table array. - Simplified a do.while() in reclaim_siblings() also with small improvements in reclaim_buffers(). - Reverted 49393d0 and fixed 'memksize' in all cases to be able to boot again on systems with 4MiB of memory. - Reverted 39c0097 because an existing bug prevents a complete installation on systems with less than 64MiB of memory. - Update LICENSE file with additional contributor information. - Fixed ext2_balloc() and ext2_ialloc() to return -ENOSPC when there is not space on device. - Fixed fs.h to include fd.h when CONFIG_NET is not set. - Fixed some kernel crashes with the message 'WARNING: page_head returned NULL! (free_pages = 0)' when using a RAMdisk drive as the root filesystem. [#98] - Fixed ext2_file_write() to avoid to brelse() a NULL buffer. [#98] - Fixed sys_open() to catch early an attempt to write on a directory. - Fixed to add some missing iput() in procfs_lookup() and procfs_followlink(). - Fixed sys_mount() to also save the mount point for nodev filesystems. - Fixed an unintended fall-through in set_color() of fbcon.c. - Fixed a long standing bug in send_sig() that missed to add SIGCONT, SIGWINCH and SIGUSR to the list of signals to ignore when the disposition is set to SIG_DFL. - Fixed an always true comparison in get_procfs_by_inode(). - Fixed a long standing bug in sys_kill() that prevented from sending the signal zero. - Fixed a long standing bug in sys_select() that returned always -EBADF when 'nfds' is equal to FD_SETSIZE. - Fixed a long standing bug in tty_read() that always returned VMIN bytes regardless of the amount of data available. - Fixed a possible null pointer dereference in proc_list(). - Fixed a long standing bug in sys_select() that didn't return -EINTR if it was interrupted by a signal. - Fixed sys_fork() to share the remaining CPU time of the parent with the new child. - Fixed to avoid a division by zero in SLEEP_HASH() macro when NR_PROCS is less than 10. - Fixed a long standing bug in data_proc_pid_cmdline() and data_proc_pid_environ() functions that led to the kernel panic. - Fixed an accidental fall through in opost(). - Fixed a missing iput() when freeing or merging a vma region. - Fixed get_free_buffer() to not create new buffers if free memory pages are lower than minimal. - Fixed inefficient code by reducing excessive calls to wakeup(&buffer_wait) in fs/buffer.c. - Fixed a bug in sys_open() that allowed to create a file on a read-only filesystem. - Small fixes, code cleanup and cosmetic changes. 1.6.0 - 15-Nov-2024 =================== - Added support for UNIX-domain sockets (with the Berkeley socket API). [#36] - Added support for sys_truncate64, sys_ftruncate64, sys_stat64, sys_lstat64, sys_fstat64 and sys_fcntl64 system calls. [#49] - Added support for sys_getdents64. [#51] - Added support for sys_chown32. [#53] - Added support for sys_utimes. [#57] - Added support for building Fiwix with the TCC compiler. [#63] - Added support for the Linux boot protocol in kexec. [#65] - Added support for the PS/2 mouse with the new device /dev/psaux. [#94] - Added the new configuration option PAGE_HASH_PERCENTAGE (0.1% by default) to set the size of the page_hash_table relative to the number of physical memory pages, with a mininum of 1 page and a maximum of 16 pages. The kernel now shows the hash tables sizes during the boot. - Added to sync drives when kernel stops because no more user processes exist. [#70] - Added the flag PF_NOTINTERRUPT to avoid waking up a process sleeping in the non-interruptible mode. - Added a consistency check on every directory entry during ext2_lookup(). - Added a call to invalidate_buffers() before reread the partition table in the ioctl BLKRRPART. - Rewritten how multiple I/O block requests are managed. Now the new I/O block layer enqueues a block request group with all the buffers needed in a read or write operation, and keeps sleeping until the whole transaction has been completed. This improves dramatically the disk accesses, reduces a lot the number of context switches, and it overall boosts the performance of the system. - Changed the file position for reads to be set to zero when a file is opened with O_APPEND. [#76] - Implement mapping framebuffer physical address to user space using mmap. [#79] - Changed the inode cache mechanism to avoid caching inodes from pseudo-filesystems. - Changed count_active_procs() to also count processes that are sleeping as non-interruptible. - Changed the tty sleeping address to be process specific in order to reduce the wakeup latency. - Moved the values sb->dirty, inode->locked and inode->dirty to flags. - Moved the call to sysrq() into the keyboard interrupt bottom half. - Improved code compaction and efficiency in ATA disk read/write. - Improved the efficiency of the buffer cache. - Improved ext2_file_write() to use a multiblock request with gbread() when the count is greater than the blocksize. - Reorganized and improved the system console related code. - Pass through 64-bit PAE memory entries as part of kexec. [#67] - Make sure that the early system log will be shown in all system consoles. - Removed unneeded low memory kernel virtual address mappings. [#88] - Reduced a number of functions in the timer interrupt by moving them to the bottom half. - Reorganized the code of the keyboard driver. - Renamed and reorganized tty_queue into charq to make it more generic. - Fixed sys_mount() to avoid mounting a device already mounted. - Fixed compilation errors when CONFIG_PCI is not defined. - Fixed to allow removing a directory in use. [#59] - Fixed in sys_getcwd() to check 'diff_dev' variable before accessing 'tmp_inode'. [#61] - Fixed vconsole_select() to not switch to an unexistent vconsole. - Fixed ata_hd_init() to make sure that the command SET_FEATURES is sent in all cases. - Fixed the kernel stack backtrace to skip the first 5 stack values. - Fixed an scrolling bug with 'vi' in fbcon_scroll_screen(). [#81] - Fixed UNIX sockets working with select(). [#83] - Fixed serial system console table overindex. [#87] - Fixed a major problem in do_switch routine. [#89] - Fixed incorrect passing of e820 memory map to Linux kexec guests. [#72] - Fixed EXT2_DESC_PER_BLOCK() to avoid redundant calculations. - Fixed kswapd() to enable interrupts after initializing devices. - Fixed a long standing bug during bitmap calculation in the ext2 filesystem. - Fixed CHECK_IF_NESTED_INTERRUPT by comparing KERNEL_CS against CS. - Fixed a kernel crash on boot when PCI is disabled and the kernel parameter 'bga=' is supplied. - Fixed sleep() to avoid race conditions by executing CLI() earlier. - Fixed a bug introduced in 4317cbe that prevented from continuing to the next running process in stop_kernel(). - Fixed read_msdos_partition() to use the default device block size instead of BLKSIZE_1K. - Fixed to flag the superblock as dirty in ext2_ialloc(), ext2_ifree(), ext2_balloc() and ext2_bfree(). - Fixed fbcon_blank_screen() to not show cursor. [#94] - Fixed a lack of schedule randomization introduced in b935b3d. - Fixed reclaim_buffers() to run with interrupts enabled. - Fixed to initialize the new vma struct in merge_vma_regions(). - Fixed to reset the flag before calling some functions to avoid reentrancy in irq_keyboard_bh(). - Fixed a long standing bug in vconsole_select() that prevented from switching between consoles in VT_PROCESS mode. - Fixed permissions in the ioctl VT_WAITACTIVE. - Fixed a long standing bug in wakeup() that put as running one in two processes sleeping, leading to frequent cases where some processes keep sleeping. - Small fixes and cosmetic changes. 1.5.0 - 15-Nov-2023 =================== - Added a kernel memory allocator that uses the buddy algorithm to manage requests smaller than PAGE_SIZE (4KiB). - Added the new file /proc/buddyinfo to view buddy algorithm statistics. - Added the new configuration option FREE_PAGES_RATIO (with a default value of 5), as the minimum percentage of free memory pages before calling the swapper to reclaim memory from the buffer cache. - Added the buffer-dirty-flush kernel daemon 'kbdflushd'. This also includes the new configuration option BUFFER_DIRTY_RATIO to limit the percentage of dirty buffers. - Added the new file '/proc/sys/vm/dirty_background_ratio' to show the current value of the option BUFFER_DIRTY_RATIO. - Added support for the Bochs Graphics Adapter with the configuration option CONFIG_BGA, and introducing the new kernel parameter 'bga=' to set the screen resolution (width x height x bpp). Refer to 'docs/kernel-parameters.txt' to know all the screen resolutions supported. - Added 32bit I/O transfers support to the ATA/ATAPI driver. - Added DMA (Multi-word) transfers support to the ATA disk driver. - Added overall improvements in the ATA disk driver. - Added a check in psig() to terminate the process if it doesn't have the stack region in its vma table. - Added PCI BARs handling in the serial driver. - Added the files 'buffer_max' and 'buffer_nr' in '/proc/sys/kernel/'. - Added more checks on data received during ATA drive identify to make sure it makes sense. - Added the magic SysRq key 'm' to show current memory information. - Added the Multiboot magic value as a new parameter in get_last_boot_addr() to avoid a kernel panic when it is booted without the Multiboot structure. - Added a check if flag MULTIBOOT_INFO_ELF_SHDR is set before reading the ELF section header table to avoid possible kernel panics. - Added support in parse_cmdline() to recognize values enclosed in double quotes. - Added support for multiple RAMdisk drives. - Added kexec implementation (see 'docs/kexec.txt') which introduces the new configuration option CONFIG_KEXEC (disabled by default). - Added microsecond resolution to sys_gettimeofday(). - Added the /proc//fd/ subdirectory containing symbolic links named by its file descriptor. - Added support for 64bit offsets and introduced the new option CONFIG_OFFSET64 (enabled by default). - Added the linkage of libgcc into the kernel in order to have functions like __divdi3() and friends, necessary when doing certain arithmetic operations with 64bit offsets on an i386 architecture. - Added support for the O_DIRECTORY flag to sys_open(). [#32] - Added support for the virtual memory split 2GiB(user)/2GiB(kernel), by introducing the new option CONFIG_VM_SPLIT22 (disabled by default). [#34] - Added support for large initrd images (see 'docs/initrd.txt'). [#34] - Added the ability to have different blksize values for each device minor. - Added the new system call sys_lchown by renaming the old sys_chown (number 16) to sys_lchown, and creating the new sys_chown (number 182) which will follow (dereference) symbolic links. This might break the compatibility with Linux 2.0 ABI. - Added support for F_DUPFD_CLOEXEC in sys/fcntl.h. [#40] - Added support for sys_readv() and sys_writev() system calls. [#42] - Added support for sys_mmap2() system call. [#47] - Added the length modifier 'l' for long long int arguments in printk(). - Changed the mode how the kernel mounts the root filesystem. From now on, it will be mounted in read/write mode by default. This introduces the new kernel parameter 'ro' to force to mount it in read-only mode. - Changed the vma table from static to dynamically allocated array and removed the option VMA_REGIONS. - Changed lots of fixed-size arrays to dynamically allocated arrays. - Changed malloc_name() and free_name() to use the new memory allocator. - Changed do_namei() to use the new memory allocator. - Changed the generic llseek() method to support 64bit offsets. - Changed the location of the kernel stack address to be right after the BSS section. [#34] - Rewritten reclaim_buffers() to be more efficient and also to free up unused buffers and shrink the buffer table. - Modified the Makefile to disable compiling with PIE option enabled by some builds of GCC. [#6] - Reverted 40bf18b because some signals weren't caugth and created malfunction in cron jobs. - Reverted 082b316 as it created a kernel panic when using IPC shared memory. - Reverted ad3fc56 so malloc_name() and free_name() will always request PAGE_SIZE bytes. This is more efficient as it helped to remove the call to strlen() and even can return now a proper ENAMETOOLONG error. - Disabled interrupts in runnable() and not_runnable() to avoid race conditions. - Removed the option RAMDISK_MAXSIZE from the RAMdisk drives. - Bar addresses of PCI devices are now discovered by the driver. - Reorganized _init() functions in start_kernel(). - Disk drives now show the ATA major version number. - Reorganized the steps to do on a page fault in kernel mode. - The kernel parameter 'ramdisksize=' no longer controls the size of the initrd image. [#34] - The build of the Minix filesystem depends on the new option CONFIG_FS_MINIX (disabled by default). - Fixed a bug in syscalls/wait4.c introduced in 9cfe372 that created malfunction in control jobs. - Fixed to call sleep_init(), buffer_init(), sched_init() and inode_init() before initializing devices. - Fixed the offset value in elf_load_interpreter() and elf_load(). - Fixed in pci_add_device() to use pci_read_short() instead of pci_read_char() for Command and Status registers. - Fixed in do_execve() to reuse tmp_name allocated in sys_execve(). - Fixed a number of common variable definitions that prevented from build Fiwix with the newest GCC cross-compiler. [#10] - Fixed in fs/namei.c to return ENOENT for missing directory. [#12] - Fixed a bug in ext2_bmap() that prevented large files from persist on an ext2 filesystem. [#14] - Fixed a bug in v2_minix_bmap() that prevented large files from persist on a minix v2 filesystem. [#14] - Fixed do_page_fault() to handle better a faulty address outside the user address space in kernel mode. - Fixed the I/O address size in the ISA serial driver. - Fixed a missing iput() in ext2_followlink() when there are too many nested symbolic links. - Fixed a missing iput() in minix_followlink() when there are too many nested symbolic links. - Fixed the hexadecimal values of /dev/tty10, /dev/tty11 and /dev/tty12 in kparms.h. - Fixed some possible race conditions in buffer cache. - Fixed a bug in mmap that prevented overlapped VMA regions from merging correctly. [#16] - Fixed to terminate properly a read or write request on hdd timeout. - Fixed a race condition in sys_nanosleep() when setting the processs sleeping time. - Fixed merge_vma_regions() to free memory pages in overlapped segments. [#16] - Fixed memory mapped files not written to disk correctly. [#18] - Fixed sys_execve() to make sure arguments and environment do not exceed the maximum length. [#20] - Fixed sys_mount() to use malloc_name() and free_name() to validate the argument 'fstype'. - Fixed do_page_fault() to handle a page fault when trying to access a non-existent user stack address in kernel mode when CONFIG_LAZY_USER_ADDR_CHECK is enabled. [#22] - Fixed verify_address() to accept a possible non-existent user stack address when CONFIG_LAZY_USER_ADDR_CHECK is disabled, since do_page_fault() will do the rest of the job. [#22] - Fixed the logic in parse_arg() to support kernel parameters without arguments. - Fixed a long standing bug where after removing an inode there was not a call to invalidate all its pages from the cache. [#24] - Fixed a missing initialization in the PCI structure during scan_bus(). [#25] - Fixed bread() to check if the bytes read matches with what was requested. - Fixed premature output of information from the serial driver when acting as the remote console. - Fixed frame buffer memory spaces to be marked as reserved if they conflict with available memory. - Fixed sys_select() to return in the timeout argument the amount of time not slept. This is Linux specific but POSIX permits this behavior. - Fixed Magic SysRq key 't' to also list zombie processes. - Fixed Magic SysRq keys to work even when the tty has not been opened yet. - Fixed get_free_page() to reduce the excessive number of iterations with reclaim_buffers() when running out of memory pages. - Fixed the mechanism to sleep all processes in stop_kernel(). - Fixed the sleep address in page_lock() to match with page_unlock(). - Fixed a kernel panic if irq_keyboard() receives an unrecognized function key. - Fixed to correctly disable the RAMdisk device if there is not enough physical memory. - Fixed an inode-related race condition in iget() and iput(). - Fixed to not use the buffer after failed to be synced in sync_one_buffer(). - Fixed in ext2_symlink(), ext2_mkdir(), ext2_mknod() and ext2_create() to make sure the inode doesn't exist. - Fixed in minix_symlink(), minix_mkdir(), minix_mknod() and minix_create() to make sure the inode doesn't exist. - Fixed a deadlock between getblk() and kbdflushd(). [#27] - Fixed a missing lock that led to a possible memory page corruption bug in bread_page(). [#27] - Fixed atapi_cd_init() to setup the correct block size in the device structure. - Fixed sys_ftruncate() to return EINVAL if its method does not exist. - Fixed sync_superblocks() to sync all superblocks if argument is zero. - Fixed the size on every symlink in procfs to 64 bytes. - Fixed iget() to avoid locking when read_inode() returns an error. - Fixed a missing call to sync_buffers() in close() methods of RAMdisk drive and floppy drive. - Fixed serial_set_termios() to avoid a division by zero. - Fixed script_load() with || instead of && that led to incorrectly recognize as script when there is an incomplete shebang. - Fixed a linked list corruption when messages are retrieved not sequentially in sys_msgrcv(). - Fixed ata_hd_init() to not fail build if CONFIG_PCI is #undef. - Fixed padding in fbcon_insert_char() to use character 0x20 instead of 0x00. - Fixed vgacon_scroll_screen() to use double-buffering. - Small fixes and cosmetic changes. 1.4.0 - 15-Nov-2022 =================== - Added support for the PCI local bus. - Added support for the UNIX System V IPC mechanisms (semaphores, message queues and shared memory). - Added the character device /dev/full. - Added the character device /dev/port. - Added the system call sys_getcwd. [#4] - Added the configuration option CONFIG_QEMU_DEBUGCON to include support for the QEMU Bochs-style debug console. - Improved the serial driver to support QEMU PCI serial devices. - Removed the extra lock when dispatching the bottom halves. - Mask the interrupt during the execution of the interrupt handler. - Reorganized the code to separate interrupt related functions from specific pic functions. - Moved set_leds() into the bottom half of the keyboard interrupt. - Moved the start and stop tty functions into the bottom half of the keyboard interrupt. - Removed the command to detect the interface type of the keyboard. - Removed the kernel parameter 'noramdisk', so from now on the RAM disk driver is disabled by default. Use the kernel parameter 'ramdisksize=' to configure and enable it. - Reversed the check order of schedule and signals before leaving the kernel space. - Improved the cooperation with reclaim_buffers() when kernel runs out of memory pages. - Added the functions inport_l() and outport_l() in core386.S. - Added the sleeping address value in the SysRq process listing. - Added the configuration option CONFIG_SYSCALL_6TH_ARG (disabled by default), to enable the 6th argument in the system calls. - Added the configuration option CONFIG_LAZY_USER_ADDR_CHECK to relax the number of checks in the linear address of a system call parameter. - Reorganized the signal functions to avoid that signals sent by the kernel have to use the same checking mechanism as if they were sent by a user process. This also fixes a permission problem when sending the SIGCHLD signal to a parent process owned by a different user, during the context of sys_exit(). - IDE driver now shows the PIO mode instead of UDMA mode, since the later is not supported yet. - IDE driver now will use the current logical values if they are valid in the identify field. - Changed the bios memory map functions to show that the ending address is inside each range. - Changed sys_execve() to use the first argument instead of argv[0]. - Reverted 75bb49d and 4ecf9dd, as the PC emulator 'copy.sh/v86' (maybe others) requires to select the drive on every command. - Fixed the slowness during the system boot-up caused by the keyboard driver not using interrupts during its own initialization. - Fixed ext2_truncate() to return the error code after calling free_dblock(). - Fixed v1_minix_truncate() and v2_minix_truncate() to return the error code after calling free_zone(). - Fixed ext2_truncate() to correctly free doubly-indirect blocks. - Fixed v1_minix_truncate() and v2_minix_truncate() to correctly free doubly-indirect blocks. - Fixed ext2_truncate() to free triply-indirect blocks. - Fixed v2_minix_truncate() to free triply-indirect blocks. - Fixed the way how the I/O permission bitmap in TSS is implemented. - Fixed the implementation of the sys_ioperm() system call. - Fixed the execution permission logic that let root user execute any file. - Fixed to handle SysRq key inside keyboard interrupt instead of in its bottom half. - Fixed a missing inode release in elf_load() when argument list exceeds ARG_MAX on an ELF binary that requires an interpreter. - Fixed a possible NULL pointer dereference with elf32_ph in elf_load() and elf_load_interpreter(). - Fixed the type of the user file descriptor (to unsigned int) in a number of system calls. - Fixed a possible index out of bounds in bios_map_add(). - Fixed a missing validation in the arguments of sys_execve(). - Fixed an inefficiency in the mechanism of ext2_dir_readdir() and minix_dir_readdir(). - Fixed the function get_last_boot_addr() to return a proper address even when there are no ELF header tables. This mostly happens when the kernel is loaded using the QEMU argument '-kernel'. - Fixed a possible out of bounds access in CHECK_UFD() macro. - Fixed a possible out of bounds in utsname structures. - Fixed UTS_MACHINE to return always 'i386' in all processor types on the i386 architecture. - Fixed do_printk() to show correctly the value of the second identifier when there are two consecutive identifiers - Fixed to show the size of an ATA drive in KB if it's less than 1MB. - Fixed to use the standard algorithm to convert LBA to CHS in the ATA driver. - Fixed the TCSETSW, TCSETSF, TCSETAW and TCSETAF ioctl functions, so now they wait output buffer to drain before apply the new settings. - Fixed a missing update of the termios structure during the ioctl functions: TCSETA, TCSETAW and TCSETAF - Fixed sys_open() to check directory permissions only if the file does not exist and O_CREAT has been specified. - Fixed callouts bottom half to avoid nesting. - Fixed a bug introduced in 8eaed51 that prevented initrd images from working. [#7] - Small fixes and cosmetic changes. - Cleaned up a lot of code. 1.3.0 - 08-Dec-2021 =================== - Added support for interrupt nesting. - Added framebuffer device support for VESA VBE 2.0+ compliant graphics cards. - Added support for the framebuffer console (fbcon). - Completely rewrite the console to support VGA text and framebuffer screens. - Improved the speed of VGA text consoles by using double-buffering and fixing also a fair number of bugs. - Updated the header file to support the latest GRUB Multiboot Specification v1. - Moved 'cpu_idle()' to a C function to include more easily some tasks to do. - Isolated the multiboot code in order to prepare the kernel to support multiple bootloader protocols. - Arrange boot.S to be compatible only with GRUB Multiboot Specification v1. - Changed from 0x90000 to 0x50000 the memory location of the 4MB temporary Page Directory to be able to hold bigger initrd images. - Changed from 5% to 1% the amount of memory that will use the inode table. - Added the ability to insert extra regions to the memory map provided by the boot loader. - Added support to detect non-contiguous areas of available memory in the BIOS memory map. - Added support for the 'magic SysRq key' to help to know the cause of a kernel freeze on a disaster situation. - Added some basic checks on data received during IDE identify to make sure it makes sense. - Added support to handle two ATA/ATAPI devices in the same IDE controller. - Reduced the size of the buffer area used in floppy I/O operations. - Increased to 50 the maximum of BIOS memory map entries. - Removed CLI() functions in ide_hd and ide_cd. - Improved the handling of the inode free list and the locking mechanism. - Improved the handling of buffer and memory page free lists. - Improved the way how is counted the first megabyte of memory. - Changed bread_page() to reuse buffers and reduce disk I/O activity. - Honour POSIX.1-2008 by returning ELOOP if flag O_NOFOLLOW is set and the trailing component (basename) of the pathname in open() is a symbolic link. - Disabled interrupts during the context switch. - Moved the values buffer->valid, buffer->locked and buffer->dirty to flags. - Moved the value pg->locked to a flag. - Changed kernel_process() to accept also the name of the process. - Removed a sizeof(void) in sys_signal() as it is not covered by the C standard. - Disabled interrupts during early boot up. - Added the configure option CONFIG_VERBOSE_SEGFAULTS (disabled by default). - Added a separate queue for all running processes. - Disabled default screen blanking. - Added a new linked list in the buffer structure to better handle the dirty buffers. - Added the 'Dirty' line in '/proc/meminfo' to keep track the amount of memory waiting to be written back to the disk. - Wakeup the INIT process (if it's sleeping on sys_wait4()) if one of the new processes inherited during the sys_exit() call is a zombie. - Saved some CPU cycles in irq_handler() by isolating unused IRQs. - Fixed the RAMdisk driver to not access blocks beyond its size. - Fixed a race condition in floppy drive when the interrupt occurred right before going to sleep. - Fixed a bug during the RAMdisk memory initialization that reserved twice the size of the initrd image. - Fixed to return EISDIR when trying to read a directory in procfs. - Fixed a memory corruption bug in procfs_readlink() that was leading to instability in user programs that read the procfs. - Fixed to use a simple assignment in elf_create_stack() instead of using a function to copy a numeric value. - Fixed a long standing memory corruption bug when returning the contents of the '/proc//cmdline' file. - Fixed a possible memory corruption bug when returning the contents of the '/proc//environ' file. - Fixed a missing queue handling in the serial driver. - Fixed to remove the calls to reset termios and tty on tty_close(). - Fixed an extra buffer_head assignment in get_free_buffer() which reduced to 50% the buffer space, and could also potentially use an already locked buffer. - Fixed a very long standing buffer corruption bug, in rename functions of ext2 and minix filesystems, that led to crashes with the message 'getblk(): no more buffers on free list!' after a heavy filesystem use. - Fixed the tty_queue_room() function and aligned to 8 the number of cblocks per queue. - Fixed the way how release_page() and reclaim_buffers() cooperate when there are no more free memory pages, in order to avoid killing the current process prematurely. - Fixed to avoid sleeping during console_flush_log_buf(). - Fixed to return ENOSPC when trying to write beyond the size of a block device. - Fixed get_proc_by_pid() to not PANIC anymore if the process didn't exist. - Fixed to avoid multiple calls to tty_queue_init(). - Fixed to return ENXIO when the device does not exist. - Fixed the CSI J Erase in Display (ED) and other sequences in console. - Fixed /dev/kmem to not access beyond the end of the virtual memory. - Fixed to make sure that disk partitions with status value other than 0x00 and 0x80 are invalid. - Fixed a race condition in ATA identify/read when the interrupt occurred right before going to sleep. - Fixed a buffer overrun in iso9660_dir_readdir(). - Fixed a bug in iso9660_dir_readdir() that prevented from including some files in the user dirent structure under certain conditions. - Fixed a very long standing bug in the memory page hashing mechanism that led to kernel freezes after a long use. - Fixed a very long standing bug in all inode lookup functions that ate an extra inode on every call, leading to the premature message "WARNING: no more inodes on free list!". - Fixed certain values to be signed instead of unsigned in kstat structure. - Fixed a bug in tty->canon_data that corrects a bad behavior in select(). - Fixed to decrease rss memory from the zombie process instead from its parent in remove_zombie(). - Fixed to name correctly the IDLE process by assigning 'idle' to argv0. - Fixed to honour the select() function when handling pipes. - Fixed to reduce over-scheduling in tty_write() by calling do_sched() only if needed. - Fixed a long standing bug in do_exit() that made some processes returning from the death. - Fixed the inode reference counter when moving directories (sys_rename) in ext2 and minix filesystems. - Fixed to update the parent directory when moving directories (sys_rename) in ext2 and minix filesystems. - Fixed to wake up all processes in select() after a console_write(). - Fixed an incomplete copy of in-memory superblock data during a remount from RO to RW, in ext2 and minix filesystems, that led to not save all changes in superblock. - Fixed to decrease the number of children either when the process is exiting, and from the correct parent after reaping a zombie process. - Fixed a long standing bug, in ext2_dir_readdir() and minix_dir_readdir(), that prevented from listening directory entries if there were blank blocks in the middle. - Small fixes and cosmetic changes. - Cleaned up a lot of code. 1.2.0 - 12-Dec-2020 =================== - Added support for serial (RS-232) devices. - Completely rewritten the way how the device table is handled. - Completely rewritten the way how the interrupt table is handled. - Added the ability to share the same IRQ between multiple devices. - Completely rewritten the way how bottom halves are managed. - Added the 'doc/devices.txt' document. - Added basic functionality of OPOST functions to tty. - Added to reschedule the keyboard bottom-half if it was busy. - Added support of O_NONBLOCK in tty_write(). - Added remote serial console support. - Added support for K_RAW and K_MEDIUMRAW keyboard modes. - Added and fixed some ECMA-48 CSI sequences in console. - Improved termios and other aspects of the tty driver to accomodate the new serial driver. - Small optimizations and bug fixes in console. - Improved the macros that build the interrupt service routines. - Fixed to force some signals that a process cannot ignore by changing their dispositions to SIG_DFL. - Fixed a bug in tty_read and tty_write that caused to send the SIGTTIN signal to the foreground process. - Fixed nanosleep() to force to 10ms any request lower than 10ms. - Fixed a signedness bug in setreuid() and setregid(). - Fixed to delimitate better the stack-related page faults in the vma region. - Fixed a NULL pointer dereference when __DEBUG__ is enabled in sys_waitpid(). - Fixed to avoid freeing an invalid page that generated PANICs in SVGAlib programs. - Fixed a bug that, under certain conditions, page_not_present() unmapped a wrong page number. - Fixed to not clone MAP_SHARED pages during fork(). - Saved twice the 'signum' value before calling the signal handler. - Small fixes and cosmetic changes. 1.1.0 - 24-Mar-2020 =================== - Added support for an initial RAMdisk (initrd) image. - Added full support for the ext2 filesystem. - Added support to execute scripts. - Limit the number of messages on spurious interrupts. - RAMdisks initialization now shows the range of the memory addresses of each RAMdisk created. - Added VMA_REGIONS as a general configurable option (default: 150 entries). - Changed the default kernel filesystem to ext2. - Renamed the sleep addresses in tty.c. - Added the ability to pass arguments to init from the kernel's command line. - Included memory values in /proc/PID/stat and /proc/PID/status. - Included the file /proc/PID/statm to provide more information about memory usage. - Changed the ATA's SET MULTIPLE MODE command to do all I/O with 4KB block size (8 sectors) by default. - Fixed to prevent from using an unexistent floppy type, in the fdd_type structure, if CMOS returns a value bigger than 0x04. - Fixed a misplaced bitwise operator AND that was not ensuring the limitation to 1GB as the amount of physical memory supported. - Fixed some bugs in *_minix_ialloc() and minix_balloc() when they fail. - Fixed a malfunction in the job control. - Fixed in sys_getgroups() to return the number of group IDs when the argument size is zero. - Fixed the DEC Set Top and Bottom Margins (DECSTBM) sequence, which affected the scroll up mechanism in consoles. - Fixed to set properly the timestamp on every read and write in all ttys. - Fixed the start time value in /proc/PID/stat of every single process. - Fixed to use a long long variable type to be able support block devices bigger than 4GB. - Fixed a memory corruption bug processing the VT100 'CSI n J' sequence. - Small fixes and cosmetic changes. 1.0.1 - 04-Aug-2018 =================== - Added a basic implementation of a Pseudo-Random Number Generator using the character devices /dev/random and /dev/urandom. - Improved the mechanism in procfs_dir_readdir() to fix a memory corruption bug. - Fixed a bug in procfs_dir_readdir() that prevented from reading correctly the entire procfs root directory. - Fixed a bug in iso9660_dir_readdir() that prevented from reading correctly any directory. - Fixed to show the complete pathname in the argv[0] of the init process. - Fixed to protect INIT process from unexpected signals. 1.0.0 - 23-Apr-2018 =================== - Fixed a bug in reclaim_buffers() to avoid kernel freeze. - Fixed a corruption bug when creating the dirent structure in the dir_readdir() functions. - Massive code cleaning. - New system calls: sys_select ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2018 Jordi Sanfeliu, et al. 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. -- Authors/contributors include: Richard R. Masters Greg Haerr Alwin Berger Gábor Stefanik The following notice is included for code incorporated from the Limine Project: Copyright (C) 2019-2023 mintsuki and contributors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: Makefile ================================================ # fiwix/Makefile # # Copyright 2018-2022, 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 TMPFILE := $(shell mktemp) ARCH = -m32 CPU = -march=i386 LANG = -std=c89 # CCEXE can be overridden at the command line. For example: make CCEXE="tcc" # To use tcc see docs/tcc.txt CCEXE=gcc CC = $(CROSS_COMPILE)$(CCEXE) $(ARCH) $(CPU) $(LANG) -D__KERNEL__ $(CONFFLAGS) #-D__DEBUG__ CFLAGS = -I$(INCLUDE) -O2 -fno-pie -fno-common -ffreestanding -Wall -Wstrict-prototypes #-Wextra -Wno-unused-parameter ifeq ($(CCEXE),gcc) LD = $(CROSS_COMPILE)ld CPP = $(CROSS_COMPILE)cpp -P -I$(INCLUDE) LIBGCC := -L$(shell dirname `$(CC) -print-libgcc-file-name`) -lgcc LDFLAGS = -m elf_i386 endif ifeq ($(CCEXE),tcc) LD = $(CROSS_COMPILE)$(CCEXE) $(ARCH) LDFLAGS = -static -nostdlib -nostdinc # If you define CONFIG_VM_SPLIT22 this should be 0x80100000: make CCEXE="tcc" TEXTADDR="0x80100000" TEXTADDR = 0xC0100000 endif DIRS = kernel \ kernel/syscalls \ mm \ fs \ drivers/char \ drivers/block \ drivers/pci \ drivers/video \ net \ lib OBJS = kernel/*.o \ kernel/syscalls/*.o \ mm/*.o \ fs/*.o \ fs/devpts/*.o \ fs/ext2/*.o \ fs/iso9660/*.o \ fs/minix/*.o \ fs/pipefs/*.o \ fs/procfs/*.o \ fs/sockfs/*.o \ drivers/char/*.o \ drivers/block/*.o \ drivers/pci/*.o \ drivers/video/*.o \ net/*.o \ lib/*.o export CC LD CFLAGS LDFLAGS INCLUDE all: @echo "#define UTS_VERSION \"`date -u`\"" > include/fiwix/version.h @for n in $(DIRS) ; do (cd $$n ; $(MAKE)) || exit ; done ifeq ($(CCEXE),gcc) $(CPP) $(CONFFLAGS) fiwix.ld > $(TMPFILE) $(LD) -N -T $(TMPFILE) $(LDFLAGS) $(OBJS) $(LIBGCC) -o fiwix rm -f $(TMPFILE) nm fiwix | sort | gzip -9c > System.map.gz endif ifeq ($(CCEXE),tcc) $(LD) -Wl,-Ttext=$(TEXTADDR) $(LDFLAGS) $(OBJS) -o fiwix endif clean: @for n in $(DIRS) ; do (cd $$n ; $(MAKE) clean) ; done rm -f *.o fiwix System.map.gz ================================================ FILE: README.md ================================================ Fiwix ===== Fiwix is an operating system kernel written from scratch, based on the UNIX architecture and fully focused on being POSIX compatible. It is designed and developed mainly as a hobby OS and, since it serves also for educational purposes, the kernel code is kept as simple as possible for the benefit of students and OS enthusiasts. It is small in size (less than 50K lines of code), runs on the i386 hardware platform and is compatible with a good base of existing GNU applications. Features -------- - Written in ANSI C language (Assembly used only in the needed parts). - GRUB Multiboot Specification v1 compliant. - Full 32bit protected mode non-preemptive kernel. - For i386 processors and higher. - Preemptive multitasking. - POSIX-compliant (mostly). - Process groups, sessions and job control. - Interprocess communication with pipes, signals and UNIX-domain sockets. - UNIX System V IPC (semaphores, message queues and shared memory). - BSD file locking mechanism (POSIX restricted to file and advisory only). - Virtual memory splits (user/kernel): 3GB/1GB and 2GB/2GB. - Demand paging with Copy-On-Write feature. - Linux 2.0 i386 ABI system calls compatibility (mostly). - ELF-i386 executable format support (statically and dynamically linked). - Round Robin based scheduler algorithm (no priorities yet). - VFS abstraction layer. - Kexec support with Multiboot Specification v1 and Linux boot protocols. - EXT2 filesystem support with 1KB, 2KB and 4KB block sizes. - Minix v1 and v2 filesystem support. - Linux-like PROC filesystem support (read only). - PIPE pseudo-filesystem support. - ISO9660 filesystem support with Rock Ridge extensions. - RAMdisk device support. - Initial RAMdisk (initrd) image support. - SVGAlib based applications support. - PCI local bus support. - QEMU/Bochs Graphics Adapter support. - UNIX98 pseudoterminals (pty) and devpts filesystem support. - Virtual consoles support (up to 12). - Keyboard driver with Linux keymaps support. - PS/2 mouse support. - Framebuffer device support for VESA VBE 2.0+ compliant graphic cards. - Framebuffer console (fbcon) support. - Serial port (RS-232) driver support. - Remote serial console support. - QEMU Bochs-style debug console support. - Parallel port printer driver support. - Basic implementation of a Pseudo-Random Number Generator. - Floppy disk device driver and DMA management. - IDE/ATA ATAPI CD-ROM device driver. - IDE/ATA hard disk device driver. Compiling --------- The command needed to build the Fiwix kernel is `make clean ; make`. This will create two the files in the root directory of the source code tree: **fiwix** (the kernel itself) and **System.map.gz** (the symbol table). Before compiling you might want to tweak the kernel configuration by changing the default values in `include/fiwix/config.h` and `include/fiwix/limits.h`. 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 would need to provide this program yourself. Fortunately, [FiwixOS](https://www.fiwix.org/downloads.html) provides a full user-space UNIX-like environment to test the Fiwix kernel. Installing ---------- You can proceed to install FiwixOS on a hard disk either by booting from the CD-ROM or from a floppy. If you chosen the latter, you will also need the Installation CD-ROM inserted in order to install the packages that form all the system environment. Let the system boot and when you are ready, just type `install.sh`. The minimal hardware requirements are as follows: - Standard IBM PC-AT architecture. - i386 processor (with floating-point processor). - 4MB of RAM memory (128MB recommended). - IDE/ATAPI CD-ROM or floppy disk (3.5", 1.44MB). - 1GB ATA hard disk. 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! *** ***************************** References ---------- - [Website](https://www.fiwix.org) - [IRC](https://web.libera.chat/) - [Mailing List](https://lists.sourceforge.net/lists/listinfo/fiwix-general) License ------- Fiwix is free software licensed under the terms of the MIT License, see the LICENSE file for more details. Copyright (C) 2018-2025, Jordi Sanfeliu. Credits ------- Fiwix was created by [Jordi Sanfeliu](https://www.fibranet.cat). You can contact me at [jordi@fibranet.cat](mailto:jordi@fibranet.cat). See also the LICENSE file for a list of contributors. ================================================ FILE: THANKS ================================================ 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 https://www.fiwix.org ================================================ FILE: docs/devices.txt ================================================ Fiwix allocated devices ======================= Character Major | Minor | Device name | Description ------+-------+---------------+------------------------------------------------ 1 Memory devices 1 /dev/mem physical memory access 2 /dev/kmem kernel virtual memory access 3 /dev/null null device 4 /dev/port I/O port access 5 /dev/zero null byte source 7 /dev/full returns ENOSPC on write 8 /dev/random random number generator 9 /dev/urandom random number generator (same as above) 4 TTY devices 0 /dev/tty0 current virtual console 1 /dev/tty1 first virtual console ... ... 12 /dev/tty12 12th virtual console 64 /dev/ttyS0 first UART serial port 65 /dev/ttyS1 second UART serial port 66 /dev/ttyS2 third UART serial port 67 /dev/ttyS3 fourth UART serial port 5 Alternate TTY devices 0 /dev/tty current tty device 1 /dev/console system console 2 /dev/ptmx PTY master multiplex 6 Parallel printer devices 0 /dev/lp0 first parallel printer port 10 Non-serial mice 1 /dev/psaux PS/2-style mouse port 29 Universal framebuffer 0 /dev/fb0 first framebuffer 136 UNIX98 PTY slaves 0 /dev/pts/0 first UNIX98 pseudo-TTY 1 /dev/pts/1 second UNIX98 pseudo-TTY ... Block Major | Minor | Device name | Description ------+-------+---------------+------------------------------------------------ 1 RAMdisk drives 0 /dev/ram0 first RAMdisk drive 1 /dev/ram1 second RAMdisk drive ... ... 9 /dev/ram9 10th RAMdisk drive 2 Floppy disks (controller 0) 0 /dev/fd0 floppy disk drive 0 1 /dev/fd1 floppy disk drive 1 4 /dev/fd0h360 floppy disk drive 0 (5.25" 360KB) 5 /dev/fd1h360 floppy disk drive 1 (5.25" 360KB) 8 /dev/fd0h1200 floppy disk drive 0 (5.25" 1.2MB) 9 /dev/fd1h1200 floppy disk drive 1 (5.25" 1.2MB) 12 /dev/fd0h720 floppy disk drive 0 (3.5" 720KB) 13 /dev/fd1h720 floppy disk drive 1 (3.5" 720KB) 16 /dev/fd0h1440 floppy disk drive 0 (3.5" 1.44MB) 17 /dev/fd1h1440 floppy disk drive 1 (3.5" 1.44MB) 3 Primary ATA interface (hard disk or CD-ROM) 0 /dev/hda whole hard disk/CD-ROM master 1 /dev/hda1 first partition 2 /dev/hda2 second partition 3 /dev/hda3 third partition 4 /dev/hda4 fourth partition 64 /dev/hdb whole hard disk/CD-ROM slave 65 /dev/hdb1 first partition 66 /dev/hdb2 second partition 67 /dev/hdb3 third partition 68 /dev/hdb4 fourth partition 22 Secondary ATA interface (hard disk or CD-ROM) 0 /dev/hdc whole hard disk/CD-ROM master 1 /dev/hdc1 first partition 2 /dev/hdc2 second partition 3 /dev/hdc3 third partition 4 /dev/hdc4 fourth partition 64 /dev/hdd whole hard disk/CD-ROM slave 65 /dev/hdd1 first partition 66 /dev/hdd2 second partition 67 /dev/hdd3 third partition 68 /dev/hdd4 fourth partition ================================================ FILE: docs/initrd.txt ================================================ Initrd image size restriction ============================= The Fiwix kernel supports very large initrd images with two different maximum sizes depending on the type of the virtual memory split. If the configuration option CONFIG_VM_SPLIT22 is not defined (the default case), the virtual memory is divided as 3GB for the user and 1GB for the kernel, in this case the maximum size of an initrd image will be near 1GB. When CONFIG_VM_SPLIT22 is defined, which means 2GB for the user and 2GB for the kernel, then the maximum size can be near 2GB. In all, the memory layout of the Fiwix kernel imposes a small and, hopefully, harmless restriction on an specific size of the initrd image. Since the kernel Page Directory and Page Tables are placed right after the end of the initrd image in memory, (see 'docs/kmem_layout.txt'), the PD and PTs will probably conflict with a user program loaded at the address 0x08048000 if the initrd image has a size of around 128MB. In this case, is recommendable define a bigger initrd image to avoid PD and PTs be placed in the same memory address as user programs. ================================================ FILE: docs/kernel-parameters.txt ================================================ Fiwix kernel parameters ======================= The following is a list of the current kernel parameters: bga= Bochs Graphics Adapter resolution (width x height x bpp) Options: 640x480x32, 800x600x32, 1024x768x32 console= Set the output console device. Options: /dev/tty[1..12], /dev/ttyS[0..3] Serial consoles have fixed settings: 9600,N,8,1 ide_nodma Disable DMA in all ATA drives. initrd= Optional ramdisk image file which will be loaded by GRUB. kexec_proto= The boot method of the new kernel. Options: multiboot1, linux kexec_size= Size of the memory space to be reserved to allocate the new kernel (in KiB). kexec_cmdline= Command line to be passed to the new kernel (enclosed in double quotes). ps2_noreset Disable PS/2 controller reset. ramdisksize= Enable the RAM disk drive and configure its size (in KiB). ro Mount root device as read-only on boot. root= Root device name. Options: /dev/fd0, /dev/hda1, ... rootfstype= Set the root filesystem type. Options: ext2, minix, iso9660 Use -- to separate kernel parameters from arguments to init. The kernel parses parameters from the kernel command line up to "--", everything found after "--" is passed as an argument to init. ================================================ FILE: docs/kexec.txt ================================================ Fiwix kexec implementation ========================== Kexec is a mechanism that let's you switch into a different kernel without rebooting your machine. Its implementation in Fiwix differs from the Linux kexec implementation because it doesn't need a system call nor specific user-space tools, and your current system will completely shutdown before jumping to the new kernel. The new kernel can be another Fiwix kernel or any other ELF binary kernel as long as it supports the Multiboot 1 Specification. It can also be a Linux Kernel by using the Linux Boot protocol. How it works ------------ Your system needs to know at boot-time that it might switch to a different kernel. That is, you need to pass some special parameters in your kernel command line during the system boot. The following are the three parameters that you need to specify: - kexec_proto= This is the boot method of the new kernel. Currently the supported values are 'multiboot1' and 'linux'. - kexec_size= Size in KB of the memory space to be reserved to allocate the new kernel. - kexec_cmdline= Command line to be passed to the new kernel (enclosed in double quotes). Example of a kernel command line: /boot/fiwix ro root=/dev/hda2 kexec_proto=multiboot1 kexec_size=500 \ kexec_cmdline="ro root=/dev/hda2" The RAMdisk drives play an important role here. You already know that they can be used to allocate an initrd image specified during the boot, or to allocate one or more all-purpose RAMdisk drives. Now they are also used for kexec to allocate the new kernel. Kexec uses always the first unused RAMdisk drive. You'll know which one is the first unused depending if you specified an initrd image, or if you specified to have all-purpose RAMdisk drives at boot-time. Fiwix is configured by default to have the following possible RAMdisk drives layout: - If an initrd image was specified at boot-time then the first RAMdisk drive is used for it (i.e: /dev/ram0). - If 'ramdisksize=' was specified at boot-time then the first unused RAMDISK_DRIVES RAMdisk drives will be used. - If 'kexec_size=' was specified at boot-time then the first unused RAMdisk drive will be used. Let's see some examples to understand better these rules: Example 1: - Kernel cmdline: "... ramdisksize=49152 initrd=/initrd.img" - Resulting RAMdisk drives layout: - /dev/ram0 -> initrd RAMdisk drive of 48MB (/ filesystem). - if RAMDISK_DRIVES == 1 - /dev/ram1 -> all-purpose RAMdisk drive of 48MB. - if RAMDISK_DRIVES == 2 - /dev/ram1 -> all-purpose RAMdisk drive of 48MB. - /dev/ram2 -> all-purpose RAMdisk drive of 48MB. ... Example 2: - Kernel cmdline: "... ramdisksize=16384" - Resulting RAMdisk drives layout: - if RAMDISK_DRIVES == 1 - /dev/ram0 -> all-purpose RAMdisk drive of 16MB. - if RAMDISK_DRIVES == 2 - /dev/ram0 -> all-purpose RAMdisk drive of 16MB. - /dev/ram1 -> all-purpose RAMdisk drive of 16MB. ... Example 3: - Kernel cmdline: "... ramdisksize=16384 kexec_size=500 ..." - Resulting RAMdisk drives layout: - if RAMDISK_DRIVES == 1 - /dev/ram0 -> all-purpose RAMdisk drive of 16MB. - /dev/ram1 -> kexec RAMdisk drive of 500KB. - if RAMDISK_DRIVES == 2 - /dev/ram0 -> all-purpose RAMdisk drive of 16MB. - /dev/ram1 -> all-purpose RAMdisk drive of 16MB. - /dev/ram2 -> kexec RAMdisk drive of 500KB. ... Multiboot --------- Once you know what RAMdisk drive will be used to allocate the new kernel, you can proceed to copy the ELF binary kernel into such RAMdisk drive by using a common user-space tool like 'cp' or 'dd'. Finally, when you are ready, just do a normal shutdown to switch automatically to the new kernel. Example: # cp fiwix /dev/ram0 # shutdown -h 0 Linux ----- In the case of Linux the boot protocol is different. The Linux kernel expects to find two 32bit numbers (in little-endian format) at the beginning of the RAMdisk drive. The first number is the length of the Linux kernel, and the second one is the length of the initramfs. If the kernel doesn't need an initramfs, the length must have a value of zero. Right after both numbers you must copy the kernel image, and then the initramfs image (if needed). The RAMdisk drive should look like the following before to kexec: +-----------+-----------+-------------------- ~~~ -+-------------------- ~~~ -+ | kernel | initramfs | the kernel image ~~~ -| the initramfs ~~~ -| | length | length | 'vmlinuz' ~~~ -| image (optional) ~~~ -| +-----------+-----------+-------------------- ~~~ -+-------------------- ~~~ -+ 0 4 8 n = (8 + kernel length) offset (in bytes) The following script might help you to format correctly the RAMdisk drive: ---------------------------------- 8< ---------------------------------------- KERNEL=linux-4.9.10 RAMFS=initramfs.cpio size=$(printf "%08x" `stat -c %s $KERNEL` | tac -rs .. | echo "$(tr -d '\n')") echo -n $size | xxd -r -p - /dev/ram0 if [ -n "$RAMFS" ]; then size=$(printf "%08x" `stat -c %s $RAMFS` | tac -rs .. | echo "$(tr -d '\n')") else size=$(printf "%08x" `echo 0` | tac -rs .. | echo "$(tr -d '\n')") fi echo -n $size | xxd -r -p - | dd of=/dev/ram0 bs=1 seek=4 cat $KERNEL $RAMFS | dd of=/dev/ram0 bs=1 seek=8 ---------------------------------- 8< ---------------------------------------- Finally, when you are ready, just do a normal shutdown to switch automatically to the new kernel. Example: # shutdown -h 0 ================================================ FILE: docs/kmem_layout.txt ================================================ Fiwix kernel memory address space on the i386 architecture ========================================================== +-------------------+ \ | page_table | | +-------------------+ | +-------------------+ | | page_hash_table | | +-------------------+ | different +-------------------+ | | RAMdisk drives: | | preallocated | - initrd | | | - all-purpose | | areas | - kexec | | +-------------------+ | of +-------------------+ | | fd_table | | contiguous +-------------------+ | +-------------------+ | memory | inode_hash_table | | +-------------------+ | spaces +-------------------+ | | buffer_hash_table | | +-------------------+ | +-------------------+ | | proc_table | | +-------------------+ / +-------------------+ | kpage_table | kernel Page Tables +-------------------+ +-------------------+ | kpage_dir | kernel Page Directory +-------------------+ +-------------------+ | initrd (optional) | allocated by GRUB or Multiboot +-------------------+ +-------------------+ | .bss section | +-------------------+ | .data section | +-------------------+ | .text section | kernel binary (fiwix) +-------------------+ 0x00100000 ... +-------------------+ | 4KB kpage_dir | this page will be reused once the definitive | only used at boot | kpage_dir is installed +-------------------+ 0x00050000 ... 0x00010000 +-------------------+ | kernel stack (4k) | later used as stack for idle process +-------------------+ ... 0x00000000 ================================================ FILE: docs/system-console.txt ================================================ Fiwix system console ==================== It is possible to specify multiple devices for console output. You can define up to NR_SYSCONSOLES 'console=' kernel command line options to select which device(s) to use for console output. Refer to 'include/fiwix/kparms.h' to know which devices are supported as system consoles. The device '/dev/tty0' is the default when no device is specified. It is directly linked to the current virtual console in use, so by default, all kernel messages will go to the current virtual console. Since the VGA graphics is always present in the PC i386 architecture, the early log messages will appear only in the first virtual console defined, regardless if a serial console was also defined. ================================================ FILE: docs/tcc.txt ================================================ Fiwix can be built with a modified version of tcc instead of gcc. A version of tcc which has been verified to compile Fiwix can be retrieved (at the time of this writing) from the following URL: https://download.savannah.gnu.org/releases/tinycc/tcc-0.9.27.tar.bz2 Fiwix uses a linker script to specify high virtual addresses for data structures but uses low physical addresses for the addresses to load the kernel sections into. tcc does not support linker scripts and so a small modification must be applied to tcc in order to calculate the appropriate physical addresess for sections in the ELF header. The layout_sections function in the file tccelf.c must be modified (around line 1674). This line: ph->p_paddr = ph->p_vaddr; Must be changed to this: ph->p_paddr = ph->p_vaddr; if (s1->text_addr == 0xC0100000) ph->p_paddr = ph->p_paddr - 0xC0000000; if (s1->text_addr == 0x80100000) ph->p_paddr = ph->p_paddr - 0x80000000; This change is specific to Fiwix and so it is unlikely to be incorporated into the standard tcc compiler in the future. Note that tcc does not support certain constructs used by CONFIG_OFFSET64 or CONFIG_PRINTK64, so you must undefine these options. These commands will build Fiwix with tcc: echo "#undef CONFIG_OFFSET64" > include/fiwix/custom_config.h echo "#undef CONFIG_PRINTK64" >> include/fiwix/custom_config.h make clean make CCEXE="tcc" CONFFLAGS="-DCUSTOM_CONFIG_H" ================================================ FILE: docs/video/font-lat9-8x14.txt ================================================ /* 0x00 */ 0x00, /* -------- */ 0x7e, /* -######- */ 0xc3, /* ##----## */ 0x99, /* #--##--# */ 0x99, /* #--##--# */ 0xf3, /* ####--## */ 0xe7, /* ###--### */ 0xe7, /* ###--### */ 0xff, /* ######## */ 0xe7, /* ###--### */ 0xe7, /* ###--### */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x01 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x02 */ 0x00, /* -------- */ 0x6e, /* -##-###- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xde, /* ##-####- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0x6e, /* -##-###- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x03 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6e, /* -##-###- */ 0xdb, /* ##-##-## */ 0xdb, /* ##-##-## */ 0xdf, /* ##-##### */ 0xd8, /* ##-##--- */ 0xdb, /* ##-##-## */ 0x6e, /* -##-###- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x04 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x10, /* ---#---- */ 0x38, /* --###--- */ 0x7c, /* -#####-- */ 0xfe, /* #######- */ 0x7c, /* -#####-- */ 0x38, /* --###--- */ 0x10, /* ---#---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x05 */ 0x00, /* -------- */ 0x88, /* #---#--- */ 0x88, /* #---#--- */ 0xf8, /* #####--- */ 0x88, /* #---#--- */ 0x88, /* #---#--- */ 0x00, /* -------- */ 0x3e, /* --#####- */ 0x08, /* ----#--- */ 0x08, /* ----#--- */ 0x08, /* ----#--- */ 0x08, /* ----#--- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x06 */ 0x00, /* -------- */ 0xf8, /* #####--- */ 0x80, /* #------- */ 0xe0, /* ###----- */ 0x80, /* #------- */ 0x80, /* #------- */ 0x00, /* -------- */ 0x3e, /* --#####- */ 0x20, /* --#----- */ 0x38, /* --###--- */ 0x20, /* --#----- */ 0x20, /* --#----- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x07 */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x80, /* #------- */ 0x80, /* #------- */ 0x80, /* #------- */ 0x78, /* -####--- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x22, /* --#---#- */ 0x3e, /* --#####- */ 0x24, /* --#--#-- */ 0x22, /* --#---#- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x08 */ 0x00, /* -------- */ 0x80, /* #------- */ 0x80, /* #------- */ 0x80, /* #------- */ 0x80, /* #------- */ 0xf8, /* #####--- */ 0x00, /* -------- */ 0x3e, /* --#####- */ 0x20, /* --#----- */ 0x38, /* --###--- */ 0x20, /* --#----- */ 0x20, /* --#----- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x09 */ 0x88, /* #---#--- */ 0x22, /* --#---#- */ 0x88, /* #---#--- */ 0x22, /* --#---#- */ 0x88, /* #---#--- */ 0x22, /* --#---#- */ 0x88, /* #---#--- */ 0x22, /* --#---#- */ 0x88, /* #---#--- */ 0x22, /* --#---#- */ 0x88, /* #---#--- */ 0x22, /* --#---#- */ 0x88, /* #---#--- */ 0x22, /* --#---#- */ /* 0x0a */ 0xaa, /* #-#-#-#- */ 0x55, /* -#-#-#-# */ 0xaa, /* #-#-#-#- */ 0x55, /* -#-#-#-# */ 0xaa, /* #-#-#-#- */ 0x55, /* -#-#-#-# */ 0xaa, /* #-#-#-#- */ 0x55, /* -#-#-#-# */ 0xaa, /* #-#-#-#- */ 0x55, /* -#-#-#-# */ 0xaa, /* #-#-#-#- */ 0x55, /* -#-#-#-# */ 0xaa, /* #-#-#-#- */ 0x55, /* -#-#-#-# */ /* 0x0b */ 0xbb, /* #-###-## */ 0xee, /* ###-###- */ 0xbb, /* #-###-## */ 0xee, /* ###-###- */ 0xbb, /* #-###-## */ 0xee, /* ###-###- */ 0xbb, /* #-###-## */ 0xee, /* ###-###- */ 0xbb, /* #-###-## */ 0xee, /* ###-###- */ 0xbb, /* #-###-## */ 0xee, /* ###-###- */ 0xbb, /* #-###-## */ 0xee, /* ###-###- */ /* 0x0c */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ /* 0x0d */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ /* 0x0e */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x0f */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ /* 0x10 */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ /* 0x11 */ 0x00, /* -------- */ 0x88, /* #---#--- */ 0xc8, /* ##--#--- */ 0xa8, /* #-#-#--- */ 0x98, /* #--##--- */ 0x88, /* #---#--- */ 0x00, /* -------- */ 0x20, /* --#----- */ 0x20, /* --#----- */ 0x20, /* --#----- */ 0x20, /* --#----- */ 0x3e, /* --#####- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x12 */ 0x00, /* -------- */ 0x88, /* #---#--- */ 0x88, /* #---#--- */ 0x50, /* -#-#---- */ 0x50, /* -#-#---- */ 0x20, /* --#----- */ 0x00, /* -------- */ 0x3e, /* --#####- */ 0x08, /* ----#--- */ 0x08, /* ----#--- */ 0x08, /* ----#--- */ 0x08, /* ----#--- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x13 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x06, /* -----##- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x14 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x15 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x06, /* -----##- */ 0x0c, /* ----##-- */ 0xfe, /* #######- */ 0x38, /* --###--- */ 0xfe, /* #######- */ 0x60, /* -##----- */ 0xc0, /* ##------ */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x16 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x02, /* ------#- */ 0x0e, /* ----###- */ 0x3e, /* --#####- */ 0x7e, /* -######- */ 0xfe, /* #######- */ 0x7e, /* -######- */ 0x3e, /* --#####- */ 0x0e, /* ----###- */ 0x02, /* ------#- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x17 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x80, /* #------- */ 0xe0, /* ###----- */ 0xf0, /* ####---- */ 0xfc, /* ######-- */ 0xfe, /* #######- */ 0xfc, /* ######-- */ 0xf0, /* ####---- */ 0xe0, /* ###----- */ 0x80, /* #------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x18 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x7e, /* -######- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x19 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x7e, /* -######- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x1a */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0xfe, /* #######- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x1b */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0xfe, /* #######- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x1c */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x7e, /* -######- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x7e, /* -######- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x1d */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x28, /* --#-#--- */ 0x6c, /* -##-##-- */ 0xfe, /* #######- */ 0x6c, /* -##-##-- */ 0x28, /* --#-#--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x1e */ 0x00, /* -------- */ 0x00, /* -------- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x36, /* --##-##- */ 0x66, /* -##--##- */ 0xfe, /* #######- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x1f */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc0, /* ##------ */ 0x7c, /* -#####-- */ 0x6e, /* -##-###- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x20 (' ') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x21 ('!') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x3c, /* --####-- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x22 ('"') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x36, /* --##-##- */ 0x36, /* --##-##- */ 0x14, /* ---#-#-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x23 ('#') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0xfe, /* #######- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0xfe, /* #######- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x24 ('$') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x10, /* ---#---- */ 0x7c, /* -#####-- */ 0xd6, /* ##-#-##- */ 0x70, /* -###---- */ 0x38, /* --###--- */ 0x1c, /* ---###-- */ 0xd6, /* ##-#-##- */ 0x7c, /* -#####-- */ 0x10, /* ---#---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x25 ('%') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x62, /* -##---#- */ 0x66, /* -##--##- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x66, /* -##--##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x26 ('&') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x38, /* --###--- */ 0x76, /* -###-##- */ 0xf6, /* ####-##- */ 0xce, /* ##--###- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x27 (''') */ 0x00, /* -------- */ 0x1c, /* ---###-- */ 0x1c, /* ---###-- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x28 ('(') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x29 (')') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x2a ('*') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0xfe, /* #######- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x2b ('+') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x7e, /* -######- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x2c (',') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x2d ('-') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x2e ('.') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x2f ('/') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x06, /* -----##- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0xc0, /* ##------ */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x30 ('0') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xd6, /* ##-#-##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x31 ('1') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x78, /* -####--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x32 ('2') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x33 ('3') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x3c, /* --####-- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x34 ('4') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x1c, /* ---###-- */ 0x3c, /* --####-- */ 0x6c, /* -##-##-- */ 0xcc, /* ##--##-- */ 0xfe, /* #######- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x35 ('5') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xfc, /* ######-- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x36 ('6') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xfc, /* ######-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x37 ('7') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x38 ('8') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x39 ('9') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7e, /* -######- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x3a (':') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x3b (';') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x3c ('<') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0xc0, /* ##------ */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x3d ('=') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x3e ('>') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0x06, /* -----##- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x3f ('?') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x40 ('@') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xde, /* ##-####- */ 0xde, /* ##-####- */ 0xde, /* ##-####- */ 0xdc, /* ##-###-- */ 0xc0, /* ##------ */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x41 ('A') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x42 ('B') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x7c, /* -#####-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0xfc, /* ######-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x43 ('C') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x66, /* -##--##- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x44 ('D') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xf8, /* #####--- */ 0x6c, /* -##-##-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x6c, /* -##-##-- */ 0xf8, /* #####--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x45 ('E') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x66, /* -##--##- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x66, /* -##--##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x46 ('F') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x66, /* -##--##- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x47 ('G') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xce, /* ##--###- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x48 ('H') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x49 ('I') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x4a ('J') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0x70, /* -###---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x4b ('K') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xcc, /* ##--##-- */ 0xd8, /* ##-##--- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xd8, /* ##-##--- */ 0xcc, /* ##--##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x4c ('L') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xf0, /* ####---- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x62, /* -##---#- */ 0x66, /* -##--##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x4d ('M') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xee, /* ###-###- */ 0xfe, /* #######- */ 0xd6, /* ##-#-##- */ 0xd6, /* ##-#-##- */ 0xd6, /* ##-#-##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x4e ('N') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xe6, /* ###--##- */ 0xe6, /* ###--##- */ 0xf6, /* ####-##- */ 0xde, /* ##-####- */ 0xce, /* ##--###- */ 0xce, /* ##--###- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x4f ('O') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x50 ('P') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x51 ('Q') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xd6, /* ##-#-##- */ 0x7c, /* -#####-- */ 0x06, /* -----##- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x52 ('R') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x7c, /* -#####-- */ 0x78, /* -####--- */ 0x6c, /* -##-##-- */ 0x66, /* -##--##- */ 0xe6, /* ###--##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x53 ('S') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc0, /* ##------ */ 0x60, /* -##----- */ 0x38, /* --###--- */ 0x0c, /* ----##-- */ 0x06, /* -----##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x54 ('T') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0x5a, /* -#-##-#- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x55 ('U') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x56 ('V') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x10, /* ---#---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x57 ('W') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xd6, /* ##-#-##- */ 0xd6, /* ##-#-##- */ 0xd6, /* ##-#-##- */ 0xfe, /* #######- */ 0xee, /* ###-###- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x58 ('X') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x38, /* --###--- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x59 ('Y') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x5a ('Z') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0x8c, /* #---##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0xc2, /* ##----#- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x5b ('[') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x5c ('\') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc0, /* ##------ */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0x06, /* -----##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x5d (']') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x5e ('^') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x66, /* -##--##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x5f ('_') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ /* 0x60 ('`') */ 0x00, /* -------- */ 0x1c, /* ---###-- */ 0x1c, /* ---###-- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x61 ('a') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0xdc, /* ##-###-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x62 ('b') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xe0, /* ###----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x7c, /* -#####-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0xfc, /* ######-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x63 ('c') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x64 ('d') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x1c, /* ---###-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x65 ('e') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc0, /* ##------ */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x66 ('f') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x1c, /* ---###-- */ 0x36, /* --##-##- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0xfc, /* ######-- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x67 ('g') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xce, /* ##--###- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7e, /* -######- */ 0x06, /* -----##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ /* 0x68 ('h') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xe0, /* ###----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x6c, /* -##-##-- */ 0x76, /* -###-##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0xe6, /* ###--##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x69 ('i') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x6a ('j') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x00, /* -------- */ 0x1c, /* ---###-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x6b ('k') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xe0, /* ###----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x66, /* -##--##- */ 0x6c, /* -##-##-- */ 0x78, /* -####--- */ 0x6c, /* -##-##-- */ 0x66, /* -##--##- */ 0xe6, /* ###--##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x6c ('l') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x6d ('m') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0xfe, /* #######- */ 0xd6, /* ##-#-##- */ 0xd6, /* ##-#-##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x6e ('n') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xdc, /* ##-###-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x6f ('o') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x70 ('p') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xdc, /* ##-###-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x00, /* -------- */ /* 0x71 ('q') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x7c, /* -#####-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x1e, /* ---####- */ 0x00, /* -------- */ /* 0x72 ('r') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xdc, /* ##-###-- */ 0x66, /* -##--##- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x73 ('s') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0x70, /* -###---- */ 0x1c, /* ---###-- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x74 ('t') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0xfc, /* ######-- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x36, /* --##-##- */ 0x1c, /* ---###-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x75 ('u') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x76 ('v') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x10, /* ---#---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x77 ('w') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xd6, /* ##-#-##- */ 0xd6, /* ##-#-##- */ 0xfe, /* #######- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x78 ('x') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x79 ('y') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xce, /* ##--###- */ 0x76, /* -###-##- */ 0x06, /* -----##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ /* 0x7a ('z') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x8c, /* #---##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x62, /* -##---#- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x7b ('{') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x0e, /* ----###- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x70, /* -###---- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x0e, /* ----###- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x7c ('|') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x7d ('}') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x70, /* -###---- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x0e, /* ----###- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x70, /* -###---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x7e ('~') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x7f */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x80 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x81 */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x82 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x1f, /* ---##### */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x83 */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x1f, /* ---##### */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x84 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x85 */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x86 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x1f, /* ---##### */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x87 */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x1f, /* ---##### */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x88 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xf8, /* #####--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x89 */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0xf8, /* #####--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x8a */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x8b */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x8c */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xf8, /* #####--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x8d */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0xf8, /* #####--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x8e */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x8f */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0xff, /* ######## */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x90 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x91 */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x92 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x3f, /* --###### */ 0x30, /* --##---- */ 0x3f, /* --###### */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x93 */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6f, /* -##-#### */ 0x60, /* -##----- */ 0x7f, /* -####### */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x94 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x95 */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x96 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7f, /* -####### */ 0x60, /* -##----- */ 0x6f, /* -##-#### */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x97 */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6f, /* -##-#### */ 0x60, /* -##----- */ 0x6f, /* -##-#### */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x98 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x0c, /* ----##-- */ 0xfc, /* ######-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x99 */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0xec, /* ###-##-- */ 0x0c, /* ----##-- */ 0xfc, /* ######-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x9a */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x9b */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0xef, /* ###-#### */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x9c */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x0c, /* ----##-- */ 0xec, /* ###-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x9d */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0xec, /* ###-##-- */ 0x0c, /* ----##-- */ 0xec, /* ###-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x9e */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0xef, /* ###-#### */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x9f */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0xef, /* ###-#### */ 0x00, /* -------- */ 0xef, /* ###-#### */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0xa0 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x82, /* #-----#- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xa1 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x3c, /* --####-- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ /* 0xa2 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x10, /* ---#---- */ 0x7c, /* -#####-- */ 0xd6, /* ##-#-##- */ 0xd0, /* ##-#---- */ 0xd0, /* ##-#---- */ 0xd6, /* ##-#-##- */ 0x7c, /* -#####-- */ 0x10, /* ---#---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xa3 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x60, /* -##----- */ 0x66, /* -##--##- */ 0xf6, /* ####-##- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xa4 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x62, /* -##---#- */ 0x60, /* -##----- */ 0xf8, /* #####--- */ 0x60, /* -##----- */ 0xf8, /* #####--- */ 0x60, /* -##----- */ 0x62, /* -##---#- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xa5 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x7e, /* -######- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xa6 */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x60, /* -##----- */ 0x1c, /* ---###-- */ 0x06, /* -----##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xa7 */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x60, /* -##----- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x0c, /* ----##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ /* 0xa8 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xa9 */ 0x00, /* -------- */ 0x7e, /* -######- */ 0x81, /* #------# */ 0x99, /* #--##--# */ 0xa5, /* #-#--#-# */ 0xa1, /* #-#----# */ 0xa5, /* #-#--#-# */ 0x99, /* #--##--# */ 0x81, /* #------# */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xaa */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x3e, /* --#####- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xab */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x36, /* --##-##- */ 0x6c, /* -##-##-- */ 0xd8, /* ##-##--- */ 0x6c, /* -##-##-- */ 0x36, /* --##-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xac */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xad */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xae */ 0x00, /* -------- */ 0x7e, /* -######- */ 0x81, /* #------# */ 0xb9, /* #-###--# */ 0xa5, /* #-#--#-# */ 0xb9, /* #-###--# */ 0xa5, /* #-#--#-# */ 0xa5, /* #-#--#-# */ 0x81, /* #------# */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xaf */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb0 */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb1 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x7e, /* -######- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb2 */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb3 */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x18, /* ---##--- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb4 */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0x8c, /* #---##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x62, /* -##---#- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb5 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xf6, /* ####-##- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ /* 0xb6 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7f, /* -####### */ 0xdb, /* ##-##-## */ 0xdb, /* ##-##-## */ 0xdb, /* ##-##-## */ 0x7b, /* -####-## */ 0x1b, /* ---##-## */ 0x1b, /* ---##-## */ 0x1b, /* ---##-## */ 0x1b, /* ---##-## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb7 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb8 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x8c, /* #---##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x62, /* -##---#- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb9 */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x70, /* -###---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xba */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xbb */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xd8, /* ##-##--- */ 0x6c, /* -##-##-- */ 0x36, /* --##-##- */ 0x6c, /* -##-##-- */ 0xd8, /* ##-##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xbc */ 0x00, /* -------- */ 0x00, /* -------- */ 0x77, /* -###-### */ 0xdf, /* ##-##### */ 0xd8, /* ##-##--- */ 0xde, /* ##-####- */ 0xde, /* ##-####- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xdf, /* ##-##### */ 0x77, /* -###-### */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xbd */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6e, /* -##-###- */ 0xdb, /* ##-##-## */ 0xdf, /* ##-##### */ 0xd8, /* ##-##--- */ 0xdb, /* ##-##-## */ 0x6e, /* -##-###- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xbe */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xbf */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ /* 0xc0 */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xc1 */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xc2 */ 0x10, /* ---#---- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xc3 */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xc4 */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xc5 */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xc6 */ 0x00, /* -------- */ 0x7e, /* -######- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xfe, /* #######- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xde, /* ##-####- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xc7 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x66, /* -##--##- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ /* 0xc8 */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x66, /* -##--##- */ 0x60, /* -##----- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0x66, /* -##--##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xc9 */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x66, /* -##--##- */ 0x60, /* -##----- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0x66, /* -##--##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xca */ 0x10, /* ---#---- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x66, /* -##--##- */ 0x60, /* -##----- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0x66, /* -##--##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xcb */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x66, /* -##--##- */ 0x60, /* -##----- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0x66, /* -##--##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xcc */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xcd */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xce */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x66, /* -##--##- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xcf */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd0 */ 0x00, /* -------- */ 0x00, /* -------- */ 0xf8, /* #####--- */ 0x6c, /* -##-##-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0xf6, /* ####-##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x6c, /* -##-##-- */ 0xf8, /* #####--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd1 */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xe6, /* ###--##- */ 0xf6, /* ####-##- */ 0xde, /* ##-####- */ 0xce, /* ##--###- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd2 */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd3 */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd4 */ 0x10, /* ---#---- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd5 */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd6 */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd7 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd8 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0xc6, /* ##---##- */ 0xce, /* ##--###- */ 0xde, /* ##-####- */ 0xd6, /* ##-#-##- */ 0xf6, /* ####-##- */ 0xe6, /* ###--##- */ 0xc6, /* ##---##- */ 0xfc, /* ######-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd9 */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xda */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xdb */ 0x10, /* ---#---- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xdc */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xdd */ 0x06, /* -----##- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xde */ 0x00, /* -------- */ 0x00, /* -------- */ 0xf0, /* ####---- */ 0x60, /* -##----- */ 0x7c, /* -#####-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xdf */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xcc, /* ##--##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xd6, /* ##-#-##- */ 0xdc, /* ##-###-- */ 0x80, /* #------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xe0 */ 0x00, /* -------- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0xdc, /* ##-###-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xe1 */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0xdc, /* ##-###-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xe2 */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0xdc, /* ##-###-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xe3 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0xdc, /* ##-###-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xe4 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0xdc, /* ##-###-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xe5 */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0xdc, /* ##-###-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xe6 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0xdb, /* ##-##-## */ 0x1b, /* ---##-## */ 0x7f, /* -####### */ 0xd8, /* ##-##--- */ 0xdb, /* ##-##-## */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xe7 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x18, /* ---##--- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ /* 0xe8 */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc0, /* ##------ */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xe9 */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc0, /* ##------ */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xea */ 0x00, /* -------- */ 0x10, /* ---#---- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc0, /* ##------ */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xeb */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc0, /* ##------ */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xec */ 0x00, /* -------- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xed */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xee */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x66, /* -##--##- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xef */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf0 */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7e, /* -######- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf1 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0xdc, /* ##-###-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf2 */ 0x00, /* -------- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf3 */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf4 */ 0x00, /* -------- */ 0x10, /* ---#---- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf5 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf6 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf7 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf8 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0xce, /* ##--###- */ 0xde, /* ##-####- */ 0xf6, /* ####-##- */ 0xe6, /* ###--##- */ 0xfc, /* ######-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf9 */ 0x00, /* -------- */ 0xc0, /* ##------ */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xfa */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xfb */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xfc */ 0x00, /* -------- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xfd */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xce, /* ##--###- */ 0x76, /* -###-##- */ 0x06, /* -----##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ /* 0xfe */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xf0, /* ####---- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x78, /* -####--- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x78, /* -####--- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ /* 0xff */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xce, /* ##--###- */ 0x76, /* -###-##- */ 0x06, /* -----##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ /* cursor shape (8x14) */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xFF, /* ######## */ 0xFF, /* ######## */ ================================================ FILE: docs/video/font-lat9-8x16.txt ================================================ /* 0x00 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0xc3, /* ##----## */ 0x99, /* #--##--# */ 0x99, /* #--##--# */ 0xf3, /* ####--## */ 0xe7, /* ###--### */ 0xe7, /* ###--### */ 0xff, /* ######## */ 0xe7, /* ###--### */ 0xe7, /* ###--### */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x01 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x02 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6e, /* -##-###- */ 0xf8, /* #####--- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xdc, /* ##-###-- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xf8, /* #####--- */ 0x6e, /* -##-###- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x03 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6e, /* -##-###- */ 0xdb, /* ##-##-## */ 0xdb, /* ##-##-## */ 0xdf, /* ##-##### */ 0xd8, /* ##-##--- */ 0xdb, /* ##-##-## */ 0x6e, /* -##-###- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x04 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x10, /* ---#---- */ 0x38, /* --###--- */ 0x7c, /* -#####-- */ 0xfe, /* #######- */ 0x7c, /* -#####-- */ 0x38, /* --###--- */ 0x10, /* ---#---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x05 */ 0x00, /* -------- */ 0x88, /* #---#--- */ 0x88, /* #---#--- */ 0xf8, /* #####--- */ 0x88, /* #---#--- */ 0x88, /* #---#--- */ 0x00, /* -------- */ 0x3e, /* --#####- */ 0x08, /* ----#--- */ 0x08, /* ----#--- */ 0x08, /* ----#--- */ 0x08, /* ----#--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x06 */ 0x00, /* -------- */ 0xf8, /* #####--- */ 0x80, /* #------- */ 0xe0, /* ###----- */ 0x80, /* #------- */ 0x80, /* #------- */ 0x00, /* -------- */ 0x3e, /* --#####- */ 0x20, /* --#----- */ 0x38, /* --###--- */ 0x20, /* --#----- */ 0x20, /* --#----- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x07 */ 0x00, /* -------- */ 0x70, /* -###---- */ 0x88, /* #---#--- */ 0x80, /* #------- */ 0x88, /* #---#--- */ 0x70, /* -###---- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x22, /* --#---#- */ 0x3c, /* --####-- */ 0x24, /* --#--#-- */ 0x22, /* --#---#- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x08 */ 0x00, /* -------- */ 0x80, /* #------- */ 0x80, /* #------- */ 0x80, /* #------- */ 0x80, /* #------- */ 0xf8, /* #####--- */ 0x00, /* -------- */ 0x3e, /* --#####- */ 0x20, /* --#----- */ 0x38, /* --###--- */ 0x20, /* --#----- */ 0x20, /* --#----- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x09 */ 0x11, /* ---#---# */ 0x44, /* -#---#-- */ 0x11, /* ---#---# */ 0x44, /* -#---#-- */ 0x11, /* ---#---# */ 0x44, /* -#---#-- */ 0x11, /* ---#---# */ 0x44, /* -#---#-- */ 0x11, /* ---#---# */ 0x44, /* -#---#-- */ 0x11, /* ---#---# */ 0x44, /* -#---#-- */ 0x11, /* ---#---# */ 0x44, /* -#---#-- */ 0x11, /* ---#---# */ 0x44, /* -#---#-- */ /* 0x0a */ 0x55, /* -#-#-#-# */ 0xaa, /* #-#-#-#- */ 0x55, /* -#-#-#-# */ 0xaa, /* #-#-#-#- */ 0x55, /* -#-#-#-# */ 0xaa, /* #-#-#-#- */ 0x55, /* -#-#-#-# */ 0xaa, /* #-#-#-#- */ 0x55, /* -#-#-#-# */ 0xaa, /* #-#-#-#- */ 0x55, /* -#-#-#-# */ 0xaa, /* #-#-#-#- */ 0x55, /* -#-#-#-# */ 0xaa, /* #-#-#-#- */ 0x55, /* -#-#-#-# */ 0xaa, /* #-#-#-#- */ /* 0x0b */ 0xdd, /* ##-###-# */ 0x77, /* -###-### */ 0xdd, /* ##-###-# */ 0x77, /* -###-### */ 0xdd, /* ##-###-# */ 0x77, /* -###-### */ 0xdd, /* ##-###-# */ 0x77, /* -###-### */ 0xdd, /* ##-###-# */ 0x77, /* -###-### */ 0xdd, /* ##-###-# */ 0x77, /* -###-### */ 0xdd, /* ##-###-# */ 0x77, /* -###-### */ 0xdd, /* ##-###-# */ 0x77, /* -###-### */ /* 0x0c */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ /* 0x0d */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ /* 0x0e */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x0f */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ /* 0x10 */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ /* 0x11 */ 0x00, /* -------- */ 0x88, /* #---#--- */ 0xc8, /* ##--#--- */ 0xa8, /* #-#-#--- */ 0x98, /* #--##--- */ 0x88, /* #---#--- */ 0x00, /* -------- */ 0x20, /* --#----- */ 0x20, /* --#----- */ 0x20, /* --#----- */ 0x20, /* --#----- */ 0x3e, /* --#####- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x12 */ 0x00, /* -------- */ 0x88, /* #---#--- */ 0x88, /* #---#--- */ 0x50, /* -#-#---- */ 0x50, /* -#-#---- */ 0x20, /* --#----- */ 0x00, /* -------- */ 0x3e, /* --#####- */ 0x08, /* ----#--- */ 0x08, /* ----#--- */ 0x08, /* ----#--- */ 0x08, /* ----#--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x13 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x0e, /* ----###- */ 0x38, /* --###--- */ 0xe0, /* ###----- */ 0x38, /* --###--- */ 0x0e, /* ----###- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x14 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xe0, /* ###----- */ 0x38, /* --###--- */ 0x0e, /* ----###- */ 0x38, /* --###--- */ 0xe0, /* ###----- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x15 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x06, /* -----##- */ 0x0c, /* ----##-- */ 0xfe, /* #######- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0xfe, /* #######- */ 0x60, /* -##----- */ 0xc0, /* ##------ */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x16 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x06, /* -----##- */ 0x1e, /* ---####- */ 0x7e, /* -######- */ 0xfe, /* #######- */ 0x7e, /* -######- */ 0x1e, /* ---####- */ 0x06, /* -----##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x17 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc0, /* ##------ */ 0xf0, /* ####---- */ 0xfc, /* ######-- */ 0xfe, /* #######- */ 0xfc, /* ######-- */ 0xf0, /* ####---- */ 0xc0, /* ##------ */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x18 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x7e, /* -######- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x19 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x7e, /* -######- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x1a */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0xfe, /* #######- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x1b */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0xfe, /* #######- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x1c */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x7e, /* -######- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x7e, /* -######- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x1d */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x28, /* --#-#--- */ 0x6c, /* -##-##-- */ 0xfe, /* #######- */ 0x6c, /* -##-##-- */ 0x28, /* --#-#--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x1e */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x06, /* -----##- */ 0x36, /* --##-##- */ 0x66, /* -##--##- */ 0xfe, /* #######- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x1f */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x20 (' ') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x21 ('!') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x3c, /* --####-- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x22 ('"') */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x24, /* --#--#-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x23 ('#') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0xfe, /* #######- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0xfe, /* #######- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x24 ('$') */ 0x00, /* -------- */ 0x10, /* ---#---- */ 0x10, /* ---#---- */ 0x7c, /* -#####-- */ 0xd6, /* ##-#-##- */ 0xd0, /* ##-#---- */ 0xd0, /* ##-#---- */ 0x7c, /* -#####-- */ 0x16, /* ---#-##- */ 0x16, /* ---#-##- */ 0xd6, /* ##-#-##- */ 0x7c, /* -#####-- */ 0x10, /* ---#---- */ 0x10, /* ---#---- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x25 ('%') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc2, /* ##----#- */ 0xc6, /* ##---##- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0xc6, /* ##---##- */ 0x86, /* #----##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x26 ('&') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x27 (''') */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x28 ('(') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x29 (')') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x2a ('*') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0xff, /* ######## */ 0x3c, /* --####-- */ 0x66, /* -##--##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x2b ('+') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x7e, /* -######- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x2c (',') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x2d ('-') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x2e ('.') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x2f ('/') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x06, /* -----##- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0xc0, /* ##------ */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x30 ('0') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xd6, /* ##-#-##- */ 0xd6, /* ##-#-##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x31 ('1') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x38, /* --###--- */ 0x78, /* -####--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x32 ('2') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0x06, /* -----##- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0xc0, /* ##------ */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x33 ('3') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x3c, /* --####-- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x34 ('4') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x1c, /* ---###-- */ 0x3c, /* --####-- */ 0x6c, /* -##-##-- */ 0xcc, /* ##--##-- */ 0xfe, /* #######- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x1e, /* ---####- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x35 ('5') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xfc, /* ######-- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x36 ('6') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x60, /* -##----- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xfc, /* ######-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x37 ('7') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x38 ('8') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x39 ('9') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7e, /* -######- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x0c, /* ----##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x3a (':') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x3b (';') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x3c ('<') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x06, /* -----##- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0x06, /* -----##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x3d ('=') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x3e ('>') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0x06, /* -----##- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x3f ('?') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x40 ('@') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xde, /* ##-####- */ 0xde, /* ##-####- */ 0xde, /* ##-####- */ 0xdc, /* ##-###-- */ 0xc0, /* ##------ */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x41 ('A') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x10, /* ---#---- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x42 ('B') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x7c, /* -#####-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0xfc, /* ######-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x43 ('C') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x66, /* -##--##- */ 0xc2, /* ##----#- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc2, /* ##----#- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x44 ('D') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xf8, /* #####--- */ 0x6c, /* -##-##-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x6c, /* -##-##-- */ 0xf8, /* #####--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x45 ('E') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x66, /* -##--##- */ 0x62, /* -##---#- */ 0x68, /* -##-#--- */ 0x78, /* -####--- */ 0x68, /* -##-#--- */ 0x60, /* -##----- */ 0x62, /* -##---#- */ 0x66, /* -##--##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x46 ('F') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x66, /* -##--##- */ 0x62, /* -##---#- */ 0x68, /* -##-#--- */ 0x78, /* -####--- */ 0x68, /* -##-#--- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x47 ('G') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x66, /* -##--##- */ 0xc2, /* ##----#- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xde, /* ##-####- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x66, /* -##--##- */ 0x3a, /* --###-#- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x48 ('H') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x49 ('I') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x4a ('J') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x1e, /* ---####- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x4b ('K') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xe6, /* ###--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x6c, /* -##-##-- */ 0x78, /* -####--- */ 0x78, /* -####--- */ 0x6c, /* -##-##-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0xe6, /* ###--##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x4c ('L') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xf0, /* ####---- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x62, /* -##---#- */ 0x66, /* -##--##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x4d ('M') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xee, /* ###-###- */ 0xfe, /* #######- */ 0xfe, /* #######- */ 0xd6, /* ##-#-##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x4e ('N') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xe6, /* ###--##- */ 0xf6, /* ####-##- */ 0xfe, /* #######- */ 0xde, /* ##-####- */ 0xce, /* ##--###- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x4f ('O') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x50 ('P') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x51 ('Q') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xd6, /* ##-#-##- */ 0xde, /* ##-####- */ 0x7c, /* -#####-- */ 0x0c, /* ----##-- */ 0x0e, /* ----###- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x52 ('R') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x7c, /* -#####-- */ 0x6c, /* -##-##-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0xe6, /* ###--##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x53 ('S') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x64, /* -##--#-- */ 0x38, /* --###--- */ 0x0c, /* ----##-- */ 0x06, /* -----##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x54 ('T') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0x7e, /* -######- */ 0x5a, /* -#-##-#- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x55 ('U') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x56 ('V') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x10, /* ---#---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x57 ('W') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xd6, /* ##-#-##- */ 0xd6, /* ##-#-##- */ 0xd6, /* ##-#-##- */ 0xfe, /* #######- */ 0xee, /* ###-###- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x58 ('X') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x6c, /* -##-##-- */ 0x7c, /* -#####-- */ 0x38, /* --###--- */ 0x38, /* --###--- */ 0x7c, /* -#####-- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x59 ('Y') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x5a ('Z') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0x86, /* #----##- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0xc2, /* ##----#- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x5b ('[') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x5c ('\') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc0, /* ##------ */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0x06, /* -----##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x5d (']') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x5e ('^') */ 0x10, /* ---#---- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x5f ('_') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ /* 0x60 ('`') */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x61 ('a') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x62 ('b') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xe0, /* ###----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x78, /* -####--- */ 0x6c, /* -##-##-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x63 ('c') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x64 ('d') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x1c, /* ---###-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x3c, /* --####-- */ 0x6c, /* -##-##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x65 ('e') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x66 ('f') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x64, /* -##--#-- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x67 ('g') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x7c, /* -#####-- */ 0x0c, /* ----##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x68 ('h') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xe0, /* ###----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x6c, /* -##-##-- */ 0x76, /* -###-##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0xe6, /* ###--##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x69 ('i') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x6a ('j') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x00, /* -------- */ 0x0e, /* ----###- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x00, /* -------- */ /* 0x6b ('k') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xe0, /* ###----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x66, /* -##--##- */ 0x6c, /* -##-##-- */ 0x78, /* -####--- */ 0x78, /* -####--- */ 0x6c, /* -##-##-- */ 0x66, /* -##--##- */ 0xe6, /* ###--##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x6c ('l') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x6d ('m') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xec, /* ###-##-- */ 0xfe, /* #######- */ 0xd6, /* ##-#-##- */ 0xd6, /* ##-#-##- */ 0xd6, /* ##-#-##- */ 0xd6, /* ##-#-##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x6e ('n') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xdc, /* ##-###-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x6f ('o') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x70 ('p') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xdc, /* ##-###-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x00, /* -------- */ /* 0x71 ('q') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x7c, /* -#####-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x1e, /* ---####- */ 0x00, /* -------- */ /* 0x72 ('r') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xdc, /* ##-###-- */ 0x76, /* -###-##- */ 0x66, /* -##--##- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x73 ('s') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0x60, /* -##----- */ 0x38, /* --###--- */ 0x0c, /* ----##-- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x74 ('t') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x10, /* ---#---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0xfc, /* ######-- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x36, /* --##-##- */ 0x1c, /* ---###-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x75 ('u') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x76 ('v') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x77 ('w') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xd6, /* ##-#-##- */ 0xd6, /* ##-#-##- */ 0xd6, /* ##-#-##- */ 0xfe, /* #######- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x78 ('x') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x38, /* --###--- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x79 ('y') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7e, /* -######- */ 0x06, /* -----##- */ 0x0c, /* ----##-- */ 0xf8, /* #####--- */ 0x00, /* -------- */ /* 0x7a ('z') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0xcc, /* ##--##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x7b ('{') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x0e, /* ----###- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x70, /* -###---- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x0e, /* ----###- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x7c ('|') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x7d ('}') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x70, /* -###---- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x0e, /* ----###- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x70, /* -###---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x7e ('~') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x7f */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x80 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x81 */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x82 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x1f, /* ---##### */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x83 */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x1f, /* ---##### */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x84 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x85 */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x86 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x1f, /* ---##### */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x87 */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x1f, /* ---##### */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x88 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xf8, /* #####--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x89 */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0xf8, /* #####--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x8a */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x8b */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x8c */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xf8, /* #####--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x8d */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0xf8, /* #####--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x8e */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x8f */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0xff, /* ######## */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x90 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x91 */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x92 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7f, /* -####### */ 0x60, /* -##----- */ 0x7f, /* -####### */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x93 */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6f, /* -##-#### */ 0x60, /* -##----- */ 0x7f, /* -####### */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x94 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x95 */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x96 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7f, /* -####### */ 0x60, /* -##----- */ 0x6f, /* -##-#### */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x97 */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6f, /* -##-#### */ 0x60, /* -##----- */ 0x6f, /* -##-#### */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x98 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x0c, /* ----##-- */ 0xfc, /* ######-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x99 */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0xec, /* ###-##-- */ 0x0c, /* ----##-- */ 0xfc, /* ######-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x9a */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x9b */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0xef, /* ###-#### */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x9c */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x0c, /* ----##-- */ 0xec, /* ###-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x9d */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0xec, /* ###-##-- */ 0x0c, /* ----##-- */ 0xec, /* ###-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x9e */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0xef, /* ###-#### */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x9f */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0xef, /* ###-#### */ 0x00, /* -------- */ 0xef, /* ###-#### */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0xa0 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x82, /* #-----#- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xa1 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x3c, /* --####-- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xa2 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x10, /* ---#---- */ 0x7c, /* -#####-- */ 0xd6, /* ##-#-##- */ 0xd0, /* ##-#---- */ 0xd0, /* ##-#---- */ 0xd0, /* ##-#---- */ 0xd6, /* ##-#-##- */ 0x7c, /* -#####-- */ 0x10, /* ---#---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xa3 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x66, /* -##--##- */ 0xf6, /* ####-##- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xa4 */ 0x00, /* -------- */ 0x1c, /* ---###-- */ 0x32, /* --##--#- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0xfc, /* ######-- */ 0x60, /* -##----- */ 0xfc, /* ######-- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x32, /* --##--#- */ 0x1c, /* ---###-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xa5 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x7e, /* -######- */ 0x18, /* ---##--- */ 0x7e, /* -######- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xa6 */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x60, /* -##----- */ 0x38, /* --###--- */ 0x0c, /* ----##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xa7 */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0x60, /* -##----- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x0c, /* ----##-- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xa8 */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0x60, /* -##----- */ 0x38, /* --###--- */ 0x0c, /* ----##-- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xa9 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x42, /* -#----#- */ 0x99, /* #--##--# */ 0xa5, /* #-#--#-# */ 0xa1, /* #-#----# */ 0xa5, /* #-#--#-# */ 0x99, /* #--##--# */ 0x42, /* -#----#- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xaa */ 0x00, /* -------- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x3e, /* --#####- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xab */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x36, /* --##-##- */ 0x6c, /* -##-##-- */ 0xd8, /* ##-##--- */ 0x6c, /* -##-##-- */ 0x36, /* --##-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xac */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xad */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xae */ 0x00, /* -------- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x42, /* -#----#- */ 0xb9, /* #-###--# */ 0xa5, /* #-#--#-# */ 0xb9, /* #-###--# */ 0xa5, /* #-#--#-# */ 0xa5, /* #-#--#-# */ 0x42, /* -#----#- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xaf */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb0 */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb1 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x7e, /* -######- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb2 */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb3 */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x18, /* ---##--- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb4 */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0x8c, /* #---##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0xc2, /* ##----#- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb5 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xf6, /* ####-##- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0x00, /* -------- */ /* 0xb6 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7f, /* -####### */ 0xd6, /* ##-#-##- */ 0xd6, /* ##-#-##- */ 0x76, /* -###-##- */ 0x36, /* --##-##- */ 0x36, /* --##-##- */ 0x36, /* --##-##- */ 0x36, /* --##-##- */ 0x36, /* --##-##- */ 0x36, /* --##-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb7 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb8 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0xcc, /* ##--##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb9 */ 0x30, /* --##---- */ 0x70, /* -###---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xba */ 0x00, /* -------- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xbb */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xd8, /* ##-##--- */ 0x6c, /* -##-##-- */ 0x36, /* --##-##- */ 0x6c, /* -##-##-- */ 0xd8, /* ##-##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xbc */ 0x00, /* -------- */ 0x00, /* -------- */ 0x77, /* -###-### */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcf, /* ##--#### */ 0xcf, /* ##--#### */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x77, /* -###-### */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xbd */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6e, /* -##-###- */ 0xdb, /* ##-##-## */ 0xdb, /* ##-##-## */ 0xdf, /* ##-##### */ 0xd8, /* ##-##--- */ 0xdb, /* ##-##-## */ 0x6e, /* -##-###- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xbe */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xbf */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xc0 */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xc1 */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xc2 */ 0x10, /* ---#---- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xc3 */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xc4 */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xc5 */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xc6 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x3e, /* --#####- */ 0x78, /* -####--- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xfc, /* ######-- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xde, /* ##-####- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xc7 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x66, /* -##--##- */ 0xc2, /* ##----#- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc2, /* ##----#- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x0c, /* ----##-- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x00, /* -------- */ /* 0xc8 */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x66, /* -##--##- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x66, /* -##--##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xc9 */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x66, /* -##--##- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x66, /* -##--##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xca */ 0x10, /* ---#---- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x66, /* -##--##- */ 0x60, /* -##----- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x66, /* -##--##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xcb */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x66, /* -##--##- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x66, /* -##--##- */ 0xfe, /* #######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xcc */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xcd */ 0x06, /* -----##- */ 0x0c, /* ----##-- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xce */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x66, /* -##--##- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xcf */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x00, /* -------- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd0 */ 0x00, /* -------- */ 0x00, /* -------- */ 0xf8, /* #####--- */ 0x6c, /* -##-##-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0xf6, /* ####-##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x6c, /* -##-##-- */ 0xf8, /* #####--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd1 */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xe6, /* ###--##- */ 0xf6, /* ####-##- */ 0xfe, /* #######- */ 0xde, /* ##-####- */ 0xce, /* ##--###- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd2 */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd3 */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd4 */ 0x10, /* ---#---- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd5 */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd6 */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd7 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x66, /* -##--##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd8 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0xc6, /* ##---##- */ 0xce, /* ##--###- */ 0xce, /* ##--###- */ 0xde, /* ##-####- */ 0xf6, /* ####-##- */ 0xe6, /* ###--##- */ 0xe6, /* ###--##- */ 0xc6, /* ##---##- */ 0xfc, /* ######-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd9 */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xda */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xdb */ 0x10, /* ---#---- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xdc */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xdd */ 0x06, /* -----##- */ 0x0c, /* ----##-- */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xde */ 0x00, /* -------- */ 0x00, /* -------- */ 0xf0, /* ####---- */ 0x60, /* -##----- */ 0x7c, /* -#####-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xdf */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xcc, /* ##--##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xd6, /* ##-#-##- */ 0xdc, /* ##-###-- */ 0x80, /* #------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xe0 */ 0x00, /* -------- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xe1 */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xe2 */ 0x00, /* -------- */ 0x10, /* ---#---- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xe3 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xe4 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xe5 */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xe6 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0xdb, /* ##-##-## */ 0x1b, /* ---##-## */ 0x7f, /* -####### */ 0xd8, /* ##-##--- */ 0xdb, /* ##-##-## */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xe7 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x18, /* ---##--- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ /* 0xe8 */ 0x00, /* -------- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xe9 */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xea */ 0x00, /* -------- */ 0x10, /* ---#---- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xeb */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xec */ 0x00, /* -------- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xed */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xee */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x66, /* -##--##- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xef */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x38, /* --###--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf0 */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7e, /* -######- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf1 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0xdc, /* ##-###-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf2 */ 0x00, /* -------- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf3 */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf4 */ 0x00, /* -------- */ 0x10, /* ---#---- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf5 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf6 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf7 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf8 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0xce, /* ##--###- */ 0xde, /* ##-####- */ 0xfe, /* #######- */ 0xf6, /* ####-##- */ 0xe6, /* ###--##- */ 0xfc, /* ######-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf9 */ 0x00, /* -------- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xfa */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xfb */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xfc */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xfd */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7e, /* -######- */ 0x06, /* -----##- */ 0x0c, /* ----##-- */ 0xf8, /* #####--- */ 0x00, /* -------- */ /* 0xfe */ 0x00, /* -------- */ 0x00, /* -------- */ 0xf0, /* ####---- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x7c, /* -#####-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xff */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7e, /* -######- */ 0x06, /* -----##- */ 0x0c, /* ----##-- */ 0xf8, /* #####--- */ 0x00, /* -------- */ /* cursor shape (8x16) */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xFF, /* ######## */ 0xFF, /* ######## */ ================================================ FILE: docs/video/font-lat9-8x8.txt ================================================ /* 0x00 */ 0x7e, /* -######- */ 0xc3, /* ##----## */ 0x99, /* #--##--# */ 0xf3, /* ####--## */ 0xe7, /* ###--### */ 0xff, /* ######## */ 0xe7, /* ###--### */ 0x7e, /* -######- */ /* 0x01 */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x02 */ 0x76, /* -###-##- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xdc, /* ##-###-- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0x76, /* -###-##- */ 0x00, /* -------- */ /* 0x03 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x6e, /* -##-###- */ 0xd8, /* ##-##--- */ 0xde, /* ##-####- */ 0xd8, /* ##-##--- */ 0x6e, /* -##-###- */ 0x00, /* -------- */ /* 0x04 */ 0x10, /* ---#---- */ 0x38, /* --###--- */ 0x7c, /* -#####-- */ 0xfe, /* #######- */ 0x7c, /* -#####-- */ 0x38, /* --###--- */ 0x10, /* ---#---- */ 0x00, /* -------- */ /* 0x05 */ 0xa0, /* #-#----- */ 0xa0, /* #-#----- */ 0xe0, /* ###----- */ 0xae, /* #-#-###- */ 0xa4, /* #-#--#-- */ 0x04, /* -----#-- */ 0x04, /* -----#-- */ 0x04, /* -----#-- */ /* 0x06 */ 0xe0, /* ###----- */ 0x80, /* #------- */ 0xc0, /* ##------ */ 0x8e, /* #---###- */ 0x88, /* #---#--- */ 0x0c, /* ----##-- */ 0x08, /* ----#--- */ 0x08, /* ----#--- */ /* 0x07 */ 0x60, /* -##----- */ 0x80, /* #------- */ 0x80, /* #------- */ 0x8c, /* #---##-- */ 0x6a, /* -##-#-#- */ 0x0c, /* ----##-- */ 0x0a, /* ----#-#- */ 0x0a, /* ----#-#- */ /* 0x08 */ 0x80, /* #------- */ 0x80, /* #------- */ 0x80, /* #------- */ 0x8e, /* #---###- */ 0xe8, /* ###-#--- */ 0x0c, /* ----##-- */ 0x08, /* ----#--- */ 0x08, /* ----#--- */ /* 0x09 */ 0x22, /* --#---#- */ 0x88, /* #---#--- */ 0x22, /* --#---#- */ 0x88, /* #---#--- */ 0x22, /* --#---#- */ 0x88, /* #---#--- */ 0x22, /* --#---#- */ 0x88, /* #---#--- */ /* 0x0a */ 0x55, /* -#-#-#-# */ 0xaa, /* #-#-#-#- */ 0x55, /* -#-#-#-# */ 0xaa, /* #-#-#-#- */ 0x55, /* -#-#-#-# */ 0xaa, /* #-#-#-#- */ 0x55, /* -#-#-#-# */ 0xaa, /* #-#-#-#- */ /* 0x0b */ 0xee, /* ###-###- */ 0xbb, /* #-###-## */ 0xee, /* ###-###- */ 0xbb, /* #-###-## */ 0xee, /* ###-###- */ 0xbb, /* #-###-## */ 0xee, /* ###-###- */ 0xbb, /* #-###-## */ /* 0x0c */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ /* 0x0d */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ /* 0x0e */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x0f */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ 0xf0, /* ####---- */ /* 0x10 */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ 0x0f, /* ----#### */ /* 0x11 */ 0x90, /* #--#---- */ 0xd0, /* ##-#---- */ 0xf0, /* ####---- */ 0xb4, /* #-##-#-- */ 0x94, /* #--#-#-- */ 0x04, /* -----#-- */ 0x04, /* -----#-- */ 0x07, /* -----### */ /* 0x12 */ 0xa0, /* #-#----- */ 0xa0, /* #-#----- */ 0xa0, /* #-#----- */ 0xae, /* #-#-###- */ 0x44, /* -#---#-- */ 0x04, /* -----#-- */ 0x04, /* -----#-- */ 0x04, /* -----#-- */ /* 0x13 */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x00, /* -------- */ /* 0x14 */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x00, /* -------- */ /* 0x15 */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0xfe, /* #######- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0xfe, /* #######- */ 0x60, /* -##----- */ 0x00, /* -------- */ /* 0x16 */ 0x02, /* ------#- */ 0x0e, /* ----###- */ 0x3e, /* --#####- */ 0xfe, /* #######- */ 0x3e, /* --#####- */ 0x0e, /* ----###- */ 0x02, /* ------#- */ 0x00, /* -------- */ /* 0x17 */ 0x80, /* #------- */ 0xe0, /* ###----- */ 0xf8, /* #####--- */ 0xfe, /* #######- */ 0xf8, /* #####--- */ 0xe0, /* ###----- */ 0x80, /* #------- */ 0x00, /* -------- */ /* 0x18 */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x7e, /* -######- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ /* 0x19 */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x7e, /* -######- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ /* 0x1a */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0xfe, /* #######- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x1b */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0xfe, /* #######- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x1c */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x7e, /* -######- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x7e, /* -######- */ 0x3c, /* --####-- */ 0x18, /* ---##--- */ /* 0x1d */ 0x00, /* -------- */ 0x24, /* --#--#-- */ 0x66, /* -##--##- */ 0xff, /* ######## */ 0x66, /* -##--##- */ 0x24, /* --#--#-- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x1e */ 0x06, /* -----##- */ 0x06, /* -----##- */ 0x36, /* --##-##- */ 0x66, /* -##--##- */ 0xfe, /* #######- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x00, /* -------- */ /* 0x1f */ 0x00, /* -------- */ 0xc0, /* ##------ */ 0x7c, /* -#####-- */ 0x6e, /* -##-###- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ /* 0x20 (' ') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x21 ('!') */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x78, /* -####--- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x00, /* -------- */ /* 0x22 ('"') */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x23 ('#') */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0xfe, /* #######- */ 0x6c, /* -##-##-- */ 0xfe, /* #######- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ /* 0x24 ('$') */ 0x30, /* --##---- */ 0x7c, /* -#####-- */ 0xc0, /* ##------ */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0xf8, /* #####--- */ 0x30, /* --##---- */ 0x00, /* -------- */ /* 0x25 ('%') */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xcc, /* ##--##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x66, /* -##--##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ /* 0x26 ('&') */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ /* 0x27 (''') */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x28 ('(') */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x00, /* -------- */ /* 0x29 (')') */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0x00, /* -------- */ /* 0x2a ('*') */ 0x00, /* -------- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0xff, /* ######## */ 0x3c, /* --####-- */ 0x66, /* -##--##- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x2b ('+') */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0xfc, /* ######-- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x2c (',') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x60, /* -##----- */ /* 0x2d ('-') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x2e ('.') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x00, /* -------- */ /* 0x2f ('/') */ 0x00, /* -------- */ 0x06, /* -----##- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0xc0, /* ##------ */ 0x00, /* -------- */ /* 0x30 ('0') */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xd6, /* ##-#-##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ /* 0x31 ('1') */ 0x30, /* --##---- */ 0x70, /* -###---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0xfc, /* ######-- */ 0x00, /* -------- */ /* 0x32 ('2') */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0x0c, /* ----##-- */ 0x38, /* --###--- */ 0x60, /* -##----- */ 0xcc, /* ##--##-- */ 0xfc, /* ######-- */ 0x00, /* -------- */ /* 0x33 ('3') */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0x0c, /* ----##-- */ 0x38, /* --###--- */ 0x0c, /* ----##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x34 ('4') */ 0x1c, /* ---###-- */ 0x3c, /* --####-- */ 0x6c, /* -##-##-- */ 0xcc, /* ##--##-- */ 0xfe, /* #######- */ 0x0c, /* ----##-- */ 0x1e, /* ---####- */ 0x00, /* -------- */ /* 0x35 ('5') */ 0xfc, /* ######-- */ 0xc0, /* ##------ */ 0xf8, /* #####--- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x36 ('6') */ 0x38, /* --###--- */ 0x60, /* -##----- */ 0xc0, /* ##------ */ 0xf8, /* #####--- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x37 ('7') */ 0xfc, /* ######-- */ 0xcc, /* ##--##-- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x00, /* -------- */ /* 0x38 ('8') */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x39 ('9') */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x7c, /* -#####-- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x70, /* -###---- */ 0x00, /* -------- */ /* 0x3a (':') */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x00, /* -------- */ /* 0x3b (';') */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x60, /* -##----- */ /* 0x3c ('<') */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0xc0, /* ##------ */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x00, /* -------- */ /* 0x3d ('=') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x3e ('>') */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0x00, /* -------- */ /* 0x3f ('?') */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x00, /* -------- */ /* 0x40 ('@') */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xde, /* ##-####- */ 0xde, /* ##-####- */ 0xde, /* ##-####- */ 0xc0, /* ##------ */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x41 ('A') */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xfc, /* ######-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ /* 0x42 ('B') */ 0xfc, /* ######-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x7c, /* -#####-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0xfc, /* ######-- */ 0x00, /* -------- */ /* 0x43 ('C') */ 0x3c, /* --####-- */ 0x66, /* -##--##- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x00, /* -------- */ /* 0x44 ('D') */ 0xf8, /* #####--- */ 0x6c, /* -##-##-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x6c, /* -##-##-- */ 0xf8, /* #####--- */ 0x00, /* -------- */ /* 0x45 ('E') */ 0xfe, /* #######- */ 0x62, /* -##---#- */ 0x68, /* -##-#--- */ 0x78, /* -####--- */ 0x68, /* -##-#--- */ 0x62, /* -##---#- */ 0xfe, /* #######- */ 0x00, /* -------- */ /* 0x46 ('F') */ 0xfe, /* #######- */ 0x62, /* -##---#- */ 0x68, /* -##-#--- */ 0x78, /* -####--- */ 0x68, /* -##-#--- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x00, /* -------- */ /* 0x47 ('G') */ 0x3c, /* --####-- */ 0x66, /* -##--##- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0xce, /* ##--###- */ 0x66, /* -##--##- */ 0x3e, /* --#####- */ 0x00, /* -------- */ /* 0x48 ('H') */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xfc, /* ######-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ /* 0x49 ('I') */ 0x78, /* -####--- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x4a ('J') */ 0x1e, /* ---####- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x4b ('K') */ 0xe6, /* ###--##- */ 0x66, /* -##--##- */ 0x6c, /* -##-##-- */ 0x78, /* -####--- */ 0x6c, /* -##-##-- */ 0x66, /* -##--##- */ 0xe6, /* ###--##- */ 0x00, /* -------- */ /* 0x4c ('L') */ 0xf0, /* ####---- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x62, /* -##---#- */ 0x66, /* -##--##- */ 0xfe, /* #######- */ 0x00, /* -------- */ /* 0x4d ('M') */ 0xc6, /* ##---##- */ 0xee, /* ###-###- */ 0xfe, /* #######- */ 0xfe, /* #######- */ 0xd6, /* ##-#-##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ /* 0x4e ('N') */ 0xc6, /* ##---##- */ 0xe6, /* ###--##- */ 0xf6, /* ####-##- */ 0xde, /* ##-####- */ 0xce, /* ##--###- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ /* 0x4f ('O') */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ /* 0x50 ('P') */ 0xfc, /* ######-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x00, /* -------- */ /* 0x51 ('Q') */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xdc, /* ##-###-- */ 0x78, /* -####--- */ 0x1c, /* ---###-- */ 0x00, /* -------- */ /* 0x52 ('R') */ 0xfc, /* ######-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x7c, /* -#####-- */ 0x6c, /* -##-##-- */ 0x66, /* -##--##- */ 0xe6, /* ###--##- */ 0x00, /* -------- */ /* 0x53 ('S') */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xe0, /* ###----- */ 0x70, /* -###---- */ 0x1c, /* ---###-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x54 ('T') */ 0xfc, /* ######-- */ 0xb4, /* #-##-#-- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x55 ('U') */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x56 ('V') */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x30, /* --##---- */ 0x00, /* -------- */ /* 0x57 ('W') */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xd6, /* ##-#-##- */ 0xfe, /* #######- */ 0xee, /* ###-###- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ /* 0x58 ('X') */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ /* 0x59 ('Y') */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x5a ('Z') */ 0xfe, /* #######- */ 0xc6, /* ##---##- */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x66, /* -##--##- */ 0xfe, /* #######- */ 0x00, /* -------- */ /* 0x5b ('[') */ 0x78, /* -####--- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x5c ('\') */ 0x00, /* -------- */ 0xc0, /* ##------ */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x0c, /* ----##-- */ 0x06, /* -----##- */ 0x00, /* -------- */ /* 0x5d (']') */ 0x78, /* -####--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x5e ('^') */ 0x18, /* ---##--- */ 0x3c, /* --####-- */ 0x66, /* -##--##- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x5f ('_') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ /* 0x60 ('`') */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x61 ('a') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ /* 0x62 ('b') */ 0xe0, /* ###----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0x7c, /* -#####-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ /* 0x63 ('c') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xc0, /* ##------ */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x64 ('d') */ 0x1c, /* ---###-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ /* 0x65 ('e') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xfc, /* ######-- */ 0xc0, /* ##------ */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x66 ('f') */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x00, /* -------- */ /* 0x67 ('g') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x7c, /* -#####-- */ 0x0c, /* ----##-- */ 0xf8, /* #####--- */ /* 0x68 ('h') */ 0xe0, /* ###----- */ 0x60, /* -##----- */ 0x6c, /* -##-##-- */ 0x76, /* -###-##- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0xe6, /* ###--##- */ 0x00, /* -------- */ /* 0x69 ('i') */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x70, /* -###---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x6a ('j') */ 0x0c, /* ----##-- */ 0x00, /* -------- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ /* 0x6b ('k') */ 0xe0, /* ###----- */ 0x60, /* -##----- */ 0x66, /* -##--##- */ 0x6c, /* -##-##-- */ 0x78, /* -####--- */ 0x6c, /* -##-##-- */ 0xe6, /* ###--##- */ 0x00, /* -------- */ /* 0x6c ('l') */ 0x70, /* -###---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x6d ('m') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xfe, /* #######- */ 0xfe, /* #######- */ 0xd6, /* ##-#-##- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ /* 0x6e ('n') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xf8, /* #####--- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ /* 0x6f ('o') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x70 ('p') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xdc, /* ##-###-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ /* 0x71 ('q') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x76, /* -###-##- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x7c, /* -#####-- */ 0x0c, /* ----##-- */ 0x1e, /* ---####- */ /* 0x72 ('r') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xdc, /* ##-###-- */ 0x76, /* -###-##- */ 0x66, /* -##--##- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x00, /* -------- */ /* 0x73 ('s') */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc0, /* ##------ */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0xf8, /* #####--- */ 0x00, /* -------- */ /* 0x74 ('t') */ 0x10, /* ---#---- */ 0x30, /* --##---- */ 0x7c, /* -#####-- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x34, /* --##-#-- */ 0x18, /* ---##--- */ 0x00, /* -------- */ /* 0x75 ('u') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ /* 0x76 ('v') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x30, /* --##---- */ 0x00, /* -------- */ /* 0x77 ('w') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xd6, /* ##-#-##- */ 0xfe, /* #######- */ 0xfe, /* #######- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ /* 0x78 ('x') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ /* 0x79 ('y') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x7c, /* -#####-- */ 0x0c, /* ----##-- */ 0xf8, /* #####--- */ /* 0x7a ('z') */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x98, /* #--##--- */ 0x30, /* --##---- */ 0x64, /* -##--#-- */ 0xfc, /* ######-- */ 0x00, /* -------- */ /* 0x7b ('{') */ 0x1c, /* ---###-- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0xe0, /* ###----- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x1c, /* ---###-- */ 0x00, /* -------- */ /* 0x7c ('|') */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x00, /* -------- */ /* 0x7d ('}') */ 0xe0, /* ###----- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x1c, /* ---###-- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0xe0, /* ###----- */ 0x00, /* -------- */ /* 0x7e ('~') */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x7f */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0x80 */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x81 */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x82 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x1f, /* ---##### */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x83 */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x1f, /* ---##### */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x84 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x85 */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x86 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x1f, /* ---##### */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x87 */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x1f, /* ---##### */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x88 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xf8, /* #####--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x89 */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0xf8, /* #####--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x8a */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x8b */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x8c */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xf8, /* #####--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x8d */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0xf8, /* #####--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x8e */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x8f */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0xff, /* ######## */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ /* 0x90 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x91 */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x92 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7f, /* -####### */ 0x60, /* -##----- */ 0x7f, /* -####### */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x93 */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6f, /* -##-#### */ 0x60, /* -##----- */ 0x7f, /* -####### */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x94 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x95 */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x96 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7f, /* -####### */ 0x60, /* -##----- */ 0x6f, /* -##-#### */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x97 */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6f, /* -##-#### */ 0x60, /* -##----- */ 0x6f, /* -##-#### */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x98 */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x0c, /* ----##-- */ 0xfc, /* ######-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x99 */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0xec, /* ###-##-- */ 0x0c, /* ----##-- */ 0xfc, /* ######-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x9a */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x9b */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0xef, /* ###-#### */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0x9c */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x0c, /* ----##-- */ 0xec, /* ###-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x9d */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0xec, /* ###-##-- */ 0x0c, /* ----##-- */ 0xec, /* ###-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x9e */ 0x00, /* -------- */ 0x00, /* -------- */ 0xff, /* ######## */ 0x00, /* -------- */ 0xef, /* ###-#### */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0x9f */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0xef, /* ###-#### */ 0x00, /* -------- */ 0xef, /* ###-#### */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ /* 0xa0 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xc6, /* ##---##- */ 0xfe, /* #######- */ /* 0xa1 */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x78, /* -####--- */ 0x30, /* --##---- */ /* 0xa2 */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xc0, /* ##------ */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x30, /* --##---- */ 0x00, /* -------- */ /* 0xa3 */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x64, /* -##--#-- */ 0xf0, /* ####---- */ 0x60, /* -##----- */ 0xe6, /* ###--##- */ 0xfc, /* ######-- */ 0x00, /* -------- */ /* 0xa4 */ 0x38, /* --###--- */ 0x64, /* -##--#-- */ 0xf0, /* ####---- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x64, /* -##--#-- */ 0x38, /* --###--- */ 0x00, /* -------- */ /* 0xa5 */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0xfc, /* ######-- */ 0x30, /* --##---- */ 0xfc, /* ######-- */ 0x30, /* --##---- */ 0x30, /* --##---- */ /* 0xa6 */ 0x48, /* -#--#--- */ 0x78, /* -####--- */ 0x84, /* #----#-- */ 0x60, /* -##----- */ 0x18, /* ---##--- */ 0x84, /* #----#-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xa7 */ 0x3e, /* --#####- */ 0x61, /* -##----# */ 0x3c, /* --####-- */ 0x66, /* -##--##- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x86, /* #----##- */ 0x7c, /* -#####-- */ /* 0xa8 */ 0x78, /* -####--- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xc0, /* ##------ */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0xf8, /* #####--- */ 0x00, /* -------- */ /* 0xa9 */ 0x7c, /* -#####-- */ 0x82, /* #-----#- */ 0x9a, /* #--##-#- */ 0xa2, /* #-#---#- */ 0xa2, /* #-#---#- */ 0x9a, /* #--##-#- */ 0x82, /* #-----#- */ 0x7c, /* -#####-- */ /* 0xaa */ 0x3c, /* --####-- */ 0x6c, /* -##-##-- */ 0x3e, /* --#####- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xab */ 0x00, /* -------- */ 0x33, /* --##--## */ 0x66, /* -##--##- */ 0xcc, /* ##--##-- */ 0x66, /* -##--##- */ 0x33, /* --##--## */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xac */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x0c, /* ----##-- */ 0x0c, /* ----##-- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xad */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xae */ 0x7c, /* -#####-- */ 0x82, /* #-----#- */ 0xb2, /* #-##--#- */ 0xaa, /* #-#-#-#- */ 0xb2, /* #-##--#- */ 0xaa, /* #-#-#-#- */ 0x82, /* #-----#- */ 0x7c, /* -#####-- */ /* 0xaf */ 0xff, /* ######## */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb0 */ 0x70, /* -###---- */ 0xd8, /* ##-##--- */ 0x70, /* -###---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb1 */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0xfc, /* ######-- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x00, /* -------- */ /* 0xb2 */ 0x70, /* -###---- */ 0xd8, /* ##-##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0xf8, /* #####--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb3 */ 0x70, /* -###---- */ 0xd8, /* ##-##--- */ 0x30, /* --##---- */ 0xd8, /* ##-##--- */ 0x70, /* -###---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb4 */ 0x6c, /* -##-##-- */ 0xfe, /* #######- */ 0xcc, /* ##--##-- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x66, /* -##--##- */ 0xfe, /* #######- */ 0x00, /* -------- */ /* 0xb5 */ 0x00, /* -------- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xf6, /* ####-##- */ 0xc0, /* ##------ */ /* 0xb6 */ 0x7f, /* -####### */ 0xdb, /* ##-##-## */ 0x7b, /* -####-## */ 0x3b, /* --###-## */ 0x1b, /* ---##-## */ 0x1b, /* ---##-## */ 0x1b, /* ---##-## */ 0x00, /* -------- */ /* 0xb7 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xb8 */ 0x78, /* -####--- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x98, /* #--##--- */ 0x30, /* --##---- */ 0x64, /* -##--#-- */ 0xfc, /* ######-- */ 0x00, /* -------- */ /* 0xb9 */ 0x60, /* -##----- */ 0xe0, /* ###----- */ 0x60, /* -##----- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xba */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xbb */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0x66, /* -##--##- */ 0x33, /* --##--## */ 0x66, /* -##--##- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xbc */ 0x7e, /* -######- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xdc, /* ##-###-- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0x7e, /* -######- */ 0x00, /* -------- */ /* 0xbd */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0xdb, /* ##-##-## */ 0xde, /* ##-####- */ 0xd8, /* ##-##--- */ 0x7e, /* -######- */ 0x00, /* -------- */ /* 0xbe */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xbf */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x00, /* -------- */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x60, /* -##----- */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ /* 0xc0 */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xfc, /* ######-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ /* 0xc1 */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xfc, /* ######-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ /* 0xc2 */ 0x78, /* -####--- */ 0x84, /* #----#-- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xfc, /* ######-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ /* 0xc3 */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xfc, /* ######-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ /* 0xc4 */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xfc, /* ######-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ /* 0xc5 */ 0x30, /* --##---- */ 0x48, /* -#--#--- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xfc, /* ######-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ /* 0xc6 */ 0x3e, /* --#####- */ 0x78, /* -####--- */ 0xd8, /* ##-##--- */ 0xfc, /* ######-- */ 0xd8, /* ##-##--- */ 0xd8, /* ##-##--- */ 0xde, /* ##-####- */ 0x00, /* -------- */ /* 0xc7 */ 0x3c, /* --####-- */ 0x66, /* -##--##- */ 0xc0, /* ##------ */ 0xc0, /* ##------ */ 0x66, /* -##--##- */ 0x3c, /* --####-- */ 0x0c, /* ----##-- */ 0x78, /* -####--- */ /* 0xc8 */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0xfe, /* #######- */ 0x62, /* -##---#- */ 0x78, /* -####--- */ 0x62, /* -##---#- */ 0xfe, /* #######- */ 0x00, /* -------- */ /* 0xc9 */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0xfe, /* #######- */ 0x62, /* -##---#- */ 0x78, /* -####--- */ 0x62, /* -##---#- */ 0xfe, /* #######- */ 0x00, /* -------- */ /* 0xca */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0xfe, /* #######- */ 0x62, /* -##---#- */ 0x78, /* -####--- */ 0x62, /* -##---#- */ 0xfe, /* #######- */ 0x00, /* -------- */ /* 0xcb */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0xfe, /* #######- */ 0x62, /* -##---#- */ 0x78, /* -####--- */ 0x62, /* -##---#- */ 0xfe, /* #######- */ 0x00, /* -------- */ /* 0xcc */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xcd */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xce */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xcf */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xd0 */ 0xf8, /* #####--- */ 0x6c, /* -##-##-- */ 0x66, /* -##--##- */ 0xf6, /* ####-##- */ 0x66, /* -##--##- */ 0x6c, /* -##-##-- */ 0xf8, /* #####--- */ 0x00, /* -------- */ /* 0xd1 */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0xe6, /* ###--##- */ 0xf6, /* ####-##- */ 0xde, /* ##-####- */ 0xce, /* ##--###- */ 0xc6, /* ##---##- */ 0x00, /* -------- */ /* 0xd2 */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xd3 */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xd4 */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xd5 */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xd6 */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xd7 */ 0x00, /* -------- */ 0x6c, /* -##-##-- */ 0x38, /* --###--- */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xd8 */ 0x3e, /* --#####- */ 0x6c, /* -##-##-- */ 0xde, /* ##-####- */ 0xd6, /* ##-#-##- */ 0xf6, /* ####-##- */ 0x6c, /* -##-##-- */ 0xf8, /* #####--- */ 0x00, /* -------- */ /* 0xd9 */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xda */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xdb */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xdc */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xdd */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xde */ 0xf0, /* ####---- */ 0x60, /* -##----- */ 0x7c, /* -#####-- */ 0x66, /* -##--##- */ 0x7c, /* -#####-- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ 0x00, /* -------- */ /* 0xdf */ 0x7c, /* -#####-- */ 0xc6, /* ##---##- */ 0xc6, /* ##---##- */ 0xcc, /* ##--##-- */ 0xc6, /* ##---##- */ 0xd6, /* ##-#-##- */ 0xdc, /* ##-###-- */ 0x80, /* #------- */ /* 0xe0 */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ /* 0xe1 */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ /* 0xe2 */ 0x78, /* -####--- */ 0x84, /* #----#-- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ /* 0xe3 */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ /* 0xe4 */ 0x6c, /* -##-##-- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ /* 0xe5 */ 0x38, /* --###--- */ 0x6c, /* -##-##-- */ 0x78, /* -####--- */ 0x0c, /* ----##-- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ /* 0xe6 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7e, /* -######- */ 0x1b, /* ---##-## */ 0x7e, /* -######- */ 0xd8, /* ##-##--- */ 0x7e, /* -######- */ 0x00, /* -------- */ /* 0xe7 */ 0x00, /* -------- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xc0, /* ##------ */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x18, /* ---##--- */ 0x70, /* -###---- */ /* 0xe8 */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xfc, /* ######-- */ 0xc0, /* ##------ */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xe9 */ 0x0c, /* ----##-- */ 0x18, /* ---##--- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xfc, /* ######-- */ 0xc0, /* ##------ */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xea */ 0x78, /* -####--- */ 0x84, /* #----#-- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xfc, /* ######-- */ 0xc0, /* ##------ */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xeb */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xfc, /* ######-- */ 0xc0, /* ##------ */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xec */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x70, /* -###---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xed */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x70, /* -###---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xee */ 0x70, /* -###---- */ 0xd8, /* ##-##--- */ 0x00, /* -------- */ 0x70, /* -###---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xef */ 0x00, /* -------- */ 0xd8, /* ##-##--- */ 0x00, /* -------- */ 0x70, /* -###---- */ 0x30, /* --##---- */ 0x30, /* --##---- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xf0 */ 0x78, /* -####--- */ 0x70, /* -###---- */ 0x18, /* ---##--- */ 0x7c, /* -#####-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xf1 */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0xf8, /* #####--- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ /* 0xf2 */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xf3 */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xf4 */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xf5 */ 0x76, /* -###-##- */ 0xdc, /* ##-###-- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xf6 */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x78, /* -####--- */ 0x00, /* -------- */ /* 0xf7 */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0xfc, /* ######-- */ 0x00, /* -------- */ 0x30, /* --##---- */ 0x00, /* -------- */ 0x00, /* -------- */ /* 0xf8 */ 0x00, /* -------- */ 0x00, /* -------- */ 0x7c, /* -#####-- */ 0xdc, /* ##-###-- */ 0xfc, /* ######-- */ 0xec, /* ###-##-- */ 0xf8, /* #####--- */ 0x00, /* -------- */ /* 0xf9 */ 0x60, /* -##----- */ 0x30, /* --##---- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ /* 0xfa */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ /* 0xfb */ 0x78, /* -####--- */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ /* 0xfc */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x76, /* -###-##- */ 0x00, /* -------- */ /* 0xfd */ 0x18, /* ---##--- */ 0x30, /* --##---- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x7c, /* -#####-- */ 0x0c, /* ----##-- */ 0xf8, /* #####--- */ /* 0xfe */ 0xf0, /* ####---- */ 0x60, /* -##----- */ 0x78, /* -####--- */ 0x6c, /* -##-##-- */ 0x6c, /* -##-##-- */ 0x78, /* -####--- */ 0x60, /* -##----- */ 0xf0, /* ####---- */ /* 0xff */ 0xcc, /* ##--##-- */ 0x00, /* -------- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0xcc, /* ##--##-- */ 0x7c, /* -#####-- */ 0x0c, /* ----##-- */ 0xf8, /* #####--- */ /* cursor shape (8x8) */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0x00, /* -------- */ 0xFF, /* ######## */ 0xFF, /* ######## */ ================================================ FILE: drivers/block/Makefile ================================================ # fiwix/drivers/block/Makefile # # Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. # Distributed under the terms of the Fiwix License. # .c.o: $(CC) $(CFLAGS) -c -o $@ $< OBJS = dma.o floppy.o part.o ata.o ata_pci.o ata_hd.o atapi.o atapi_cd.o \ ramdisk.o blk_queue.o all: $(OBJS) clean: rm -f *.o ================================================ FILE: drivers/block/ata.c ================================================ /* * fiwix/drivers/block/ata.c * * Copyright 2018-2022, 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 static struct fs_operations ata_driver_fsop = { 0, 0, ata_open, ata_close, NULL, /* read */ NULL, /* write */ ata_ioctl, ata_llseek, NULL, /* readdir */ NULL, /* readdir64 */ 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 */ ata_read, ata_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 */ }; struct ide *ide_table; static struct ide default_ide_table[NR_IDE_CTRLS] = { { IDE_PRIMARY, "primary", IDE0_BASE, IDE0_CTRL, 0, IDE0_IRQ, 0, 0, &ide0_timer, { 0 }, 0, 0, { { IDE_MASTER, "master", "hda", IDE0_MAJOR, 0, IDE_MASTER_MSF, 0, 0, 0, 0, 0, 0, 0, 0, { 0 }, { 0 }, { 0 }, 0, 0, 0, 0, {{ 0 }} }, { IDE_SLAVE, "slave", "hdb", IDE0_MAJOR, 0, IDE_SLAVE_MSF, 0, 0, 0, 0, 0, 0, 0, 0, { 0 }, { 0 }, { 0 }, 0, 0, 0, 0, {{ 0 }} } } }, { IDE_SECONDARY, "secondary", IDE1_BASE, IDE1_CTRL, 0, IDE1_IRQ, 0, 0, &ide1_timer, { 0 }, 0, 0, { { IDE_MASTER, "master", "hdc", IDE1_MAJOR, 0, IDE_MASTER_MSF, 0, 0, 0, 0, 0, 0, 0, 0, { 0 }, { 0 }, { 0 }, 0, 0, 0, 0, {{ 0 }} }, { IDE_SLAVE, "slave", "hdd", IDE1_MAJOR, 0, IDE_SLAVE_MSF, 0, 0, 0, 0, 0, 0, 0, 0, { 0 }, { 0 }, { 0 }, 0, 0, 0, 0, {{ 0 }} } } } }; static struct device ide_device[NR_IDE_CTRLS] = { { "ide0", IDE0_MAJOR, { 0, 0, 0, 0, 0, 0, 0, 0 }, NULL, NULL, &ata_driver_fsop, NULL, NULL, NULL }, { "ide1", IDE1_MAJOR, { 0, 0, 0, 0, 0, 0, 0, 0 }, NULL, NULL, &ata_driver_fsop, NULL, NULL, NULL } }; static struct interrupt irq_config_ide[NR_IDE_CTRLS] = { { 0, "ide0", &irq_ide0, NULL }, { 0, "ide1", &irq_ide1, NULL } }; static void ata_identify_device(struct ide *ide, struct ata_drv *drive) { int cmd, status; if((status = ata_select_drv(ide, drive->num, ATA_CHS_MODE, 0))) { /* some controllers return 0xFF to indicate a non-drive condition */ if(status == 0xFF) { return; } printk("WARNING: %s(): error on device '%s'.\n", __FUNCTION__, drive->dev_name); ata_error(ide, status); } cmd = (drive->flags & DRIVE_IS_ATAPI) ? ATAPI_IDENTIFY_PACKET : ATA_IDENTIFY; outport_b(ide->base + ATA_FEATURES, 0); outport_b(ide->base + ATA_SECCNT, 0); outport_b(ide->base + ATA_SECTOR, 0); outport_b(ide->base + ATA_LCYL, 0); outport_b(ide->base + ATA_HCYL, 0); ata_set_timeout(ide, WAIT_FOR_DISK, WAKEUP_AND_RETURN); outport_b(ide->base + ATA_COMMAND, cmd); if(ide->wait_interrupt) { sleep(ide, PROC_UNINTERRUPTIBLE); } } static int identify_drive(struct ide *ide, struct ata_drv *drive) { short int status; unsigned char *buffer, *buffer2; int n; /* read device identification using a 16bit transfer */ ata_identify_device(ide, drive); status = inport_b(ide->base + ATA_STATUS); if((status & (ATA_STAT_RDY | ATA_STAT_DRQ)) != (ATA_STAT_RDY | ATA_STAT_DRQ)) { return 1; } if(!(buffer = (void *)kmalloc(ATA_HD_SECTSIZE))) { return 1; } inport_sw(ide->base + ATA_DATA, (void *)buffer, ATA_HD_SECTSIZE / sizeof(short int)); /* re-read again using a 32bit transfer */ ata_identify_device(ide, drive); status = inport_b(ide->base + ATA_STATUS); if((status & (ATA_STAT_RDY | ATA_STAT_DRQ)) != (ATA_STAT_RDY | ATA_STAT_DRQ)) { kfree((unsigned int)buffer); return 1; } if(!(buffer2 = (void *)kmalloc(ATA_HD_SECTSIZE))) { kfree((unsigned int)buffer); return 1; } inport_sl(ide->base + ATA_DATA, (void *)buffer2, ATA_HD_SECTSIZE / sizeof(unsigned int)); /* now compare results */ drive->flags |= DRIVE_HAS_DATA32; for(n = 0; n < ATA_HD_SECTSIZE; n++) { if(buffer[n] != buffer2[n]) { /* not good, fall back to 16bits */ drive->flags &= ~DRIVE_HAS_DATA32; break; } } kfree((unsigned int)buffer2); memcpy_b(&drive->ident, (void *)buffer, sizeof(struct ata_drv_ident)); kfree((unsigned int)buffer); /* some basic checks to make sure that data received makes sense */ if(drive->ident.logic_cyls > 0x7F00 && drive->ident.logic_heads > 0x7F00 && drive->ident.logic_spt > 0x7F00 && drive->ident.buffer_cache > 0x7F00) { memset_b(&drive->ident, 0, sizeof(struct ata_drv_ident)); return 1; } if(drive->flags & DRIVE_IS_ATAPI) { if(((drive->ident.gen_config >> 8) & 0x1F) == ATAPI_IS_CDROM) { drive->flags |= DRIVE_IS_CDROM; } if(drive->ident.gen_config & 0x3) { printk("WARNING: %s(): packet size must be 16 bytes!\n"); } } /* more checks */ if(!(drive->flags & DRIVE_IS_CDROM) && (!drive->ident.logic_cyls || !drive->ident.logic_heads || !drive->ident.logic_spt)) { memset_b(&drive->ident, 0, sizeof(struct ata_drv_ident)); return 1; } return 0; } static void get_device_size(struct ata_drv *drive) { if(drive->ident.capabilities & ATA_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 & ATA_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_piomode(struct ata_drv *drive) { int piomode; piomode = 0; if(drive->ident.fields_validity & ATA_HAS_ADVANCED_PIO) { if(drive->ident.adv_pio_modes & 1) { piomode = 3; } if(drive->ident.adv_pio_modes & 2) { piomode = 4; } } return piomode; } static int get_udma(struct ata_drv *drive) { int n, udma; udma = 0; /* * The support for DMA is only for drives which already have UDMA * enabled (by BIOS). */ if(drive->ident.ultradma >> 8) { for(n = 7; n >= 0; n--) { if((drive->ident.ultradma >> 8) & (1 << n)) { udma = n; break; } } } return udma; } static int get_ata(struct ata_drv *drive) { int ata; ata = 3; /* minimum is ATA-3 */ while(drive->ident.majorver & (1 << (ata + 1))) { ata++; } if(!ata) { ata = 1; /* now minimum is ATA-1 */ while(drive->ident.majorver & (1 << (ata + 1))) { ata++; } } return ata; } static void show_capabilities(struct ide *ide, struct ata_drv *drive) { unsigned int cyl, hds, sect; __loff_t size; int ksize, nrsectors; if(!(drive->flags & (DRIVE_IS_DISK | DRIVE_IS_CDROM))) { return; } cyl = drive->ident.logic_cyls; hds = drive->ident.logic_heads; sect = drive->ident.logic_spt; if(drive->ident.fields_validity & ATA_HAS_CURR_VALUES) { cyl = drive->ident.cur_log_cyls; hds = drive->ident.cur_log_heads; sect = drive->ident.cur_log_spt; } drive->pio_mode = get_piomode(drive); drive->udma_mode = get_udma(drive); size = (__loff_t)drive->nr_sects * BPS; size = size >> 10; if(size < 1024) { /* the size is less than 1MB (will be reported in KB) */ ksize = size; size = 0; } else { size = size >> 10; ksize = 0; } printk("%s\t\t\t\t", drive->dev_name); printk("%s %s ", ide->name, drive->name); if(!(drive->flags & DRIVE_IS_ATAPI)) { printk("ATA"); printk("%d", get_ata(drive)); } else { printk("ATAPI"); } swap_asc_word(drive->ident.model_number, 40); if(drive->flags & DRIVE_IS_DISK) { if(ksize) { printk(" disk drive %dKB\n", ksize); } else { printk(" disk drive %dMB\n", (unsigned int)size); } printk(" model=%s\n", drive->ident.model_number); if(drive->nr_sects < ATA_MIN_LBA) { printk("\t\t\t\tCHS=%d/%d/%d", cyl, hds, sect); } else { drive->flags |= DRIVE_REQUIRES_LBA; printk("\t\t\t\tsectors=%d", drive->nr_sects); } printk(" cache=%dKB\n", drive->ident.buffer_cache >> 1); /* default values for 'xfer' */ drive->xfer.read_cmd = ATA_READ_PIO; drive->xfer.write_cmd = ATA_WRITE_PIO; } if(drive->flags & DRIVE_IS_CDROM) { printk(" CDROM drive\n"); printk("\t\t\t\tmodel=%s\n", drive->ident.model_number); printk("\t\t\t\tcache=%dKB\n", drive->ident.buffer_cache >> 1); } /* default common values for 'xfer' */ drive->xfer.copy_read_fn = inport_sw; drive->xfer.copy_write_fn = outport_sw; drive->xfer.copy_raw_factor = 2; /* 16bit */ drive->multi = 1; printk("\t\t\t\tPIO%d", drive->pio_mode); if((drive->ident.rw_multiple & 0xFF) > 1) { /* * Some very old controllers report a value of 16 here but they * don't support read or write multiple in PIO mode. So far, * I can detect these old controlers because they report a zero * in the Advanced PIO Data Transfer Supported Field (word 64). */ if(drive->pio_mode > 0) { drive->flags |= DRIVE_HAS_RW_MULTIPLE; drive->xfer.read_cmd = ATA_READ_MULTIPLE_PIO; drive->xfer.write_cmd = ATA_WRITE_MULTIPLE_PIO; drive->multi = drive->ident.rw_multiple & 0xFF; nrsectors = PAGE_SIZE / ATA_HD_SECTSIZE; drive->multi = MIN(drive->multi, nrsectors); } } #ifdef CONFIG_PCI if(drive->ident.capabilities & ATA_HAS_DMA && drive->ident.ultradma) { if(ide->pci_dev && ide->pci_dev->bar[4] > 0) { if(drive->flags & DRIVE_IS_DISK) { drive->flags |= DRIVE_HAS_DMA; drive->xfer.read_cmd = ATA_READ_DMA; drive->xfer.write_cmd = ATA_WRITE_DMA; printk(", UDMA%d", drive->udma_mode); } } } #endif /* CONFIG_PCI */ if(drive->flags & DRIVE_HAS_DATA32) { printk(", 32bit"); drive->xfer.copy_read_fn = inport_sl; drive->xfer.copy_write_fn = outport_sl; drive->xfer.copy_raw_factor = 4; } else { printk(", 16bit"); } printk(", multi %d", drive->multi); if(drive->flags & DRIVE_HAS_RW_MULTIPLE) { printk("(%d)", drive->ident.rw_multiple & 0xFF); } if(drive->ident.capabilities & ATA_HAS_LBA) { drive->flags |= DRIVE_REQUIRES_LBA; printk(", LBA"); } printk("\n"); } static int ata_softreset(struct ide *ide) { int error; unsigned short int dev_type; error = 0; /* select drive 0 (don't care of ATA_STAT_BSY bit) */ outport_b(ide->base + ATA_DRVHD, ATA_CHS_MODE); ata_delay(); /* prepare for an interrupt */ ata_set_timeout(ide, WAIT_FOR_DISK, WAKEUP_AND_RETURN); outport_b(ide->ctrl + ATA_DEV_CTRL, ATA_DEVCTR_SRST); ata_delay(); outport_b(ide->ctrl + ATA_DEV_CTRL, 0); ata_delay(); /* select drive 0 (don't care of ATA_STAT_BSY bit) */ outport_b(ide->base + ATA_DRVHD, ATA_CHS_MODE); ata_delay(); if(ide->wait_interrupt) { sleep(ide, PROC_UNINTERRUPTIBLE); } if(ata_wait_nobusy(ide)) { printk("WARNING: %s(): reset error on ide%d(0).\n", __FUNCTION__, ide->channel); error = 1; } else { /* find out the device type */ if(inport_b(ide->base + ATA_SECCNT) == 1 && inport_b(ide->base + ATA_SECTOR) == 1) { dev_type = (inport_b(ide->base + ATA_HCYL) << 8) | inport_b(ide->base + ATA_LCYL); switch(dev_type) { case 0xEB14: ide->drive[IDE_MASTER].flags |= DRIVE_IS_ATAPI; break; case 0x0: default: ide->drive[IDE_MASTER].flags |= DRIVE_IS_DISK; } } } /* select drive 1 (don't care of ATA_STAT_BSY bit) */ outport_b(ide->base + ATA_DRVHD, ATA_CHS_MODE + (1 << 4)); ata_delay(); if(ata_wait_nobusy(ide)) { printk("WARNING: %s(): reset error on ide%d(1).\n", __FUNCTION__, ide->channel); /* select drive 0 (don't care of ATA_STAT_BSY bit) */ outport_b(ide->base + ATA_DRVHD, ATA_CHS_MODE); ata_delay(); ata_wait_nobusy(ide); error |= (1 << 4); } outport_b(ide->ctrl + ATA_DEV_CTRL, 0); ata_delay(); if(error > 1) { return error; } /* find out the device type */ if(inport_b(ide->base + ATA_SECCNT) == 1 && inport_b(ide->base + ATA_SECTOR) == 1) { dev_type = (inport_b(ide->base + ATA_HCYL) << 8) | inport_b(ide->base + ATA_LCYL); switch(dev_type) { case 0xEB14: ide->drive[IDE_SLAVE].flags |= DRIVE_IS_ATAPI; break; case 0x0: default: ide->drive[IDE_SLAVE].flags |= DRIVE_IS_DISK; } } return error; } static void sector2chs(__off_t offset, int *cyl, int *head, int *sector, struct ata_drv_ident *ident) { int r; *cyl = offset / (ident->logic_spt * ident->logic_heads); r = offset % (ident->logic_spt * ident->logic_heads); *head = r / ident->logic_spt; *sector = (r % ident->logic_spt) + 1; } void irq_ide0(int num, struct sigcontext *sc) { struct ide *ide; ide = &ide_table[IDE_PRIMARY]; if(!ide->wait_interrupt) { printk("WARNING: %s(): unexpected interrupt!\n", __FUNCTION__); } else { ide->irq_timeout = ide->wait_interrupt = 0; ata_end_request(ide); } } void irq_ide1(int num, struct sigcontext *sc) { struct ide *ide; ide = &ide_table[IDE_SECONDARY]; if(!ide->wait_interrupt) { printk("WARNING: %s(): unexpected interrupt!\n", __FUNCTION__); } else { ide->irq_timeout = ide->wait_interrupt = 0; ata_end_request(ide); } } void ide0_timer(unsigned int arg) { struct ide *ide; ide = &ide_table[IDE_PRIMARY]; ide->irq_timeout = 1; ide->wait_interrupt = 0; ata_end_request(ide); } void ide1_timer(unsigned int arg) { struct ide *ide; ide = &ide_table[IDE_SECONDARY]; ide->irq_timeout = 1; ide->wait_interrupt = 0; ata_end_request(ide); } void ata_error(struct ide *ide, int status) { int error; if(status & ATA_STAT_ERR) { error = inport_b(ide->base + ATA_ERROR); if(error) { printk("error=0x%x [", error); if(error & ATA_ERR_AMNF) { printk("address mark not found, "); } if(error & ATA_ERR_TK0NF) { printk("track 0 not found (no media) or media error, "); } if(error & ATA_ERR_ABRT) { printk("command aborted, "); } if(error & ATA_ERR_MCR) { printk("media change requested, "); } if(error & ATA_ERR_IDNF) { printk("id mark not found, "); } if(error & ATA_ERR_MC) { printk("media changer, "); } if(error & ATA_ERR_UNC) { printk("uncorrectable data, "); } if(error & ATA_ERR_BBK) { printk("bad block, "); } printk("]"); } } if(status & ATA_STAT_DWF) { printk("device fault, "); } if(status & ATA_STAT_BSY) { printk("device busy, "); } printk("\n"); } void ata_delay(void) { int n; for(n = 0; n < 10000; n++) { NOP(); } } void ata_wait400ns(struct ide *ide) { int n; /* wait 400ns */ for(n = 0; n < 4; n++) { inport_b(ide->ctrl + ATA_ALT_STATUS); } } int ata_wait_nobusy(struct ide *ide) { int n, retries, status; status = 0; SET_ATA_RDY_RETR(retries); for(n = 0; n < retries; n++) { status = inport_b(ide->base + ATA_STATUS); if(!(status & ATA_STAT_BSY)) { return 0; } ata_delay(); } return status; } int ata_wait_state(struct ide *ide, unsigned char state) { int n, retries, status; status = 0; SET_ATA_RDY_RETR(retries); for(n = 0; n < retries; n++) { status = inport_b(ide->base + ATA_STATUS); if(!(status & ATA_STAT_BSY)) { if(status & (ATA_STAT_ERR | ATA_STAT_DWF)) { return status; } if(status & state) { return 0; } } ata_delay(); } return status; } int ata_io(struct ide *ide, struct ata_drv *drive, __off_t offset, int nrsectors) { int cyl, sector, head; if(drive->flags & DRIVE_REQUIRES_LBA) { if(!ata_select_drv(ide, drive->num, ATA_LBA_MODE, offset >> 24)) { outport_b(ide->base + ATA_FEATURES, 0); outport_b(ide->base + ATA_SECCNT, nrsectors); outport_b(ide->base + ATA_LOWLBA, offset & 0xFF); outport_b(ide->base + ATA_MIDLBA, (offset >> 8) & 0xFF); outport_b(ide->base + ATA_HIGHLBA, (offset >> 16) & 0xFF); return 0; } } else { sector2chs(offset, &cyl, &head, §or, &drive->ident); if(!ata_select_drv(ide, drive->num, ATA_CHS_MODE, head)) { outport_b(ide->base + ATA_FEATURES, 0); outport_b(ide->base + ATA_SECCNT, nrsectors); outport_b(ide->base + ATA_SECTOR, sector); outport_b(ide->base + ATA_LCYL, cyl); outport_b(ide->base + ATA_HCYL, (cyl >> 8)); return 0; } } printk("WARNING: %s(): %s: drive not ready.\n", __FUNCTION__, drive->dev_name); return -EIO; } void ata_set_timeout(struct ide *ide, int timeout, int reason) { ide->wait_interrupt = ide->base; ide->creq.fn = ide->timer_fn; ide->creq.arg = reason; add_callout(&ide->creq, timeout); } void ata_end_request(struct ide *ide) { struct blk_request *br, *brh; struct xfer_data *xd; if(!ide->irq_timeout) { del_callout(&ide->creq); } if(ide->creq.arg == WAKEUP_AND_RETURN) { wakeup(ide); return; } if((br = (struct blk_request *)ide->device->requests_queue)) { if(br->status != BR_PROCESSING) { /* FIXME: needed? */ printk("WARNING: block request: flag is %d in block %d.\n", br->status, br->block); } xd = (struct xfer_data *)br->device->xfer_data; br->errno = xd->rw_end_fn(ide, xd); if(br->errno < 0 || xd->count == xd->sectors_to_io) { ide->device->requests_queue = (void *)br->next; br->status = BR_COMPLETED; if(br->head_group) { brh = br->head_group; brh->left--; if(!brh->left) { wakeup(brh); } } else { wakeup(br); } if(br->errno < 0) { return; } } if(br->next) { run_blk_request(br->device); } } } int ata_select_drv(struct ide *ide, int drive, int mode, unsigned char lba28_head) { int n, status; status = 0; for(n = 0; n < MAX_IDE_ERR; n++) { if((status = ata_wait_nobusy(ide))) { continue; } break; } if(status) { return status; } /* 0x80 and 0x20 are for the obsolete bits #7 and #5 respectively */ outport_b(ide->base + ATA_DRVHD, 0x80 | 0x20 | (mode + (drive << 4)) | lba28_head); ata_wait400ns(ide); for(n = 0; n < MAX_IDE_ERR; n++) { if((status = ata_wait_nobusy(ide))) { continue; } break; } return status; } 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]; } /* set default values */ void ide_table_init(struct ide *ide, int channel) { memcpy_b(ide, &default_ide_table[channel], sizeof(struct ide)); } int ata_channel_init(struct ide *ide) { int drv_num; int devices, errno; struct ata_drv *drive; if(!register_irq(ide->irq, &irq_config_ide[ide->channel])) { enable_irq(ide->irq); } ide->device = &ide_device[ide->channel]; errno = ata_softreset(ide); devices = 0; ide_device[ide->channel].blksize = (unsigned int *)kmalloc(MAX_MINORS * sizeof(unsigned int)); ide_device[ide->channel].device_data = (unsigned int *)kmalloc(MAX_MINORS * sizeof(unsigned int)); memset_l(ide_device[ide->channel].blksize, 0, MAX_MINORS); memset_l(ide_device[ide->channel].device_data, 0, MAX_MINORS); for(drv_num = IDE_MASTER; drv_num <= IDE_SLAVE; drv_num++) { /* * ata_softreset() returns error in the low nibble for master * devices, and in the high nibble for slave devices. */ if(!(errno & (1 << (drv_num * 4)))) { drive = &ide->drive[drv_num]; if(!(identify_drive(ide, drive))) { get_device_size(drive); show_capabilities(ide, drive); SET_MINOR(ide_device[ide->channel].minors, drv_num << drive->minor_shift); ide_device[ide->channel].blksize[drv_num << drive->minor_shift] = BLKSIZE_1K; if(!devices) { register_device(BLK_DEV, &ide_device[ide->channel]); } if(drive->flags & DRIVE_IS_DISK) { if(!ata_hd_init(ide, drive)) { devices++; } } if(drive->flags & DRIVE_IS_CDROM) { if(!atapi_cd_init(ide, drive)) { devices++; } } } } } if(!devices) { disable_irq(ide->irq); unregister_irq(ide->irq, &irq_config_ide[ide->channel]); kfree((unsigned int)ide_device[ide->channel].blksize); kfree((unsigned int)ide_device[ide->channel].device_data); } return devices; } int ata_open(struct inode *i, struct fd *f) { struct ide *ide; struct ata_drv *drive; if(!(ide = get_ide_controller(i->rdev))) { return -EINVAL; } if(!get_device(BLK_DEV, i->rdev)) { return -ENXIO; } drive = &ide->drive[GET_DRIVE_NUM(i->rdev)]; return drive->fsop->open(i, f); } int ata_close(struct inode *i, struct fd *f) { struct ide *ide; struct ata_drv *drive; if(!(ide = get_ide_controller(i->rdev))) { return -EINVAL; } if(!get_device(BLK_DEV, i->rdev)) { return -ENXIO; } drive = &ide->drive[GET_DRIVE_NUM(i->rdev)]; return drive->fsop->close(i, f); } int ata_read(__dev_t dev, __blk_t block, char *buffer, int blksize) { struct ide *ide; struct ata_drv *drive; if(!(ide = get_ide_controller(dev))) { printk("%s(): no ide controller!\n", __FUNCTION__); return -EINVAL; } if(!get_device(BLK_DEV, dev)) { return -ENXIO; } drive = &ide->drive[GET_DRIVE_NUM(dev)]; return drive->fsop->read_block(dev, block, buffer, blksize); } int ata_write(__dev_t dev, __blk_t block, char *buffer, int blksize) { struct ide *ide; struct ata_drv *drive; if(!(ide = get_ide_controller(dev))) { printk("%s(): no ide controller!\n", __FUNCTION__); return -EINVAL; } if(!get_device(BLK_DEV, dev)) { return -ENXIO; } drive = &ide->drive[GET_DRIVE_NUM(dev)]; return drive->fsop->write_block(dev, block, buffer, blksize); } int ata_ioctl(struct inode *i, struct fd *f, int cmd, unsigned int arg) { struct ide *ide; struct ata_drv *drive; if(!(ide = get_ide_controller(i->rdev))) { return -EINVAL; } if(!get_device(BLK_DEV, i->rdev)) { return -ENXIO; } drive = &ide->drive[GET_DRIVE_NUM(i->rdev)]; return drive->fsop->ioctl(i, f, cmd, arg); } __loff_t ata_llseek(struct inode *i, __loff_t offset) { struct ide *ide; struct ata_drv *drive; if(!(ide = get_ide_controller(i->rdev))) { return -EINVAL; } if(!get_device(BLK_DEV, i->rdev)) { return -ENXIO; } drive = &ide->drive[GET_DRIVE_NUM(i->rdev)]; return drive->fsop->llseek(i, offset); } void ata_init(void) { int channel; struct ide *ide; ide_table = (struct ide *)kmalloc(sizeof(struct ide) * NR_IDE_CTRLS); memset_b(ide_table, 0, PAGE_SIZE); channel = IDE_PRIMARY; #ifdef CONFIG_PCI channel = ata_pci(ide_table); #endif /* CONFIG_PCI */ /* ISA addresses are discarded if ide_pci() found a controller */ channel = channel ? NR_IDE_CTRLS : IDE_PRIMARY; ide = ide_table; while(channel < NR_IDE_CTRLS) { ide_table_init(ide, channel); printk("ide%d 0x%04x-0x%04x %d\t", channel, ide->base, ide->base + IDE_BASE_LEN, ide->irq); printk("ISA IDE controller\n"); if(!ata_channel_init(ide)) { printk("\t\t\t\tno drives detected\n"); } channel++; ide++; } } ================================================ FILE: drivers/block/ata_hd.c ================================================ /* * fiwix/drivers/block/ata_hd.c * * Copyright 2018-2022, 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 fs_operations ata_hd_driver_fsop = { 0, 0, ata_hd_open, ata_hd_close, NULL, /* read */ NULL, /* write */ ata_hd_ioctl, ata_hd_llseek, NULL, /* readdir */ NULL, /* readdir64 */ 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 */ ata_hd_read, ata_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 ata_drv *drive, struct partition *part) { int n, minor; struct device *d; minor = 0; if(!(d = get_device(BLK_DEV, rdev))) { printk("WARNING: %s(): unable to assign minors to device %d,%d.\n", __FUNCTION__, MAJOR(rdev), MINOR(rdev)); return; } for(n = 0; n < NR_PARTITIONS; n++) { if(drive->num == IDE_MASTER) { minor = (1 << drive->minor_shift) + n; } if(drive->num == IDE_SLAVE) { minor = (1 << drive->minor_shift) + n + 1; } CLEAR_MINOR(d->minors, minor); if(part[n].type) { SET_MINOR(d->minors, minor); ((unsigned int *)d->blksize)[minor] = BLKSIZE_1K; ((unsigned int *)d->device_data)[minor] = part[n].nr_sects / 2; } } } static __off_t block2sector(__blk_t block, int blksize, struct partition *part, int minor) { __off_t sector; sector = block * (blksize / ATA_HD_SECTSIZE); if(minor) { sector += part[minor - 1].startsect; } return sector; } static int setup_transfer(int mode, __dev_t dev, __blk_t block, char *buffer, int blksize) { struct ide *ide; struct ata_drv *drive; struct partition *part; if(!(ide = get_ide_controller(dev))) { return -EINVAL; } drive = &ide->drive[GET_DRIVE_NUM(dev)]; drive->xd.minor = MINOR(dev); if(drive->num) { drive->xd.minor &= ~(1 << IDE_SLAVE_MSF); } blksize = blksize ? blksize : BLKSIZE_1K; drive->xd.sectors_to_io = MIN(blksize, PAGE_SIZE) / ATA_HD_SECTSIZE; part = drive->part_table; drive->xd.offset = block2sector(block, blksize, part, drive->xd.minor); if(drive->flags & DRIVE_HAS_RW_MULTIPLE) { drive->xd.nrsectors = MIN(drive->xd.sectors_to_io, drive->multi); drive->xd.datalen = ATA_HD_SECTSIZE * drive->xd.nrsectors; } else { drive->xd.nrsectors = 1; drive->xd.datalen = ATA_HD_SECTSIZE; } drive->xd.dev = dev; drive->xd.block = block; drive->xd.buffer = buffer; drive->xd.blksize = blksize; drive->xd.count = 0; if(mode == BLK_READ) { #ifdef CONFIG_PCI drive->xd.bm_cmd = BM_COMMAND_READ; #endif /* CONFIG_PCI */ drive->xd.cmd = drive->xfer.read_cmd; drive->xd.mode = "read"; drive->xd.rw_end_fn = drive->read_end_fn; return drive->read_fn(ide, drive, &drive->xd); } else { #ifdef CONFIG_PCI drive->xd.bm_cmd = BM_COMMAND_WRITE; #endif /* CONFIG_PCI */ drive->xd.cmd = drive->xfer.write_cmd; drive->xd.mode = "write"; drive->xd.rw_end_fn = drive->write_end_fn; return drive->write_fn(ide, drive, &drive->xd); } } static int pio_read(struct ide *ide, struct ata_drv *drive, struct xfer_data *xd) { ide->device->xfer_data = xd; if(ata_io(ide, drive, xd->offset, xd->nrsectors)) { return -EIO; } ata_set_timeout(ide, WAIT_FOR_DISK, 0); outport_b(ide->base + ATA_COMMAND, xd->cmd); return 0; } static int pio_read_end(struct ide *ide, struct xfer_data *xd) { struct ata_drv *drive; int status; drive = &ide->drive[GET_DRIVE_NUM(xd->dev)]; if(ide->irq_timeout) { status = inport_b(ide->base + ATA_STATUS); if((status & (ATA_STAT_RDY | ATA_STAT_DRQ)) != (ATA_STAT_RDY | ATA_STAT_DRQ)) { printk("WARNING: %s(): %s: timeout on hard disk dev %d,%d during read.\n", __FUNCTION__, drive->dev_name, MAJOR(xd->dev), MINOR(xd->dev)); return -EIO; } } status = ata_wait_nobusy(ide); if(status & ATA_STAT_ERR) { printk("WARNING: %s(): %s: error on hard disk dev %d,%d during read.\n", __FUNCTION__, drive->dev_name, MAJOR(xd->dev), MINOR(xd->dev)); printk("\tstatus=0x%x ", status); ata_error(ide, status); printk("\tblock %d, sector %d.\n", xd->block, xd->offset); inport_b(ide->base + ATA_STATUS); /* clear any pending interrupt */ return -EIO; } drive->xfer.copy_read_fn(ide->base + ATA_DATA, (void *)xd->buffer, xd->datalen / drive->xfer.copy_raw_factor); xd->count += xd->nrsectors; if(xd->count < xd->sectors_to_io) { xd->offset += xd->nrsectors; xd->buffer += (ATA_HD_SECTSIZE * xd->nrsectors); return pio_read(ide, drive, xd); } inport_b(ide->base + ATA_STATUS); /* clear any pending interrupt */ return xd->sectors_to_io * ATA_HD_SECTSIZE; } static int pio_write(struct ide *ide, struct ata_drv *drive, struct xfer_data *xd) { int status; ide->device->xfer_data = xd; if(ata_io(ide, drive, xd->offset, xd->nrsectors)) { return -EIO; } outport_b(ide->base + ATA_COMMAND, drive->xfer.write_cmd); status = ata_wait_nobusy(ide); if(status & ATA_STAT_ERR) { printk("WARNING: %s(): %s: error on hard disk dev %d,%d during write.\n", __FUNCTION__, drive->dev_name, MAJOR(xd->dev), MINOR(xd->dev)); printk("\tstatus=0x%x ", status); ata_error(ide, status); printk("\tblock %d, sector %d.\n", xd->block, xd->offset); inport_b(ide->base + ATA_STATUS); /* clear any pending interrupt */ return -EIO; } ata_set_timeout(ide, WAIT_FOR_DISK, 0); drive->xfer.copy_write_fn(ide->base + ATA_DATA, (void *)xd->buffer, xd->datalen / drive->xfer.copy_raw_factor); return 0; } static int pio_write_end(struct ide *ide, struct xfer_data *xd) { struct ata_drv *drive; int status; drive = &ide->drive[GET_DRIVE_NUM(xd->dev)]; if(ide->irq_timeout) { status = inport_b(ide->base + ATA_STATUS); if((status & (ATA_STAT_RDY | ATA_STAT_DRQ)) != (ATA_STAT_RDY | ATA_STAT_DRQ)) { printk("WARNING: %s(): %s: timeout on hard disk dev %d,%d during write.\n", __FUNCTION__, drive->dev_name, MAJOR(xd->dev), MINOR(xd->dev)); return -EIO; } } xd->count += xd->nrsectors; if(xd->count < xd->sectors_to_io) { xd->offset += xd->nrsectors; xd->buffer += (ATA_HD_SECTSIZE * xd->nrsectors); return pio_write(ide, drive, xd); } inport_b(ide->base + ATA_STATUS); /* clear any pending interrupt */ return xd->sectors_to_io * ATA_HD_SECTSIZE; } #ifdef CONFIG_PCI static int dma_transfer(struct ide *ide, struct ata_drv *drive, struct xfer_data *xd) { ide->device->xfer_data = xd; if(ata_io(ide, drive, xd->offset, xd->nrsectors)) { return -EIO; } ata_setup_dma(ide, drive, xd->buffer, xd->datalen, xd->bm_cmd); ata_set_timeout(ide, WAIT_FOR_DISK, 0); outport_b(ide->base + ATA_COMMAND, xd->cmd); ata_start_dma(ide, drive); return 0; } static int dma_transfer_end(struct ide *ide, struct xfer_data *xd) { struct ata_drv *drive; int status; drive = &ide->drive[GET_DRIVE_NUM(xd->dev)]; if(ide->irq_timeout) { status = inport_b(ide->base + ATA_STATUS); if((status & (ATA_STAT_RDY | ATA_STAT_DRQ)) != (ATA_STAT_RDY | ATA_STAT_DRQ)) { ata_stop_dma(ide, drive); printk("WARNING: %s(): %s: timeout on hard disk dev %d,%d during %s.\n", __FUNCTION__, drive->dev_name, MAJOR(xd->dev), MINOR(xd->dev), xd->mode); return -EIO; } } ata_stop_dma(ide, drive); status = ata_wait_nobusy(ide); if(status & ATA_STAT_ERR) { printk("WARNING: %s(): %s: error on hard disk dev %d,%d during %s.\n", __FUNCTION__, drive->dev_name, MAJOR(xd->dev), MINOR(xd->dev), xd->mode); printk("\tstatus=0x%x ", status); ata_error(ide, status); printk("\tblock %d, sector %d.\n", xd->block, xd->offset); inport_b(ide->base + ATA_STATUS); /* clear any pending interrupt */ return -EIO; } xd->count += xd->nrsectors; if(xd->count < xd->sectors_to_io) { xd->offset += xd->nrsectors; xd->buffer += (ATA_HD_SECTSIZE * xd->nrsectors); dma_transfer(ide, drive, xd); } inport_b(ide->base + ATA_STATUS); /* clear any pending interrupt */ return xd->sectors_to_io * ATA_HD_SECTSIZE; } #endif /* CONFIG_PCI */ int ata_hd_open(struct inode *i, struct fd *f) { return 0; } int ata_hd_close(struct inode *i, struct fd *f) { sync_buffers(i->rdev); return 0; } int ata_hd_read(__dev_t dev, __blk_t block, char *buffer, int blksize) { return setup_transfer(BLK_READ, dev, block, buffer, blksize); } int ata_hd_write(__dev_t dev, __blk_t block, char *buffer, int blksize) { return setup_transfer(BLK_WRITE, dev, block, buffer, blksize); } int ata_hd_ioctl(struct inode *i, struct fd *f, int cmd, unsigned int arg) { int minor; struct ide *ide; struct ata_drv *drive; struct partition *part; struct hd_geometry *geom; struct device *d; int errno; if(!(ide = get_ide_controller(i->rdev))) { return -EINVAL; } minor = MINOR(i->rdev); drive = &ide->drive[GET_DRIVE_NUM(i->rdev)]; if(drive->num) { minor &= ~(1 << IDE_SLAVE_MSF); } part = 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 = drive->ident.logic_cyls; geom->heads = (char)drive->ident.logic_heads; geom->sectors = (char)drive->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)drive->nr_sects; } else { *(int *)arg = (unsigned int)drive->part_table[minor - 1].nr_sects; } break; case BLKSSZGET: if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int)))) { return errno; } *(int *)arg = 512; break; case BLKBSZGET: if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int)))) { return errno; } d = ide->device; *(int *)arg = ((unsigned int *)d->blksize)[MINOR(i->rdev)]; break; case BLKFLSBUF: sync_buffers(i->rdev); invalidate_buffers(i->rdev); break; case BLKRRPART: invalidate_buffers(i->rdev); read_msdos_partition(i->rdev, part); assign_minors(i->rdev, drive, part); break; default: return -EINVAL; break; } return 0; } __loff_t ata_hd_llseek(struct inode *i, __loff_t offset) { return offset; } int ata_hd_init(struct ide *ide, struct ata_drv *drive) { int n, status, value; __dev_t rdev; struct device *d; struct partition *part; rdev = 0; drive->fsop = &ata_hd_driver_fsop; part = drive->part_table; if(drive->num == IDE_MASTER) { rdev = MKDEV(drive->major, drive->num); drive->minor_shift = IDE_MASTER_MSF; if(!(d = get_device(BLK_DEV, rdev))) { return -EINVAL; } ((unsigned int *)d->device_data)[0] = drive->nr_sects / 2; } else { rdev = MKDEV(drive->major, 1 << IDE_SLAVE_MSF); drive->minor_shift = IDE_SLAVE_MSF; if(!(d = get_device(BLK_DEV, rdev))) { return -EINVAL; } ((unsigned int *)d->device_data)[1 << IDE_SLAVE_MSF] = drive->nr_sects / 2; } /* prepare for an interrupt */ ata_set_timeout(ide, WAIT_FOR_DISK, WAKEUP_AND_RETURN); if(drive->flags & DRIVE_HAS_RW_MULTIPLE) { /* read/write in 4KB blocks (8 sectors) as maximum */ outport_b(ide->base + ATA_SECCNT, MIN(PAGE_SIZE / ATA_HD_SECTSIZE, drive->multi)); outport_b(ide->base + ATA_DRVHD, drive->num << 4); outport_b(ide->base + ATA_COMMAND, ATA_SET_MULTIPLE_MODE); ata_wait400ns(ide); status = inport_b(ide->base + ATA_STATUS); if(status & (ATA_STAT_ERR | ATA_STAT_DWF)) { printk("WARNING: %s(): error while setting R/W multiple mode.\n", __FUNCTION__); printk("\t"); ata_error(ide, status); printk("\n"); } } if(ide->wait_interrupt) { sleep(ide, PROC_UNINTERRUPTIBLE); } outport_b(ide->ctrl + ATA_DEV_CTRL, ATA_DEVCTR_DRQ); for(;;) { status = inport_b(ide->base + ATA_STATUS); if(!(status & ATA_STAT_BSY)) { break; } ata_delay(); } if(ata_select_drv(ide, drive->num, 0, 0)) { printk("WARNING: %s(): %s: drive not ready.\n", __FUNCTION__, drive->dev_name); } drive->read_fn = pio_read; drive->write_fn = pio_write; drive->read_end_fn = pio_read_end; drive->write_end_fn = pio_write_end; /* setup the transfer mode */ outport_b(ide->base + ATA_FEATURES, ATA_SET_XFERMODE); if(drive->flags & DRIVE_HAS_DMA) { value = (ATA_SET_XFERMODE_UDMA | drive->udma_mode); } else { if(drive->pio_mode > 2) { value = ATA_SET_XFERMODE_PIO | drive->pio_mode; } else { /* PIO default mode */ value = 0; } } outport_b(ide->base + ATA_SECCNT, value); outport_b(ide->base + ATA_DRVHD, drive->num << 4); ata_set_timeout(ide, WAIT_FOR_DISK, WAKEUP_AND_RETURN); outport_b(ide->base + ATA_COMMAND, ATA_SET_FEATURES); if(ide->wait_interrupt) { sleep(ide, PROC_UNINTERRUPTIBLE); } ata_wait400ns(ide); status = inport_b(ide->base + ATA_STATUS); if(status & (ATA_STAT_ERR | ATA_STAT_DWF)) { printk("WARNING: %s(): error while setting transfer mode.\n", __FUNCTION__); printk("\t"); ata_error(ide, status); printk("\n"); } #ifdef CONFIG_PCI /* set DMA Capable drive bit */ if(drive->flags & DRIVE_HAS_DMA) { drive->read_fn = drive->write_fn = dma_transfer; drive->read_end_fn = drive->write_end_fn = dma_transfer_end; value = inport_b(ide->bm + BM_STATUS); outport_b(ide->bm + BM_STATUS, value | (BM_STATUS_DRVDMA << drive->num)); } #endif /* CONFIG_PCI */ /* show disk partition summary */ printk("\t\t\t\tpartition summary: "); if(!read_msdos_partition(rdev, part)) { assign_minors(rdev, drive, part); for(n = 0; n < NR_PARTITIONS; n++) { /* status values other than 0x00 and 0x80 are invalid */ if(part[n].status && part[n].status != 0x80) { continue; } if(part[n].type) { printk("%s%d ", drive->dev_name, n + 1); } } } printk("\n"); return 0; } ================================================ FILE: drivers/block/ata_pci.c ================================================ /* * fiwix/drivers/block/ata_pci.c * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #ifdef CONFIG_PCI static int setup_ata_device(struct ide *ide, struct pci_device *pci_dev) { int bar, channel, found; int size; /* enable I/O space */ pci_write_short(pci_dev, PCI_COMMAND, pci_dev->command | PCI_COMMAND_IO); for(found = 0, channel = 0; channel < NR_IDE_CTRLS; channel++, ide++) { bar = channel * 2; if(pci_dev->flags[bar] & PCI_F_ADDR_SPACE_MEM) { printk("MMIO-based BAR registers are not supported.\n"); continue; } printk("ide%d ", channel); found++; ide_table_init(ide, channel); size = IDE_BASE_LEN; printk("0x%04x-0x%04x %d\t", ide->base, ide->base + size, ide->irq); switch(pci_dev->prog_if & 0xF) { case 0: printk("ISA controller in compatibility mode-only\n"); break; case 0xA: printk("ISA controller in compatibility mode, supports\n"); printk("\t\t\t\tboth channels switched to PCI native mode\n"); break; case 0x5: case 0xF: if((pci_dev->prog_if & 0xF) == 0x5) { printk("PCI controller in native mode-only\n"); } else { printk("PCI controller in native mode, supports\n"); printk("\t\t\t\tboth channels switched to ISA compatibility mode\n"); } ide->base = pci_dev->bar[bar]; ide->ctrl = pci_dev->bar[bar + 1]; ide->irq = pci_dev->irq; size = pci_dev->size[bar]; break; } if(!(kparms.flags & KPARMS_IDE_NODMA)) { if(pci_dev->bar[4] && (pci_dev->prog_if & 0x80)) { ide->bm = (pci_dev->bar[4] + (channel * 8)); printk("\t\t\t\tbus master DMA at 0x%x\n", ide->bm); /* enable I/O space and bus master */ pci_write_short(pci_dev, PCI_COMMAND, pci_dev->command | (PCI_COMMAND_IO | PCI_COMMAND_MASTER)); ide->pci_dev = pci_dev; } } pci_show_desc(pci_dev); if(!ata_channel_init(ide)) { printk("\t\t\t\tno drives detected\n"); } } return found; } void ata_setup_dma(struct ide *ide, struct ata_drv *drive, char *buffer, int datalen, int mode) { int value; struct prd *prd_table = &drive->xfer.prd_table; prd_table->addr = (unsigned int)V2P(buffer); prd_table->size = datalen; prd_table->eot = PRDT_MARK_END; outport_l(ide->bm + BM_PRD_ADDRESS, V2P((unsigned int)prd_table)); value = inport_b(ide->bm + BM_COMMAND); outport_b(ide->bm + BM_COMMAND, value | mode); /* clear Error and Interrupt bits */ value = inport_b(ide->bm + BM_STATUS); outport_b(ide->bm + BM_STATUS, value | BM_STATUS_ERROR | BM_STATUS_INTR); } void ata_start_dma(struct ide *ide, struct ata_drv *drive) { int value; value = inport_b(ide->bm + BM_COMMAND); outport_b(ide->bm + BM_COMMAND, value | BM_COMMAND_START); } void ata_stop_dma(struct ide *ide, struct ata_drv *drive) { int status; inport_b(ide->bm + BM_STATUS); /* extra read */ status = inport_b(ide->bm + BM_STATUS); outport_b(ide->bm + BM_COMMAND, 0); /* stop bus master */ outport_b(ide->bm + BM_STATUS, status); } int ata_pci(struct ide *ide) { struct pci_device *pci_dev; int found; pci_dev = pci_device_table; found = 0; while(pci_dev) { if(pci_dev->class == PCI_CLASS_STORAGE_IDE) { if((found = setup_ata_device(ide, pci_dev))) { /* only one device is supported */ break; } } pci_dev = pci_dev->next; } return found; } #endif /* CONFIG_PCI */ ================================================ FILE: drivers/block/atapi.c ================================================ /* * fiwix/drivers/block/atapi.c * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include int send_packet_command(unsigned char *pkt, struct ide *ide, struct ata_drv *drive, int len) { int status; outport_b(ide->ctrl + ATA_DEV_CTRL, 0); ata_delay(); outport_b(ide->base + ATA_DRVHD, ATA_CHS_MODE); ata_delay(); if(ata_select_drv(ide, drive->num, ATA_CHS_MODE, 0)) { printk("WARNING: %s(): %s: drive not ready to receive PACKET command.\n", __FUNCTION__, drive->dev_name); return 1; } status = ata_wait_state(ide, ATA_STAT_RDY); outport_b(ide->base + ATA_FEATURES, 0); outport_b(ide->base + ATA_SECCNT, 0); outport_b(ide->base + ATA_SECTOR, 0); outport_b(ide->base + ATA_LCYL, len & 0xFF); outport_b(ide->base + ATA_HCYL, len >> 8); outport_b(ide->base + ATA_DRVHD, drive->num << 4); outport_b(ide->base + ATA_COMMAND, ATA_PACKET); ata_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. */ status = ata_wait_state(ide, ATA_STAT_DRQ); if(status) { printk("WARNING: %s(): %s: drive not ready after PACKET sent.\n", __FUNCTION__, drive->dev_name); return 1; } outport_sw(ide->base + ATA_DATA, pkt, 12 / sizeof(short int)); return 0; } int atapi_cmd_testunit(struct ide *ide, struct ata_drv *drive) { unsigned char pkt[12]; memset_b(pkt, 0, sizeof(pkt)); pkt[0] = ATAPI_TEST_UNIT; return send_packet_command(pkt, ide, drive, 0); } int atapi_cmd_reqsense(struct ide *ide, struct ata_drv *drive) { unsigned char pkt[12]; memset_b(pkt, 0, sizeof(pkt)); pkt[0] = ATAPI_REQUEST_SENSE; pkt[4] = 252; /* this command can send up to 252 bytes */ return send_packet_command(pkt, ide, drive, 0); } int atapi_cmd_startstop(int action, struct ide *ide, struct ata_drv *drive) { unsigned char pkt[12]; memset_b(pkt, 0, sizeof(pkt)); pkt[0] = ATAPI_START_STOP; pkt[4] = action; return send_packet_command(pkt, ide, drive, 0); } int atapi_cmd_mediumrm(int action, struct ide *ide, struct ata_drv *drive) { unsigned char pkt[12]; memset_b(pkt, 0, sizeof(pkt)); pkt[0] = ATAPI_MEDIUM_REMOVAL; pkt[4] = action; return send_packet_command(pkt, ide, drive, 0); } int atapi_cmd_get_capacity(struct ide *ide, struct ata_drv *drive) { unsigned char pkt[12]; memset_b(pkt, 0, sizeof(pkt)); pkt[0] = ATAPI_GET_CAPACITY; return send_packet_command(pkt, ide, drive, 8); } int atapi_cmd_read10(struct ide *ide, struct ata_drv *drive) { unsigned char pkt[12]; struct xfer_data *xd; ide->device->xfer_data = &drive->xd; xd = &drive->xd; memset_b(pkt, 0, sizeof(pkt)); pkt[0] = ATAPI_READ10; pkt[2] = (xd->block >> 24) & 0xFF; pkt[3] = (xd->block >> 16) & 0xFF; pkt[4] = (xd->block >> 8) & 0xFF; pkt[5] = xd->block & 0xFF; pkt[7] = (xd->sectors_to_io >> 8) & 0xFF; pkt[8] = xd->sectors_to_io & 0xFF; return send_packet_command(pkt, ide, drive, BLKSIZE_2K); } ================================================ FILE: drivers/block/atapi_cd.c ================================================ /* * fiwix/drivers/block/atapi_cd.c * * Copyright 2018-2022, 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 fs_operations atapi_cd_driver_fsop = { 0, 0, atapi_cd_open, atapi_cd_close, NULL, /* read */ NULL, /* write */ atapi_cd_ioctl, atapi_cd_llseek, NULL, /* readdir */ NULL, /* readdir64 */ 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 */ atapi_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 int setup_transfer(int mode, __dev_t dev, __blk_t block, char *buffer, int blksize) { struct ide *ide; struct ata_drv *drive; if(!(ide = get_ide_controller(dev))) { return -EINVAL; } drive = &ide->drive[GET_DRIVE_NUM(dev)]; drive->xd.dev = dev; drive->xd.block = block; drive->xd.buffer = buffer; drive->xd.blksize = BLKSIZE_2K; drive->xd.sectors_to_io = 1; drive->xd.offset = 0; drive->xd.datalen = ATAPI_CD_SECTSIZE; drive->xd.nrsectors = 1; drive->xd.count = 0; drive->xd.retries = 0; drive->xd.max_retries = MAX_CD_ERR; drive->xd.mode = "read"; drive->xd.rw_end_fn = drive->read_end_fn; return drive->read_fn(ide, drive, &drive->xd); } static int atapi_pio_read(struct ide *ide, struct ata_drv *drive, struct xfer_data *xd) { ide->device->xfer_data = xd; if(!xd->retries) { if(atapi_cmd_read10(ide, drive)) { printk("\tread error on block=%d, offset=%d\n", xd->block, xd->block * xd->blksize); return -EIO; } } ata_set_timeout(ide, WAIT_FOR_CD, 0); return 0; } static int atapi_pio_read_end(struct ide *ide, struct xfer_data *xd) { int status, errno, bytes; int errcode, sense_key; struct ata_drv *drive; drive = &ide->drive[GET_DRIVE_NUM(xd->dev)]; status = 0; while(xd->retries < xd->max_retries) { xd->retries++; if(ide->irq_timeout) { ide->irq_timeout = 0; status = inport_b(ide->base + ATA_STATUS); if((status & (ATA_STAT_RDY | ATA_STAT_DRQ)) != (ATA_STAT_RDY | ATA_STAT_DRQ)) { atapi_pio_read(ide, drive, xd); return 0; } } status = inport_b(ide->base + ATA_STATUS); if(status & ATA_STAT_ERR) { atapi_pio_read(ide, drive, xd); return 0; } if((status & (ATA_STAT_DRQ | ATA_STAT_BSY)) == 0) { break; } bytes = (inport_b(ide->base + ATA_HCYL) << 8) + inport_b(ide->base + ATA_LCYL); if(!bytes || bytes > xd->blksize) { break; } bytes = MAX(bytes, ATAPI_CD_SECTSIZE); /* only 2KB blocksize is supported */ drive->xfer.copy_read_fn(ide->base + ATA_DATA, (void *)xd->buffer, bytes / drive->xfer.copy_raw_factor); /* wait for another interrupt (FIXME: only if in PIO mode) */ return atapi_pio_read(ide, drive, xd); } if(status & ATA_STAT_ERR) { errno = inport_b(ide->base + ATA_ERROR); printk("WARNING: %s(): error on cdrom device %d,%d, status=0x%x error=0x%x,\n", __FUNCTION__, MAJOR(xd->dev), MINOR(xd->dev), status, errno); if(xd->retries < xd->max_retries) { xd->retries++; atapi_pio_read(ide, drive, xd); } return 0; } if(xd->retries >= xd->max_retries) { errcode = inport_b(ide->base + ATA_ERROR); sense_key = (errcode & 0xF0) >> 4; printk("WARNING: %s(): timeout on cdrom device %d,%d, status=0x%x.\n", __FUNCTION__, MAJOR(xd->dev), MINOR(xd->dev), status); printk("\tSense Key code indicates a '%s' condition.\n", sense_key_err[sense_key & 0xF]); /* no need to retry after an illegal request */ if((sense_key & 0xF) == RS_ILLEGAL_REQUEST) { return -EIO; } /* a drive reset may be required at this moment */ xd->retries = 0; if(xd->max_retries) { xd->max_retries--; atapi_pio_read(ide, drive, xd); return 0; } return -EIO; } drive->xd.count = drive->xd.sectors_to_io; return ATAPI_CD_SECTSIZE; } static int request_sense(char *buffer, __dev_t dev, struct ide *ide, struct ata_drv *drive) { int errcode; int sense_key, sense_asc; errcode = inport_b(ide->base + ATA_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("\t\treqsense() returned %d\n", errcode); errcode = atapi_pio_read(ide, drive, &drive->xd); printk("\t\tatapi_pio_read() returned %d\n", errcode); errcode = (int)(buffer[0] & 0x7F); sense_key = (int)(buffer[2] & 0xF); sense_asc = (int)(buffer[12] & 0xFF); printk("\t\terrcode = %x\n", errcode); printk("\t\tsense_key = %x\n", sense_key); printk("\t\tsense_asc = %x\n", sense_asc); return errcode; } static void get_capacity(struct inode *i, struct ide *ide, struct ata_drv *drive) { struct device *d; unsigned int lba, blocklen; if(atapi_cmd_get_capacity(ide, drive)) { printk("WARNING: %s(): error on cdrom device %d,%d while trying to get capacity.\n", __FUNCTION__, MAJOR(i->rdev), MINOR(i->rdev)); } /* this line just to catch interrupt */ ata_set_timeout(ide, WAIT_FOR_CD, WAKEUP_AND_RETURN); drive->xfer.copy_read_fn(ide->base + ATA_DATA, (void *)&lba, 4 / drive->xfer.copy_raw_factor); drive->xfer.copy_read_fn(ide->base + ATA_DATA, (void *)&blocklen, 4 / drive->xfer.copy_raw_factor); d = ide->device; ((unsigned int *)d->device_data)[MINOR(i->rdev)] = (__bswap32(lba) + 1) * (__bswap32(blocklen) / 1024); } int atapi_cd_open(struct inode *i, struct fd *f) { char *buffer; int errcode; int sense_key, sense_asc; int retries; struct ide *ide; struct ata_drv *drive; if(!(ide = get_ide_controller(i->rdev))) { return -EINVAL; } drive = &ide->drive[GET_DRIVE_NUM(i->rdev)]; if(!(buffer = (void *)kmalloc(PAGE_SIZE))) { 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); } /* this line just to catch interrupt */ ata_set_timeout(ide, WAIT_FOR_CD, WAKEUP_AND_RETURN); for(retries = 0; retries < MAX_CD_ERR; retries++) { if(!(errcode = atapi_cmd_startstop(CD_LOAD, ide, drive))) { break; } /* FIXME: this code has not been tested */ 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_pio_read(ide, drive, &drive->xd); 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); return -ENOMEDIUM; } } } if(retries == MAX_CD_ERR) { if(sense_key == RS_NOT_READY) { kfree((unsigned int)buffer); 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)); request_sense(buffer, i->rdev, ide, drive); } /* this line just to catch interrupt */ ata_set_timeout(ide, WAIT_FOR_CD, WAKEUP_AND_RETURN); get_capacity(i, ide, drive); kfree((unsigned int)buffer); return 0; } int atapi_cd_close(struct inode *i, struct fd *f) { struct ide *ide; struct ata_drv *drive; struct device *d; if(!(ide = get_ide_controller(i->rdev))) { return -EINVAL; } drive = &ide->drive[GET_DRIVE_NUM(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 */ ata_set_timeout(ide, WAIT_FOR_CD, WAKEUP_AND_RETURN); d = ide->device; ((unsigned int *)d->device_data)[MINOR(i->rdev)] = 0; return 0; } int atapi_cd_read(__dev_t dev, __blk_t block, char *buffer, int blksize) { return setup_transfer(BLK_READ, dev, block, buffer, blksize); } int atapi_cd_ioctl(struct inode *i, struct fd *f, int cmd, unsigned int arg) { struct ide *ide; struct device *d; int errno; if(!(ide = get_ide_controller(i->rdev))) { return -EINVAL; } switch(cmd) { case BLKSSZGET: if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int)))) { return errno; } *(int *)arg = 2048; break; case BLKBSZGET: if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int)))) { return errno; } d = ide->device; *(int *)arg = ((unsigned int *)d->blksize)[MINOR(i->rdev)]; break; default: return -EINVAL; break; } return 0; } __loff_t atapi_cd_llseek(struct inode *i, __loff_t offset) { return offset; } int atapi_cd_init(struct ide *ide, struct ata_drv *drive) { struct device *d; unsigned char minor; drive->fsop = &atapi_cd_driver_fsop; minor = !drive->minor_shift ? 0 : 1 << drive->minor_shift; if(!(d = get_device(BLK_DEV, MKDEV(drive->major, minor)))) { return -EINVAL; } SET_MINOR(d->minors, minor); ((unsigned int *)d->blksize)[minor] = BLKSIZE_2K; /* default transfer mode */ drive->read_fn = atapi_pio_read; drive->write_fn = NULL; drive->read_end_fn = atapi_pio_read_end; drive->write_end_fn = NULL; return 0; } ================================================ FILE: drivers/block/blk_queue.c ================================================ /* * fiwix/drivers/block/blk_queue.c * * Copyright 2024, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include #include #include /* append the request into the queue */ void add_blk_request(struct blk_request *br) { unsigned int flags; struct blk_request *h; struct device *d; d = br->device; SAVE_FLAGS(flags); CLI(); if((h = (struct blk_request *)d->requests_queue)) { while(h->next) { h = h->next; } h->next = br; } else { d->requests_queue = (void *)br; } RESTORE_FLAGS(flags); } int do_blk_request(struct device *d, void *fn, struct buffer *buf) { struct blk_request *br; int errno; if(!(br = (struct blk_request *)kmalloc(sizeof(struct blk_request)))) { printk("WARNING: %s(): no more free memory for block requests.\n", __FUNCTION__); return -ENOMEM; } memset_b(br, 0, sizeof(struct blk_request)); br->dev = buf->dev; br->block = buf->block; br->size = buf->size; br->buffer = buf; br->device = d; br->fn = fn; add_blk_request(br); run_blk_request(d); if(br->status != BR_COMPLETED) { sleep(br, PROC_UNINTERRUPTIBLE); } errno = br->errno; if(!br->head_group) { kfree((unsigned int)br); } return errno; } void run_blk_request(struct device *d) { unsigned int flags; struct blk_request *br, *brh; int errno; SAVE_FLAGS(flags); CLI(); br = (struct blk_request *)d->requests_queue; while(br) { if(br->status) { if(br->status == BR_COMPLETED) { printk("%s(): status marked as BR_COMPLETED, picking the next one ...\n", __FUNCTION__); d->requests_queue = (void *)br->next; br = br->next; continue; } return; } br->status = BR_PROCESSING; if(!(errno = br->fn(br->dev, br->block, br->buffer->data, br->size))) { return; } br->errno = errno; d->requests_queue = (void *)br->next; br->status = BR_COMPLETED; if(br->head_group) { brh = br->head_group; brh->left--; brh->errno = errno; if(!brh->left) { wakeup(brh); } } else { wakeup(br); } br = br->next; } RESTORE_FLAGS(flags); } ================================================ FILE: drivers/block/dma.c ================================================ /* * fiwix/drivers/block/dma.c * * Copyright 2018-2022, 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 }; char *dma_resources[DMA_CHANNELS]; 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, 0, sizeof(dma_resources)); } ================================================ FILE: drivers/block/floppy.c ================================================ /* * fiwix/drivers/block/floppy.c * * Copyright 2018-2022, 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 #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 = { 0, 0 }; 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\"" },*/ }; /* buffer area used for I/O operations (1KB) */ char fdc_transfer_area[BPS * 2]; 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 struct fs_operations fdc_driver_fsop = { 0, 0, fdc_open, fdc_close, NULL, /* read */ NULL, /* write */ fdc_ioctl, fdc_llseek, NULL, /* readdir */ NULL, /* readdir64 */ 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", FDC_MAJOR, { 0, 0, 0, 0, 0, 0, 0, 0 }, NULL, NULL, &fdc_driver_fsop, NULL, NULL, NULL }; static struct interrupt irq_config_floppy = { 0, "floppy", &irq_floppy, NULL }; 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); /* avoid sleep if interrupt already happened */ if(fdc_wait_interrupt) { 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); /* avoid sleep if interrupt already happened */ if(fdc_wait_interrupt) { 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); /* avoid sleep if interrupt already happened */ if(fdc_wait_interrupt) { 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]; } } static int setup_transfer(int mode, __dev_t dev, __blk_t block, char *buffer, int blksize) { unsigned char minor; unsigned int sectors_io; int cyl, head, sector; int retries; struct callout_req creq; struct device *d; char *op; minor = MINOR(dev); if(!TEST_MINOR(floppy_device.minors, minor)) { return -ENXIO; } if(!blksize) { if(!(d = get_device(BLK_DEV, dev))) { return -EINVAL; } blksize = ((unsigned int *)d->blksize)[MINOR(dev)]; } blksize = blksize ? blksize : BLKSIZE_1K; lock_resource(&floppy_resource); set_current_fdd_type(minor); if(mode == BLK_READ) { op = "fdc_read"; } else { op = "fdc_write"; } if(fdc_block2chs(block, blksize, &cyl, &head, §or)) { printk("WARNING: %s(): fd%d: invalid block number %d on %s device %d,%d.\n", op, 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", op, 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.\n", op, current_fdd, floppy_device.name, MAJOR(dev), MINOR(dev)); continue; } if(mode == BLK_READ) { start_dma(FLOPPY_DMA, fdc_transfer_area, blksize, DMA_MODE_WRITE | DMA_MODE_SINGLE); fdc_wait_interrupt = FDC_READ; fdc_out(FDC_READ); } else { start_dma(FLOPPY_DMA, fdc_transfer_area, blksize, DMA_MODE_READ | DMA_MODE_SINGLE); memcpy_b((void *)fdc_transfer_area, buffer, blksize); 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.\n", op, current_fdd, floppy_device.name, MAJOR(dev), MINOR(dev)); continue; } creq.fn = fdc_timer; creq.arg = FDC_TR_DEFAULT; add_callout(&creq, WAIT_FDC); /* avoid sleep if interrupt already happened */ if(fdc_wait_interrupt) { sleep(&irq_floppy, PROC_UNINTERRUPTIBLE); } if(fdc_timeout) { need_reset = 1; printk("WARNING: %s(): fd%d: timeout on %s device %d,%d.\n", op, current_fdd, floppy_device.name, MAJOR(dev), MINOR(dev)); continue; } del_callout(&creq); fdc_get_results(); if(mode == BLK_WRITE) { 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,\n", op, 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_io = (fdc_results[ST_CYL] - cyl) * (current_fdd_type->heads * current_fdd_type->spt); sectors_io += (fdc_results[ST_HEAD] - head) * current_fdd_type->spt; sectors_io += fdc_results[ST_SECTOR] - sector; if(sectors_io * BPS != blksize) { printk("WARNING: %s(): fd%d: error on %s device %d,%d (%d sectors I/O),\n", op, current_fdd, floppy_device.name, MAJOR(dev), MINOR(dev), sectors_io); printk("\tblock=%d, sector=%d, cylinder/head=%d/%d\n", block, sector, cyl, head); unlock_resource(&floppy_resource); fdc_motor_off(); return -EIO; } if(mode == BLK_READ) { memcpy_b(buffer, (void *)fdc_transfer_area, blksize); } unlock_resource(&floppy_resource); return sectors_io * BPS; } void irq_floppy(int num, struct sigcontext *sc) { if(!fdc_wait_interrupt) { printk("WARNING: %s(): fd%d: unexpected interrupt!\n", __FUNCTION__, current_fdd); 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 *f) { 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 *f) { unsigned char minor; minor = MINOR(i->rdev); if(!TEST_MINOR(floppy_device.minors, minor)) { return -ENXIO; } sync_buffers(i->rdev); 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) { return setup_transfer(BLK_READ, dev, block, buffer, blksize); } int fdc_write(__dev_t dev, __blk_t block, char *buffer, int blksize) { return setup_transfer(BLK_WRITE, dev, block, buffer, blksize); } int fdc_ioctl(struct inode *i, struct fd *f, int cmd, unsigned int arg) { unsigned char minor; struct hd_geometry *geom; struct device *d; int errno, size; minor = MINOR(i->rdev); if(!TEST_MINOR(floppy_device.minors, minor)) { return -ENXIO; } if(!(d = get_device(BLK_DEV, i->rdev))) { return -EINVAL; } size = ((unsigned int *)d->device_data)[MINOR(i->rdev)]; 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 = size * 2; break; case BLKSSZGET: if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int)))) { return errno; } *(int *)arg = 512; break; case BLKBSZGET: if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int)))) { return errno; } *(int *)arg = ((unsigned int *)d->blksize)[MINOR(i->rdev)]; break; case BLKFLSBUF: sync_buffers(i->rdev); invalidate_buffers(i->rdev); break; default: return -EINVAL; } return 0; } __loff_t fdc_llseek(struct inode *i, __loff_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; int n; 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; /* * Some BIOS may return the value 0x05 (for 2.88MB floppy type) which is * not supported by Fiwix. This prevents from using an unexistent type * in the fdd_type structure if this happens. */ if(master > 4) { master = 4; } floppy_device.blksize = (unsigned int *)kmalloc(MAX_MINORS * sizeof(unsigned int)); floppy_device.device_data = (unsigned int *)kmalloc(MAX_MINORS * sizeof(unsigned int)); memset_l(floppy_device.blksize, 0, MAX_MINORS); memset_l(floppy_device.device_data, 0, MAX_MINORS); if(master) { if(!register_irq(FLOPPY_IRQ, &irq_config_floppy)) { enable_irq(FLOPPY_IRQ); } printk("fd0 0x%04x-0x%04x %d\t", FDC_SRA, FDC_CCR, FLOPPY_IRQ); printk("%s ", fdd_type[master].name); fdd_status[0].type = fdd_status[1].type = master; for(n = 0; n < 18; n += 4) { SET_MINOR(floppy_device.minors, n); ((unsigned int *)floppy_device.blksize)[n] = BLKSIZE_1K; } ((unsigned int *)floppy_device.device_data)[0] = fdd_type[master].sizekb; ((unsigned int *)floppy_device.device_data)[4] = fdd_type[1].sizekb; ((unsigned int *)floppy_device.device_data)[8] = fdd_type[2].sizekb; ((unsigned int *)floppy_device.device_data)[12] = fdd_type[3].sizekb; ((unsigned int *)floppy_device.device_data)[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, &irq_config_floppy)) { enable_irq(FLOPPY_IRQ); } } printk("fd1 0x%04x-0x%04x %d\t", FDC_SRA, FDC_CCR, FLOPPY_IRQ); printk("%s ", fdd_type[slave].name); fdd_status[1].type = slave; for(n = 1; n < 18; n += 4) { SET_MINOR(floppy_device.minors, n); ((unsigned int *)floppy_device.blksize)[n] = BLKSIZE_1K; } ((unsigned int *)floppy_device.device_data)[1] = fdd_type[master].sizekb; ((unsigned int *)floppy_device.device_data)[5] = fdd_type[1].sizekb; ((unsigned int *)floppy_device.device_data)[9] = fdd_type[2].sizekb; ((unsigned int *)floppy_device.device_data)[13] = fdd_type[3].sizekb; ((unsigned int *)floppy_device.device_data)[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); } } } } ================================================ FILE: drivers/block/part.c ================================================ /* * 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) { struct buffer *buf; struct device *d; int blksize; if(!(d = get_device(BLK_DEV, dev))) { return -ENXIO; } blksize = ((unsigned int *)d->blksize)[MINOR(dev)]; if(!(buf = bread(dev, PARTITION_BLOCK, blksize))) { printk("WARNING: %s(): unable to read partition block in device %d,%d.\n", __FUNCTION__, MAJOR(dev), MINOR(dev)); return -EIO; } memcpy_b(part, (void *)(buf->data + MBR_CODE_SIZE), sizeof(struct partition) * NR_PARTITIONS); brelse(buf); return 0; } ================================================ FILE: drivers/block/ramdisk.c ================================================ /* * fiwix/drivers/block/ramdisk.c * * Copyright 2018-2022, 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 ramdisk_minors; struct ramdisk ramdisk_table[RAMDISK_TOTAL]; static struct fs_operations ramdisk_driver_fsop = { 0, 0, ramdisk_open, ramdisk_close, NULL, /* read */ NULL, /* write */ ramdisk_ioctl, ramdisk_llseek, NULL, /* readdir */ NULL, /* readdir64 */ 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", RAMDISK_MAJOR, { 0, 0, 0, 0, 0, 0, 0, 0 }, NULL, NULL, &ramdisk_driver_fsop, NULL, NULL, NULL }; 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 *f) { if(!get_ramdisk(MINOR(i->rdev))) { return -ENXIO; } return 0; } int ramdisk_close(struct inode *i, struct fd *f) { if(!get_ramdisk(MINOR(i->rdev))) { return -ENXIO; } sync_buffers(i->rdev); return 0; } int ramdisk_read(__dev_t dev, __blk_t block, char *buffer, int blksize) { int size; __off_t offset; struct ramdisk *ramdisk; struct device *d; if(!(ramdisk = get_ramdisk(MINOR(dev)))) { return -ENXIO; } if(!(d = get_device(BLK_DEV, dev))) { return -EINVAL; } size = ((unsigned int *)d->device_data)[MINOR(dev)] * 1024; offset = block * blksize; if(offset > size) { printk("%s(): block %d is beyond the size of the ramdisk.\n", __FUNCTION__, block); return -EIO; } 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; struct device *d; if(!(ramdisk = get_ramdisk(MINOR(dev)))) { return -ENXIO; } if(!(d = get_device(BLK_DEV, dev))) { return -EINVAL; } size = ((unsigned int *)d->device_data)[MINOR(dev)] * 1024; offset = block * blksize; if(offset > size) { printk("%s(): block %d is beyond the size of the ramdisk.\n", __FUNCTION__, block); return -EIO; } blksize = MIN(blksize, size - offset); memcpy_b(ramdisk->addr + offset, buffer, blksize); return blksize; } int ramdisk_ioctl(struct inode *i, struct fd *f, int cmd, unsigned int arg) { struct hd_geometry *geom; struct device *d; int errno, size; if(!get_ramdisk(MINOR(i->rdev))) { return -ENXIO; } if(!(d = get_device(BLK_DEV, i->rdev))) { return -EINVAL; } size = ((unsigned int *)d->device_data)[MINOR(i->rdev)]; 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 = size * 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 = size * 2; break; default: return -EINVAL; } return 0; } __loff_t ramdisk_llseek(struct inode *i, __loff_t offset) { return offset; } void ramdisk_init(void) { int n; struct ramdisk *ramdisk; if(ramdisk_minors) { ramdisk_device.blksize = (unsigned int *)kmalloc(MAX_MINORS * sizeof(unsigned int)); ramdisk_device.device_data = (unsigned int *)kmalloc(MAX_MINORS * sizeof(unsigned int)); memset_l(ramdisk_device.blksize, 0, MAX_MINORS); memset_l(ramdisk_device.device_data, 0, MAX_MINORS); for(n = 0; n < ramdisk_minors; n++) { SET_MINOR(ramdisk_device.minors, n); ramdisk = get_ramdisk(n); ((unsigned int *)ramdisk_device.blksize)[n] = BLKSIZE_1K; ((unsigned int *)ramdisk_device.device_data)[n] = ramdisk->size; printk("ram%d 0x%08x-0x%08x RAMdisk of %dKB size, %dKB blocksize\n", n, ramdisk->addr, ramdisk->addr + (ramdisk->size * 1024), ramdisk->size, BLKSIZE_1K / 1024); } register_device(BLK_DEV, &ramdisk_device); } } ================================================ FILE: drivers/char/Makefile ================================================ # fiwix/drivers/char/Makefile # # Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. # Distributed under the terms of the Fiwix License. # .c.o: $(CC) $(CFLAGS) -c -o $@ $< OBJS = console.o tty.o charq.o vt.o ps2.o defkeymap.o keyboard.o psaux.o \ memdev.o serial.o lp.o fb.o sysrq.o pty.o all: $(OBJS) clean: rm -f *.o ================================================ FILE: drivers/char/charq.c ================================================ /* * fiwix/drivers/char/charq.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include /* * charq.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) */ #define CB_POOL_SIZE 64 /* number of cblocks in the central pool */ 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; } 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 charq_putchar(struct clist *q, unsigned char ch) { unsigned 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)) { charq_putchar(q, ch); errno = 0; } else { errno = -EAGAIN; } RESTORE_FLAGS(flags); return errno; } int charq_unputchar(struct clist *q) { unsigned 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 charq_getchar(struct clist *q) { unsigned 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 charq_flush(struct clist *q) { unsigned int flags; SAVE_FLAGS(flags); CLI(); while(q->head != NULL) { delete_cblock_from_head(q); } RESTORE_FLAGS(flags); } int charq_room(struct clist *q) { return (NR_CB_QUEUE * CBSIZE) - q->count; } void charq_init(void) { int n; struct cblock *cb; memset_b(cblock_pool, 0, 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); } } ================================================ FILE: drivers/char/console.c ================================================ /* * fiwix/drivers/char/console.c * * Copyright 2018-2022, 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 #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 */ /* 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" #define SCREEN_SIZE (video.columns * video.lines) #define VC_BUF_LINES (video.lines * SCREENS_LOG) short int current_cons; short int *vc_screen[NR_VCONSOLES + 1]; short int *vcbuf; struct video_parms video; struct vconsole vc[NR_VCONSOLES + 1]; static struct fs_operations tty_driver_fsop = { 0, 0, tty_open, tty_close, tty_read, tty_write, tty_ioctl, tty_llseek, NULL, /* readdir */ NULL, /* readdir64 */ 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", VCONSOLES_MAJOR, { 0, 0, 0, 0, 0, 0, 0, 0 }, NULL, NULL, &tty_driver_fsop, NULL, NULL, NULL }; static struct device console_device = { "console", SYSCON_MAJOR, { 0, 0, 0, 0, 0, 0, 0, 0 }, NULL, NULL, &tty_driver_fsop, NULL, NULL, NULL }; unsigned short int ansi_color_table[] = { COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BROWN, COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE }; static int is_vconsole(__dev_t dev) { if(MAJOR(dev) == VCONSOLES_MAJOR && MINOR(dev) <= NR_VCONSOLES) { return 1; } return 0; } 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 cr(struct vconsole *vc) { vc->x = 0; } static void lf(struct vconsole *vc) { if(vc->y == vc->lines) { video.scroll_screen(vc, 0, SCROLL_UP); } else { vc->y++; } } static void ri(struct vconsole *vc) { video.scroll_screen(vc, 0, SCROLL_DOWN); } 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 = vc->columns - vc->x; video.write_screen(vc, from, count, vc->color_attr); from = ((vc->y + 1) * vc->columns); count = SCREEN_SIZE - from; break; case CSI_J_STA2CUR: /* Erase Up [1J */ from = vc->y * vc->columns; count = vc->x + 1; video.write_screen(vc, from, count, vc->color_attr); from = 0; count = vc->y * vc->columns; break; case CSI_J_SCREEN: /* Erase Screen [2J */ from = 0; count = SCREEN_SIZE; break; default: return; } video.write_screen(vc, from, count, vc->color_attr); } 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; break; case CSI_K_STA2CUR: /* Erase Start of Line [1K */ from = vc->y * vc->columns; count = vc->x + 1; break; case CSI_K_LINE: /* Erase Line [2K */ from = vc->y * vc->columns; count = vc->columns; break; default: return; } video.write_screen(vc, from, count, vc->color_attr); } static void csi_X(struct vconsole *vc, int count) { int from; from = (vc->y * vc->columns) + vc->x; count = count > (vc->columns - vc->x) ? vc->columns - vc->x : count; video.write_screen(vc, from, count, vc->color_attr); } static void csi_L(struct vconsole *vc, int count) { if(count > (vc->lines - vc->top)) { count = vc->lines - vc->top; } while(count--) { video.scroll_screen(vc, vc->y, SCROLL_DOWN); } } static void csi_M(struct vconsole *vc, int count) { if(count > (vc->lines - vc->top)) { count = vc->lines - vc->top; } while(count--) { video.scroll_screen(vc, vc->y, SCROLL_UP); } } static void csi_P(struct vconsole *vc, int count) { if(count > vc->columns) { count = vc->columns; } while(count--) { video.delete_char(vc); } } static void csi_at(struct vconsole *vc, int count) { if(count > vc->columns) { count = vc->columns; } while(count--) { video.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; } /* Select Graphic Rendition */ static void csi_m(struct vconsole *vc) { int n; if(vc->reverse) { vc->color_attr = ((vc->color_attr & 0x7000) >> 4) | ((vc->color_attr & 0x0700) << 4) | (vc->color_attr & 0x8800); } for(n = 0; n < vc->nparms; n++) { switch(vc->parms[n]) { case SGR_DEFAULT: default_color_attr(vc); break; case SGR_BOLD: vc->bold = 1; break; case SGR_BLINK: vc->blink = 1; break; case SGR_REVERSE: vc->reverse = 1; break; /* normal intensity */ case 21: case 22: vc->bold = 0; break; case SGR_BLINK_OFF: vc->blink = 0; break; case SGR_REVERSE_OFF: vc->reverse = 0; break; case SGR_BLACK_FG: case SGR_RED_FG: case SGR_GREEN_FG: case SGR_BROWN_FG: case SGR_BLUE_FG: case SGR_MAGENTA_FG: case SGR_CYAN_FG: case SGR_WHITE_FG: vc->color_attr = (vc->color_attr & 0xF8FF) | (ansi_color_table[vc->parms[n] - 30]); break; case SGR_DEFAULT_FG_U_ON: case SGR_DEFAULT_FG_U_OFF: /* not supported yet */ break; case SGR_BLACK_BG: case SGR_RED_BG: case SGR_GREEN_BG: case SGR_BROWN_BG: case SGR_BLUE_BG: case SGR_MAGENTA_BG: case SGR_CYAN_BG: case SGR_WHITE_BG: vc->color_attr = (vc->color_attr & 0x8FFF) | ((ansi_color_table[vc->parms[n] - 40]) << 4); break; case SGR_DEFAULT_BG: /* not supported yet */ break; } } if(vc->bold) { vc->color_attr |= 0x0800; } else { vc->color_attr &= ~0x0800; } if(vc->blink) { vc->color_attr |= 0x8000; } else { 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--) { charq_putchar(&tty->read_q, *(buf++)); } tty->input(tty); } static void vcbuf_scroll_up(void) { memcpy_w(vcbuf, vcbuf + video.columns, (VC_BUF_SIZE - video.columns) * 2); } static void vcbuf_refresh(struct vconsole *vc) { short int *screen; screen = (short int *)vc->screen; memset_w(vcbuf, BLANK_MEM, VC_BUF_SIZE); memcpy_w(vcbuf, screen, SCREEN_SIZE); } static void echo_char(struct vconsole *vc, unsigned char *buf, unsigned int count) { unsigned char ch; unsigned int flags; SAVE_FLAGS(flags); CLI(); if(vc->flags & CONSOLE_HAS_FOCUS) { if(video.buf_top) { video.restore_screen(vc); video.show_cursor(vc, ON); video.buf_top = 0; } } while(count--) { ch = *buf++; if(ch == '\0') { 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->flags & CONSOLE_HAS_FOCUS) { video.buf_y++; } } else if(ch == '\t') { while(vc->x < (vc->columns - 1)) { if(vc->tty->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->flags & CONSOLE_HAS_FOCUS) { video.buf_y++; } } if(vc->y >= vc->lines) { video.scroll_screen(vc, 0, SCROLL_UP); vc->y--; } video.put_char(vc, ch); if(vc->x < vc->columns - 1) { vc->check_x = 0; vc->x++; } else { vc->check_x = 1; } } if(vc->y >= vc->lines) { video.scroll_screen(vc, 0, SCROLL_UP); vc->y--; } if(vc->flags & CONSOLE_HAS_FOCUS) { if(video.buf_y >= VC_BUF_LINES) { vcbuf_scroll_up(); video.buf_y--; } } } video.update_curpos(vc); RESTORE_FLAGS(flags); } void vconsole_reset(struct tty *tty) { int n; struct vconsole *vc; vc = (struct vconsole *)tty->driver_data; vc->top = 0; vc->lines = video.lines; vc->columns = video.columns; 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; vc->nparms = 0; memset_b(vc->parms, 0, sizeof(vc->parms)); default_color_attr(vc); vc->saved_x = vc->saved_y = 0; for(n = 0; n < MAX_TAB_COLS; n++) { if(!(n % TAB_SIZE)) { vc->tty->tab_stop[n] = 1; } else { vc->tty->tab_stop[n] = 0; } } tty_reset(tty); vc->tty->winsize.ws_row = vc->lines - vc->top; vc->tty->winsize.ws_col = vc->columns; init_vt(vc); vc->flags &= ~CONSOLE_BLANKED; video.update_curpos(vc); } /* https://vt100.net/docs/vt100-ug/chapter3.html */ void vconsole_write(struct tty *tty) { int n; unsigned char ch; int numeric; struct vconsole *vc; vc = (struct vconsole *)tty->driver_data; if(vc->flags & CONSOLE_HAS_FOCUS) { if(video.buf_top) { video.restore_screen(vc); video.buf_top = 0; video.show_cursor(vc, ON); video.update_curpos(vc); } } ch = numeric = 0; while(!vc->scrlock && tty->write_q.count > 0) { ch = charq_getchar(&tty->write_q); if(vc->esc) { if(vc->sbracket) { if(ISDIGIT(ch)) { numeric = 1; if(vc->semicolon) { vc->parmv2 *= 10; vc->parmv2 += ch - '0'; } else { vc->parmv1 *= 10; vc->parmv1 += ch - '0'; } vc->parms[vc->nparms] *= 10; vc->parms[vc->nparms] += ch - '0'; continue; } switch(ch) { case ';': vc->semicolon = 1; vc->parmv2 = 0; vc->nparms++; continue; case '?': vc->question = 1; continue; case '@': /* Insert Character(s) [ n @ */ vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; csi_at(vc, vc->parmv1); CSE; continue; case 'A': /* Cursor Up [ n A */ vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; adjust(vc, vc->x, vc->y - vc->parmv1); CSE; continue; case 'B': /* Cursor Down [ n B */ vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; adjust(vc, vc->x, vc->y + vc->parmv1); CSE; continue; case 'C': /* Cursor Forward [ n C */ vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; adjust(vc, vc->x + vc->parmv1, vc->y); CSE; continue; case 'D': /* Cursor Backward [ n D */ vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; adjust(vc, vc->x - vc->parmv1, vc->y); CSE; continue; case 'E': /* Cursor Next Line(s) [ n E */ vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; adjust(vc, 0, vc->y + vc->parmv1); CSE; continue; case 'F': /* Cursor Previous Line(s) [ n F */ vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; adjust(vc, 0, vc->y - vc->parmv1); CSE; continue; case 'G': /* Cursor Horizontal Position [ n 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': /* Horizontal Vertical 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 'I': /* Cursor Forward Tabulation [ n I */ vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; while(vc->parmv1--){ while(vc->x < (vc->columns - 1)) { if(vc->tty->tab_stop[++vc->x]) { break; } } } adjust(vc, vc->x, vc->y); 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) [ n L */ vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; csi_L(vc, vc->parmv1); CSE; continue; case 'M': /* Delete Line(s) [ n M */ vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; csi_M(vc, vc->parmv1); CSE; continue; case 'P': /* Delete Character(s) [ n P */ vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; csi_P(vc, vc->parmv1); CSE; continue; case 'S': /* Scroll Up [ n S */ vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; while(vc->parmv1--) { video.scroll_screen(vc, 0, SCROLL_UP); } CSE; continue; case 'T': /* Scroll Down [ n T */ vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; while(vc->parmv1--) { video.scroll_screen(vc, 0, SCROLL_DOWN); } CSE; continue; case 'X': /* Erase Character(s) [ n X */ vc->parmv1 = !vc->parmv1 ? 1 : vc->parmv1; csi_X(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 [ n 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->tty->tab_stop[vc->x] = 0; break; case 3: /* Clear All Tabs [3g */ case 5: /* Clear All Tabs [5g */ for(n = 0; n < MAX_TAB_COLS; n++) vc->tty->tab_stop[n] = 0; break; } CSE; continue; case 'h': if(vc->question) { switch(vc->parmv1) { /* DEC modes */ case 25: /* Switch Cursor Visible [?25h */ video.show_cursor(vc, ON); break; } } CSE; continue; case 'l': if(vc->question) { switch(vc->parmv1) { /* DEC modes */ case 25: /* Switch Cursor Invisible [?25l */ video.show_cursor(vc, OFF); break; } } CSE; continue; case 'm': /* Select Graphic Rendition n ... m */ vc->nparms++; 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': /* Top and Bottom Margins [r / [{start};{end}r */ if(!vc->parmv1) { vc->parmv1++; } if(!vc->parmv2) { vc->parmv2 = video.lines; } if(vc->parmv1 < vc->parmv2 && vc->parmv2 <= video.lines) { vc->top = vc->parmv1 - 1; vc->lines = vc->parmv2; 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; vc->nparms = 0; memset_b(vc->parms, 0, sizeof(vc->parms)); 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 Up D */ lf(vc); CSE; continue; case 'E': /* Move To Next Line E */ cr(vc); lf(vc); CSE; continue; case 'H': /* Set Tab H */ vc->tty->tab_stop[vc->x] = 1; CSE; continue; case 'M': /* Scroll Down 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(ch) { if(vc->vc_mode != KD_GRAPHICS) { video.update_curpos(vc); } wakeup(&tty->write_q); } } void vconsole_select(int new_cons) { new_cons++; if(new_cons > NR_VCONSOLES) { return; } 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.relsig, KERNEL)) { 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, KERNEL)) { init_vt(&vc[new_cons]); } } if(video.buf_top) { video.buf_top = 0; video.show_cursor(&vc[current_cons], ON); video.update_curpos(&vc[current_cons]); } vc[current_cons].vidmem = NULL; vc[current_cons].flags &= ~CONSOLE_HAS_FOCUS; vc[new_cons].vidmem = (unsigned char *)video.address; vc[new_cons].flags |= CONSOLE_HAS_FOCUS; current_cons = new_cons; video.update_curpos(&vc[current_cons]); video.restore_screen(&vc[current_cons]); set_leds(vc[current_cons].led_status); video.buf_y = vc[current_cons].y; video.buf_top = 0; vcbuf_refresh(&vc[current_cons]); video.show_cursor(&vc[current_cons], COND); video.cursor_blink((unsigned int)&vc[current_cons]); } } void unblank_screen(struct vconsole *vc) { if(!(vc->flags & CONSOLE_BLANKED)) { return; } video.restore_screen(vc); vc->flags &= ~CONSOLE_BLANKED; video.show_cursor(vc, ON); } 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) { 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->tty->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--) { charq_putchar(&tty->write_q, '\b'); } } void console_init(void) { int syscon, n; struct tty *tty; if(video.flags & VPF_VGA) { printk("console 0x%04x-0x%04x -\t%s\n", video.port, video.port + 1, video.signature); } if(video.flags & VPF_VESAFB) { printk("console -\tcolor framebuffer, screen=%dx%d, font=%dx%d\n", video.columns, video.lines, video.fb_char_width, video.fb_char_height); } for(n = 1; n <= NR_VCONSOLES; n++) { if((tty = register_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; if(video.flags & VPF_VGA) { vc[n].screen = (short int *)kmalloc(PAGE_SIZE); } if(video.flags & VPF_VESAFB) { vc[n].screen = vc_screen[n]; } vc[n].vidmem = NULL; memset_w(vc[n].screen, BLANK_MEM, SCREEN_SIZE); vconsole_reset(tty); } } printk("\t\t\t\t%d virtual consoles\n", NR_VCONSOLES); #ifdef CONFIG_QEMU_DEBUGCON if(kstat.flags & KF_HAS_DEBUGCON) { printk("\t\t\t\tQEMU Bochs-style debug console emulation enabled\n"); } #endif /* CONFIG_QEMU_DEBUGCON */ current_cons = 1; video.show_cursor(&vc[current_cons], ON); vc[current_cons].vidmem = (unsigned char *)video.address; vc[current_cons].flags |= CONSOLE_HAS_FOCUS; if(video.flags & VPF_VGA) { memcpy_w(vc[current_cons].screen, video.address, SCREEN_SIZE); } video.get_curpos(&vc[current_cons]); video.update_curpos(&vc[current_cons]); video.buf_y = vc[current_cons].y; video.buf_top = 0; SET_MINOR(console_device.minors, 0); SET_MINOR(console_device.minors, 1); for(n = 0; n <= NR_VCONSOLES; n++) { SET_MINOR(tty_device.minors, n); } register_device(CHR_DEV, &console_device); register_device(CHR_DEV, &tty_device); /* check if a vconsole will act as a system console */ for(n = 0, syscon = 0; n < NR_SYSCONSOLES; n++) { if(is_vconsole(sysconsole_table[n].dev)) { if((tty = get_tty(sysconsole_table[n].dev))) { if(!syscon) { syscon = tty->dev; } register_console(tty); } } } if(syscon) { /* flush early log into the first console */ tty = get_tty(syscon); flush_log_buf(tty); } } ================================================ FILE: drivers/char/defkeymap.c ================================================ /* * fiwix/drivers/char/defkeymap.c * * Copyright 2018-2022, 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 - 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 01 - ESC */ C('['), C('['), 0, 0, 0, 0, 0, 0, A('['), 0, 0, 0, 0, 0, 0, 0, /* 02 - 1 */ '1', '!', 0, 0, 0, 0, 0, 0, A('1'), 0, 0, 0, 0, 0, 0, 0, /* 03 - 2 */ '2', '@', '@', 0, 0, 0, 0, 0, A('2'), 0, 0, 0, 0, 0, 0, 0, /* 04 - 3 */ '3', '#', 0, 0, C('['), 0, 0, 0, A('3'), 0, 0, 0, 0, 0, 0, 0, /* 05 - 4 */ '4', '$', '$', 0, C('\\'),0, 0, 0, A('4'), 0, 0, 0, 0, 0, 0, 0, /* 06 - 5 */ '5', '%', 0, 0, C(']'), 0, 0, 0, A('5'), 0, 0, 0, 0, 0, 0, 0, /* 07 - 6 */ '6', '^', 0, 0, C('^'), 0, 0, 0, A('6'), 0, 0, 0, 0, 0, 0, 0, /* 08 - 7 */ '7', '&', '{', 0, C('_'), 0, 0, 0, A('7'), 0, 0, 0, 0, 0, 0, 0, /* 09 - 8 */ '8', '*', '[', 0, BS, 0, 0, 0, A('8'), 0, 0, 0, 0, 0, 0, 0, /* 10 - 9 */ '9', '(', ']', 0, 0, 0, 0, 0, A('9'), 0, 0, 0, 0, 0, 0, 0, /* 11 - 0 */ '0', ')', '}', 0, 0, 0, 0, 0, A('0'), 0, 0, 0, 0, 0, 0, 0, /* 12 - -_ */ '-', '_', '\\', 0, C('_'), 0, 0, 0, A('-'), 0, 0, 0, 0, 0, 0, 0, /* 13 - =+ */ '=', '+', 0, 0, 0, 0, 0, 0, A('='), 0, 0, 0, 0, 0, 0, 0, /* 14 - BS */ BS, BS, 0, 0, 0, 0, 0, 0, A(BS), 0, 0, 0, 0, 0, 0, 0, /* 15 - TAB */ '\t', '\t', 0, 0, 0, 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 - [{ */ '[', '{', 0, 0, C('['), 0, 0, 0, A('['), 0, 0, 0, 0, 0, 0, 0, /* 27 - ]} */ ']', '}', '~', 0, 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 - LCTRL */ LCTRL, LCTRL, LCTRL, LCTRL, LCTRL, 0, 0, 0, LCTRL, 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 - ;: */ ';', ':', 0, 0, 0, 0, 0, 0, A(';'), 0, 0, 0, 0, 0, 0, 0, /* 40 - '" */ '\'', '"', 0, 0, C('G'), 0, 0, 0, A('\''),0, 0, 0, 0, 0, 0, 0, /* 41 - `~ */ '`', '~', 0, 0, 0, 0, 0, 0, A('`'), 0, 0, 0, 0, 0, 0, 0, /* 42 - LSHF */ LSHIFT, LSHIFT, LSHIFT, LSHIFT, LSHIFT, 0, 0, 0, LSHIFT, 0, 0, 0, 0, 0, 0, 0, /* 43 - \| */ '\\', '|', 0, 0, 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 - ,< */ ',', '<', 0, 0, 0, 0, 0, 0, A(','), 0, 0, 0, 0, 0, 0, 0, /* 52 - .> */ '.', '>', 0, 0, 0, 0, 0, 0, A('.'), 0, 0, 0, 0, 0, 0, 0, /* 53 - /? */ SLASH, '?', 0, 0, BS, 0, 0, 0, A('/'), 0, 0, 0, 0, 0, 0, 0, /* 54 - RSHF */ RSHIFT, RSHIFT, RSHIFT, RSHIFT, RSHIFT, 0, 0, 0, RSHIFT, 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 */ ' ', ' ', 0, 0, 0, 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, 0, 0, F1, 0, 0, 0, AF1, 0, 0, 0, 0, 0, 0, 0, /* 60 - F2 */ F2, SF2, 0, 0, F2, 0, 0, 0, AF2, 0, 0, 0, 0, 0, 0, 0, /* 61 - F3 */ F3, SF3, 0, 0, F3, 0, 0, 0, AF3, 0, 0, 0, 0, 0, 0, 0, /* 62 - F4 */ F4, SF4, 0, 0, F4, 0, 0, 0, AF4, 0, 0, 0, 0, 0, 0, 0, /* 63 - F5 */ F5, SF5, 0, 0, F5, 0, 0, 0, AF5, 0, 0, 0, 0, 0, 0, 0, /* 64 - F6 */ F6, SF6, 0, 0, F6, 0, 0, 0, AF6, 0, 0, 0, 0, 0, 0, 0, /* 65 - F7 */ F7, SF7, 0, 0, F7, 0, 0, 0, AF7, 0, 0, 0, 0, 0, 0, 0, /* 66 - F8 */ F8, SF8, 0, 0, F8, 0, 0, 0, AF8, 0, 0, 0, 0, 0, 0, 0, /* 67 - F9 */ F9, SF9, 0, 0, F9, 0, 0, 0, AF9, 0, 0, 0, 0, 0, 0, 0, /* 68 - F10 */ F10, SF10, 0, 0, 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, 0, SCRL4, 0, 0, 0, SCRL, 0, 0, 0, 0, 0, 0, 0, /* 71 - HOME/7 */ HOME, HOME, HOME, HOME, HOME, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 72 - UP /8 */ UP, UP, UP, UP, UP, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 73 - PGUP/9 */ PGUP, PGUP, PGUP, PGUP, PGUP, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, /* 76 - MID /5 */ MID, MID, MID, MID, MID, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 77 - RIGH/6 */ RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - DOWN/2 */ DOWN, DOWN, DOWN, DOWN, DOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 81 - PGDN/3 */ PGDN, PGDN, PGDN, PGDN, PGDN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 82 - INS /0 */ INS, INS, INS, INS, INS, 0, 0, 0, 0, 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 - SYSRQ */ SYSRQ, 0, 0, 0, 0, 0, 0, 0, SYSRQ, 0, 0, 0, 0, 0, 0, 0, /* 85 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 86 - <> */ '<', '>', '|', 0, 0, 0, 0, 0, A('<'), 0, 0, 0, 0, 0, 0, 0, /* 87 - F11 */ SF1, SF1, 0, 0, F11, 0, 0, 0, AF11, 0, 0, 0, 0, 0, 0, 0, /* 88 - F12 */ SF2, SF2, 0, 0, F12, 0, 0, 0, AF12, 0, 0, 0, 0, 0, 0, 0, /* 89 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 91 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 92 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 93 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 94 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 95 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 96 - E0ENTER*/ E0ENTER,E0ENTER,E0ENTER,E0ENTER,E0ENTER,0, 0, 0, E0ENTER,0, 0, 0, 0, 0, 0, 0, /* 97 - RCTRL */ RCTRL, RCTRL, RCTRL, RCTRL, RCTRL, 0, 0, 0, RCTRL, 0, 0, 0, 0, 0, 0, 0, /* 98 - E0SLASH*/ E0SLASH,E0SLASH,E0SLASH,E0SLASH,E0SLASH,0, 0, 0, E0SLASH,0, 0, 0, 0, 0, 0, 0, /* 99 - */ 0, 0, 0, 0, C('\\'),0, 0, 0, C('\\'),0, 0, 0, 0, 0, 0, 0, /* 100 - ALTGR */ ALTGR, ALTGR, ALTGR, ALTGR, ALTGR, 0, 0, 0, ALTGR, 0, 0, 0, 0, 0, 0, 0, /* 101 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 102 - E0HOME */ E0HOME, E0HOME, E0HOME, E0HOME, E0HOME, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 103 - E0UP */ E0UP, E0UP, E0UP, E0UP, E0UP, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 104 - E0PGUP */ E0PGUP, E0PGUP, E0PGUP, E0PGUP, E0PGUP, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 105 - E0LEFT */ E0LEFT, E0LEFT, E0LEFT, E0LEFT, E0LEFT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 106 - E0RIGHT*/ E0RIGHT,E0RIGHT,E0RIGHT,E0RIGHT,E0RIGHT,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 107 - E0END */ E0END, E0END, E0END, E0END, E0END, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 108 - E0DOWN */ E0DOWN, E0DOWN, E0DOWN, E0DOWN, E0DOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 109 - E0PGDN */ E0PGDN, E0PGDN, E0PGDN, E0PGDN, E0PGDN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 110 - E0INS */ E0INS, E0INS, E0INS, E0INS, E0INS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 111 - E0DEL */ E0DEL, E0DEL, E0DEL, E0DEL, E0DEL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 112 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 113 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 114 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 115 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 116 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 117 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 118 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 119 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 120 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 121 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 122 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 123 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 124 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 125 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 126 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 127 - */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; ================================================ FILE: drivers/char/fb.c ================================================ /* * fiwix/drivers/char/fb.c * * Copyright 2021-2022, Jordi Sanfeliu. All rights reserved. * Portions Copyright 2024, Greg Haerr. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IO_FB_XRES 2 /* TODO(ghaerr): to be removed shortly */ #define IO_FB_YRES 3 static struct fs_operations fb_driver_fsop = { 0, 0, fb_open, fb_close, fb_read, fb_write, fb_ioctl, fb_llseek, NULL, /* readdir */ NULL, /* readdir64 */ fb_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 fb_device = { "fb", FB_MAJOR, { 0, 0, 0, 0, 0, 0, 0, 0 }, NULL, NULL, &fb_driver_fsop, NULL, NULL, NULL }; int fb_open(struct inode *i, struct fd *f) { return 0; } int fb_close(struct inode *i, struct fd *f) { return 0; } int fb_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { unsigned int addr; if(f->offset >= video.memsize) { return 0; } addr = (unsigned int)video.address + f->offset; count = MIN(count, video.memsize - f->offset); memcpy_b(buffer, (void *)addr, count); f->offset += count; return count; } int fb_write(struct inode *i, struct fd *f, const char *buffer, __size_t count) { unsigned int addr; if(f->offset >= video.memsize) { return -ENOSPC; } addr = (unsigned int)video.address + f->offset; count = MIN(count, video.memsize - f->offset); memcpy_b((void *)addr, buffer, count); f->offset += count; return count; } int fb_mmap(struct inode *i, struct vma *vma) { unsigned int fbaddr, addr; fbaddr = (unsigned int)video.address; for (addr = vma->start; addr < vma->end; addr += 4096) { /* map framebuffer physaddr into user space without page allocations */ map_page_flags(current, addr, fbaddr, PROT_READ|PROT_WRITE, PAGE_NOALLOC); fbaddr += 4096; } return 0; } int fb_ioctl(struct inode *i, struct fd *f, int cmd, unsigned int arg) { switch (cmd) { case IO_FB_XRES: return video.fb_width; case IO_FB_YRES: return video.fb_height; default: return -EINVAL; } } __loff_t fb_llseek(struct inode *i, __loff_t offset) { return offset; } void fb_init(void) { unsigned int limit, from; SET_MINOR(fb_device.minors, FB_MINOR); limit = (unsigned int)video.address + video.memsize - 1; /* * Frame buffer memory must be marked as reserved because its memory * range (e.g: 0xFD000000-0xFDFFFFFF) might conflict with the physical * memory below 1GB (e.g: 0x3D000000-0x3DFFFFFF + PAGE_OFFSET). */ from = (unsigned int)video.address - PAGE_OFFSET; bios_map_reserve(from, from + video.memsize); printk("fb0 0x%08x-0x%08x\ttype=%s %x.%x resolution=%dx%dx%d size=%dMB\n", video.address, limit, video.signature, video.fb_version >> 8, video.fb_version & 0xFF, video.fb_width, video.fb_height, video.fb_bpp, video.memsize / 1024 / 1024 ); #ifdef CONFIG_PCI if(video.pci_dev) { pci_show_desc(video.pci_dev); } #endif /* CONFIG_PCI */ if(register_device(CHR_DEV, &fb_device)) { printk("ERROR: %s(): unable to register fb device.\n", __FUNCTION__); return; } } ================================================ FILE: drivers/char/keyboard.c ================================================ /* * fiwix/drivers/char/keyboard.c * * Copyright 2018-2022, 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 #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 e0_keys[128] = { 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0F */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ 0, 0, 0, 0, E0ENTER, RCTRL, 0, 0, /* 0x18-0x1F */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2F */ 0, 0, 0, 0, 0, E0SLASH, 0, 0, /* 0x30-0x37 */ ALTGR, 0, 0, 0, 0, 0, 0, 0, /* 0x38-0x3F */ 0, 0, 0, 0, 0, 0, 0, E0HOME, /* 0x40-0x47 */ E0UP, E0PGUP, 0, E0LEFT, 0, E0RIGHT, 0, E0END, /* 0x48-0x4F */ E0DOWN, E0PGDN, E0INS, E0DEL, 0, 0, 0, 0, /* 0x50-0x57 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x58-0x5f */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x68-0x6F */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7F */ }; 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 altsysrq = 0; static int sysrq_op = 0; static unsigned char kb_identify[2] = {0, 0}; static unsigned char is_ps2 = 0; static unsigned char orig_scan_set = 0; volatile unsigned char ack = 0; static char do_switch_console = -1; static unsigned char do_buf_scroll = 0; static unsigned char do_setleds = 0; static unsigned char do_tty_stop = 0; static unsigned char do_tty_start = 0; static unsigned char do_sysrq = 0; char ctrl_alt_del = 1; char any_key_to_reboot = 0; static struct bh keyboard_bh = { 0, &irq_keyboard_bh, NULL }; static struct interrupt irq_config_keyboard = { 0, "keyboard", &irq_keyboard, NULL }; 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 tilde_table[NR_DIACR] = { { 'A', '\303' }, { 'N', '\321' }, { 'O', '\325' }, { 'a', '\343' }, { 'n', '\361' }, { 'o', '\365' }, }; 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_identify(void) { char config; /* disable */ ps2_write(PS2_DATA, PS2_KB_DISABLE); if(ps2_wait_ack()) { printk("WARNING: %s(): ACK not received on disable command!\n", __FUNCTION__); } else { is_ps2++; } /* identify */ ps2_write(PS2_DATA, PS2_DEV_IDENTIFY); if(ps2_wait_ack()) { printk("WARNING: %s(): ACK not received on identify command!\n", __FUNCTION__); } else { is_ps2++; } kb_identify[0] = ps2_read(PS2_DATA); kb_identify[1] = ps2_read(PS2_DATA); /* get scan code */ ps2_write(PS2_COMMAND, PS2_CMD_RECV_CONFIG); config = ps2_read(PS2_DATA); /* save state */ ps2_write(PS2_COMMAND, PS2_CMD_SEND_CONFIG); ps2_write(PS2_DATA, config & ~0x40); /* unset translation */ ps2_write(PS2_DATA, PS2_KB_GETSETSCAN); if(ps2_wait_ack()) { printk("WARNING: %s(): ACK not received on get scan code command!\n", __FUNCTION__); } ps2_write(PS2_DATA, 0); if(ps2_wait_ack()) { printk("WARNING: %s(): ACK not received on get scan code command!\n", __FUNCTION__); } orig_scan_set = ps2_read(PS2_DATA); if(orig_scan_set != 2) { ps2_write(PS2_DATA, PS2_KB_GETSETSCAN); ps2_write(PS2_DATA, 2); if(ps2_wait_ack()) { printk("WARNING: %s(): ACK not received on set scan code command!\n", __FUNCTION__); } } ps2_write(PS2_COMMAND, PS2_CMD_SEND_CONFIG); ps2_write(PS2_DATA, config); /* restore state */ /* enable */ ps2_write(PS2_DATA, PS2_DEV_ENABLE); if(ps2_wait_ack()) { printk("WARNING: %s(): ACK not received on enable command!\n", __FUNCTION__); } ps2_clear_buffer(); } static void putc(struct tty *tty, unsigned char ch) { if(tty->count) { if(charq_putchar(&tty->read_q, ch) < 0) { if(tty->termios.c_iflag & IMAXBEL) { vconsole_beep(); } } } } static void puts(struct tty *tty, char *seq) { char ch; if(tty->count) { while((ch = *(seq++))) { putc(tty, ch); } } } void set_leds(unsigned char led_status) { ps2_write(PS2_DATA, PS2_KB_SETLED); ps2_wait_ack(); ps2_write(PS2_DATA, led_status); ps2_wait_ack(); } void irq_keyboard(int num, struct sigcontext *sc) { __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(PS2_DATA); /* keyboard controller said 'acknowledge!' */ if(scode == DEV_ACK) { ack = 1; return; } keyboard_bh.flags |= BH_ACTIVE; /* if in pure raw mode just queue the scan code and return */ if(tty->kbd.mode == K_RAW) { putc(tty, scode); return; } if(scode == EXTKEY) { extkey = 1; return; } if(extkey) { key = e0_keys[scode & 0x7F]; } else { key = scode & 0x7F; } if(tty->kbd.mode == K_MEDIUMRAW) { putc(tty, key | (scode & 0x80)); extkey = 0; return; } key = keymap[NR_MODIFIERS * (scode & 0x7F)]; /* bit 7 enabled means a key has been released */ if(scode & NR_SCODES) { switch(key) { case CTRL: case LCTRL: case RCTRL: ctrl = 0; break; case ALT: if(!extkey) { alt = 0; altsysrq = 0; } else { altgr = 0; } break; case SHIFT: case LSHIFT: case RSHIFT: 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; do_setleds = 1; } leds = 1; return; case NUMS: if(!leds) { vc->led_status ^= NUMSBIT; vc->numlock = !vc->numlock; do_setleds = 1; } leds = 1; return; case SCRL: if(!leds) { if(vc->scrlock) { do_tty_start = 1; } else { do_tty_stop = 1; } } leds = 1; return; case CTRL: case LCTRL: case RCTRL: ctrl = 1; return; case ALT: if(!extkey) { alt = 1; } else { altgr = 1; } return; case SHIFT: case LSHIFT: case RSHIFT: 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_buf_scroll = SCROLL_UP; return; } if(shift && (key == PGDN)) { do_buf_scroll = SCROLL_DOWN; return; } if(extkey && (scode == SLASH_NPAD)) { key = SLASH; } if(any_key_to_reboot) { reboot(); } type = key & 0xFF00; c = key & 0xFF; if(altsysrq) { /* treat 0-9 and a-z keys as normal */ type &= ~META_KEYS; } switch(type) { case FN_KEYS: if(c > sizeof(fn_seq) / sizeof(char *)) { printk("WARNING: %s(): unrecognized function key.\n", __FUNCTION__); break; } puts(tty, fn_seq[c]); break; case SPEC_KEYS: switch(key) { case CR: putc(tty, C('M')); break; case SYSRQ: altsysrq = 1; 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 TILDE ^ DEAD_KEYS: deadkey = 4; diacr = tilde_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(altsysrq) { switch(c) { case 'l': sysrq_op = SYSRQ_STACK; do_sysrq = 1; break; case 'm': sysrq_op = SYSRQ_MEMORY; do_sysrq = 1; break; case 't': sysrq_op = SYSRQ_TASKS; do_sysrq = 1; break; default: sysrq_op = SYSRQ_UNDEF; do_sysrq = 1; break; } break; } if(deadkey && c == ' ') { c = diacr_chars[deadkey - 1]; } putc(tty, c); break; } deadkey = 0; } void irq_keyboard_bh(struct sigcontext *sc) { struct tty *tty; struct vconsole *vc; char value; tty = get_tty(MKDEV(VCONSOLES_MAJOR, current_cons)); vc = (struct vconsole *)tty->driver_data; video.screen_on(vc); if(do_switch_console >= 0) { value = do_switch_console; do_switch_console = -1; vconsole_select(value); } if(do_buf_scroll) { value = do_buf_scroll; do_buf_scroll = 0; video.buf_scroll(vc, value); } if(do_setleds) { do_setleds = 0; set_leds(vc->led_status); } if(do_tty_start) { do_tty_start = do_tty_stop = 0; tty->start(tty); } if(do_tty_stop) { do_tty_start = do_tty_stop = 0; tty->stop(tty); } if(do_sysrq) { do_sysrq = 0; sysrq(sysrq_op); } for(tty = tty_table; tty; tty = tty->next) { if(MAJOR(tty->dev) == VCONSOLES_MAJOR && MINOR(tty->dev) < NR_VCONSOLES) { if(!tty->read_q.count) { continue; } if(tty->kbd.mode == K_RAW || tty->kbd.mode == K_MEDIUMRAW) { wakeup(&tty->read_q); continue; } if(!can_lock_area(AREA_TTY_READ)) { keyboard_bh.flags |= BH_ACTIVE; continue; } tty->input(tty); unlock_area(AREA_TTY_READ); } } } void keyboard_init(void) { struct tty *tty; struct vconsole *vc; int errno; tty = get_tty(MKDEV(VCONSOLES_MAJOR, current_cons)); vc = (struct vconsole *)tty->driver_data; video.screen_on(vc); video.cursor_blink((unsigned int)vc); add_bh(&keyboard_bh); if(!register_irq(KEYBOARD_IRQ, &irq_config_keyboard)) { enable_irq(KEYBOARD_IRQ); } /* reset device */ ps2_write(PS2_DATA, PS2_DEV_RESET); if(ps2_wait_ack()) { printk("WARNING: %s(): ACK not received on reset command!\n", __FUNCTION__); } if((errno = ps2_read(PS2_DATA)) != DEV_RESET_OK) { /* some keyboards return an ID byte before 0xAA */ if((errno = ps2_read(PS2_DATA)) != DEV_RESET_OK) { printk("WARNING: %s(): keyboard returned 0x%x on reset (1).\n", __FUNCTION__, errno); } } ps2_clear_buffer(); keyboard_identify(); printk("keyboard 0x%04x-0x%04x %d", 0x60, 0x64, KEYBOARD_IRQ); printk("\ttype=%s %s", kb_identify[0] == 0xAB ? "MF2" : "unknown", is_ps2 ? "PS/2" : ""); printk(" %s", (kb_identify[1] == 0x41 || kb_identify[1] == 0xC1) ? "translated" : ""); printk(" scan set 2"); if(orig_scan_set != 2) { printk(" (was %d)", orig_scan_set); } printk("\n"); ps2_write(PS2_DATA, PS2_DEV_RATE); ps2_wait_ack(); ps2_write(PS2_DATA, DELAY_250 | RATE_30); ps2_wait_ack(); } ================================================ FILE: drivers/char/lp.c ================================================ /* * fiwix/drivers/char/lp.c * * Copyright 2018-2022, 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, /* llseek */ NULL, /* readdir */ NULL, /* readdir64 */ 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", LP_MAJOR, { 0, 0, 0, 0, 0, 0, 0, 0 }, NULL, NULL, &lp_driver_fsop, NULL, NULL, NULL }; 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 *f) { 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 *f) { 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 *f, 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; } } } ================================================ FILE: drivers/char/memdev.c ================================================ /* * fiwix/drivers/char/memdev.c * * Copyright 2018-2022, 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_llseek, NULL, /* readdir */ NULL, /* readdir64 */ 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_llseek, NULL, /* readdir */ NULL, /* readdir64 */ 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_llseek, NULL, /* readdir */ NULL, /* readdir64 */ 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 port_driver_fsop = { 0, 0, port_open, port_close, port_read, port_write, NULL, /* ioctl */ port_llseek, NULL, /* readdir */ NULL, /* readdir64 */ 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_llseek, NULL, /* readdir */ NULL, /* readdir64 */ 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 full_driver_fsop = { 0, 0, full_open, full_close, full_read, full_write, NULL, /* ioctl */ full_llseek, NULL, /* readdir */ NULL, /* readdir64 */ 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 urandom_driver_fsop = { 0, 0, urandom_open, urandom_close, urandom_read, urandom_write, NULL, /* ioctl */ urandom_llseek, NULL, /* readdir */ NULL, /* readdir64 */ 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, /* llseek */ NULL, /* readdir */ NULL, /* readdir64 */ 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", MEMDEV_MAJOR, { 0, 0, 0, 0, 0, 0, 0, 0 }, NULL, NULL, &memdev_driver_fsop, NULL, NULL, NULL }; int mem_open(struct inode *i, struct fd *f) { return 0; } int mem_close(struct inode *i, struct fd *f) { return 0; } int mem_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { unsigned int physical_memory; physical_memory = (kstat.physical_pages << PAGE_SHIFT); if(f->offset >= physical_memory) { return 0; } count = MIN(count, physical_memory - f->offset); memcpy_b(buffer, (void *)P2V((unsigned int)f->offset), count); f->offset += count; return count; } int mem_write(struct inode *i, struct fd *f, const char *buffer, __size_t count) { unsigned int physical_memory; physical_memory = (kstat.physical_pages << PAGE_SHIFT); if(f->offset >= physical_memory) { return 0; } count = MIN(count, physical_memory - f->offset); memcpy_b((void *)P2V((unsigned int)f->offset), buffer, count); f->offset += count; return count; } __loff_t mem_llseek(struct inode *i, __loff_t offset) { return offset; } int kmem_open(struct inode *i, struct fd *f) { return 0; } int kmem_close(struct inode *i, struct fd *f) { return 0; } int kmem_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { unsigned int physical_memory; physical_memory = P2V((kstat.physical_pages << PAGE_SHIFT)); if(P2V(f->offset + count) < physical_memory) { memcpy_b(buffer, (void *)P2V((unsigned int)f->offset), count); f->offset += count; } else { count = 0; } return count; } int kmem_write(struct inode *i, struct fd *f, const char *buffer, __size_t count) { unsigned int physical_memory; physical_memory = P2V((kstat.physical_pages << PAGE_SHIFT)); if(P2V(f->offset + count) < physical_memory) { memcpy_b((void *)P2V((unsigned int)f->offset), buffer, count); f->offset += count; } else { count = 0; } return count; } __loff_t kmem_llseek(struct inode *i, __loff_t offset) { return offset; } int null_open(struct inode *i, struct fd *f) { return 0; } int null_close(struct inode *i, struct fd *f) { return 0; } int null_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { return 0; } int null_write(struct inode *i, struct fd *f, const char *buffer, __size_t count) { return count; } __loff_t null_llseek(struct inode *i, __loff_t offset) { return offset; } int port_open(struct inode *i, struct fd *f) { return 0; } int port_close(struct inode *i, struct fd *f) { return 0; } int port_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { unsigned int n; if(f->offset >= 65535) { return 0; } count = MIN(count, 65536 - f->offset); for(n = f->offset; n < (f->offset + count); n++, buffer++) { *buffer = inport_b(n); } f->offset += count; return count; } int port_write(struct inode *i, struct fd *f, const char *buffer, __size_t count) { unsigned int n; if(f->offset >= 65535) { return 0; } count = MIN(count, 65536 - f->offset); for(n = f->offset; n < (f->offset + count); n++, buffer++) { outport_b(n, *buffer); } f->offset += count; return count; } __loff_t port_llseek(struct inode *i, __loff_t offset) { return offset; } int zero_open(struct inode *i, struct fd *f) { return 0; } int zero_close(struct inode *i, struct fd *f) { return 0; } int zero_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { memset_b(buffer, 0, count); return count; } int zero_write(struct inode *i, struct fd *f, const char *buffer, __size_t count) { return count; } __loff_t zero_llseek(struct inode *i, __loff_t offset) { return offset; } int full_open(struct inode *i, struct fd *f) { return 0; } int full_close(struct inode *i, struct fd *f) { return 0; } int full_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { memset_b(buffer, 0, count); return count; } int full_write(struct inode *i, struct fd *f, const char *buffer, __size_t count) { return -ENOSPC; } __loff_t full_llseek(struct inode *i, __loff_t offset) { return offset; } int urandom_open(struct inode *i, struct fd *f) { return 0; } int urandom_close(struct inode *i, struct fd *f) { return 0; } int urandom_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { int n; for(n = 0; n < count; n++) { kstat.random_seed = kstat.random_seed * 1103515245 + 12345; *buffer = (char)(unsigned int)(kstat.random_seed / 65536) % 256; buffer++; } return count; } int urandom_write(struct inode *i, struct fd *f, const char *buffer, __size_t count) { return count; } __loff_t urandom_llseek(struct inode *i, __loff_t offset) { return offset; } int memdev_open(struct inode *i, struct fd *f) { 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_PORT: i->fsop = &port_driver_fsop; break; case MEMDEV_ZERO: i->fsop = &zero_driver_fsop; break; case MEMDEV_FULL: i->fsop = &full_driver_fsop; break; case MEMDEV_RANDOM: i->fsop = &urandom_driver_fsop; break; case MEMDEV_URANDOM: i->fsop = &urandom_driver_fsop; break; default: return -ENXIO; } return i->fsop->open(i, f); } /* * 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(!is_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) { 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_PORT); SET_MINOR(memdev_device.minors, MEMDEV_ZERO); SET_MINOR(memdev_device.minors, MEMDEV_FULL); SET_MINOR(memdev_device.minors, MEMDEV_RANDOM); SET_MINOR(memdev_device.minors, MEMDEV_URANDOM); if(register_device(CHR_DEV, &memdev_device)) { printk("ERROR: %s(): unable to register memory devices.\n", __FUNCTION__); return; } } ================================================ FILE: drivers/char/ps2.c ================================================ /* * fiwix/drivers/char/ps2.c * * Copyright 2024, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include /* * PS/2 System Control Port A bits * ------------------------------- * #7 RW:fixed disk activity led * #6 RW:fixed disk activity led * #5 RW:reserved * #4 RW:watchdog timer status * #3 RW:security lock latch * #2 RW:reserved * #1 RW:alternate gate A20 * #0 RW:alternate hot reset */ #define PS2_SYSCTRL_A 0x92 /* PS/2 system control port A (R/W) */ extern volatile unsigned char ack; /* wait controller output buffer to be full or for controller acknowledge */ static int is_ready_to_read(void) { int n; for(n = 0; n < PS2_TIMEOUT; n++) { if(ack) { return 1; } if(inport_b(PS2_STATUS) & PS2_STAT_OUTBUSY) { return 1; } } printk("WARNING: PS/2 controller not ready to read.\n"); return 0; } /* wait controller input buffer to be clear */ static int is_ready_to_write(void) { int n; for(n = 0; n < PS2_TIMEOUT; n++) { if(!(inport_b(PS2_STATUS) & PS2_STAT_INBUSY)) { return 1; } } return 0; } static void ps2_delay(void) { int n; for(n = 0; n < 1000; n++) { NOP(); } } int ps2_wait_ack(void) { int n; if(is_ready_to_read()) { for(n = 0; n < 1000; n++) { if(inport_b(PS2_DATA) == DEV_ACK) { return 0; } ps2_delay(); } } return 1; } void ps2_write(const unsigned char port, const unsigned char byte) { ack = 0; if(is_ready_to_write()) { outport_b(port, byte); } } unsigned char ps2_read(const unsigned char port) { if(is_ready_to_read()) { return inport_b(port); } return 0; } void ps2_clear_buffer(void) { int n; for(n = 0; n < 1000; n++) { ps2_delay(); if(inport_b(PS2_STATUS) & PS2_STAT_OUTBUSY) { ps2_read(PS2_DATA); continue; } break; } } void reboot(void) { CLI(); ps2_write(PS2_SYSCTRL_A, 0x01); /* Fast Hot Reset */ ps2_write(PS2_COMMAND, PS2_CMD_HOTRESET); /* Hot Reset */ HLT(); } void ps2_init(void) { int errno; unsigned char config, config2; char type, supp_ports; type = supp_ports = 0; /* disable device(s) */ ps2_write(PS2_COMMAND, PS2_CMD_DISABLE_CH1); ps2_write(PS2_COMMAND, PS2_CMD_DISABLE_CH2); /* flush buffer */ inport_b(PS2_DATA); /* get controller configuration */ config = 0; ps2_write(PS2_COMMAND, PS2_CMD_RECV_CONFIG); config = ps2_read(PS2_DATA); if(!(kparms.flags & KPARMS_PS2_NORESET)) { /* set controller configuration (disabling IRQs) */ ps2_write(PS2_COMMAND, PS2_CMD_SEND_CONFIG); ps2_write(PS2_DATA, config & ~(0x01 | 0x02)); /* PS/2 controller self-test */ ps2_write(PS2_COMMAND, PS2_CMD_SELF_TEST); if((errno = ps2_read(PS2_DATA)) != 0x55) { printk("WARNING: %s(): PS/2 controller not returned 0x55 after self-test (was 0x%x), try with the parameter 'ps2_noreset'.\n", __FUNCTION__, errno); return; } else { supp_ports = 1; } /* * This sets again the controller configuration since the previous * step may also reset the PS/2 controller to its power-on defaults. */ ps2_write(PS2_COMMAND, PS2_CMD_SEND_CONFIG); ps2_write(PS2_DATA, config); } /* double-check if we have a second channel */ if(config & 0x20) { ps2_write(PS2_COMMAND, PS2_CMD_ENABLE_CH2); ps2_write(PS2_COMMAND, PS2_CMD_RECV_CONFIG); if(!(ps2_read(PS2_DATA) & 0x20)) { supp_ports = 1 + (config & 0x20 ? 1 : 0); } ps2_write(PS2_COMMAND, PS2_CMD_DISABLE_CH2); } /* test interface first channel */ ps2_write(PS2_COMMAND, PS2_CMD_TEST_CH1); if((errno = ps2_read(PS2_DATA)) != 0) { printk("WARNING: %s(): test in first PS/2 interface returned 0x%x.\n", __FUNCTION__, errno); } if(supp_ports > 1) { /* test interface second channel */ ps2_write(PS2_COMMAND, PS2_CMD_TEST_CH2); if((errno = ps2_read(PS2_DATA)) != 0) { printk("WARNING: %s(): test in second PS/2 interface returned 0x%x.\n", __FUNCTION__, errno); } } /* check if it is a type 1 or type 2 controller */ config2 = 0; ps2_write(PS2_COMMAND, PS2_CMD_RECV_CONFIG); config = ps2_read(PS2_DATA); /* save state */ ps2_write(PS2_COMMAND, PS2_CMD_SEND_CONFIG); ps2_write(PS2_DATA, config | 0x40); /* set translation */ ps2_write(PS2_COMMAND, PS2_CMD_RECV_CONFIG); config2 = ps2_read(PS2_DATA); ps2_write(PS2_COMMAND, PS2_CMD_SEND_CONFIG); ps2_write(PS2_DATA, config); /* restore state */ type = (config2 & 0x40) ? 1 : 0; /* enable interrupts if channels are present */ config |= 0x01; if(supp_ports > 1) { config |= 0x02; } config |= 0x40; /* allow translation */ ps2_write(PS2_COMMAND, PS2_CMD_SEND_CONFIG); ps2_write(PS2_DATA, config); printk("ps/2 0x%04x,0x%04x -\tcontroller type=%d, channels=%d\n", PS2_DATA, PS2_COMMAND, type, supp_ports); /* enable device(s) */ ps2_write(PS2_COMMAND, PS2_CMD_ENABLE_CH1); keyboard_init(); #ifdef CONFIG_PSAUX if(supp_ports > 1) { ps2_write(PS2_COMMAND, PS2_CMD_ENABLE_CH2); psaux_init(); } #endif /* CONFIG_PSAUX */ } ================================================ FILE: drivers/char/psaux.c ================================================ /* * fiwix/drivers/char/psaux.c * * Copyright 2024, 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 #ifdef CONFIG_PSAUX static struct fs_operations psaux_driver_fsop = { 0, 0, psaux_open, psaux_close, psaux_read, psaux_write, NULL, /* ioctl */ NULL, /* llseek */ NULL, /* readdir */ NULL, /* readdir64 */ NULL, /* mmap */ psaux_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 psaux_device = { "psaux", PSAUX_MAJOR, { 0, 0, 0, 0, 0, 0, 0, 0 }, NULL, NULL, &psaux_driver_fsop, NULL, NULL, NULL }; struct psaux psaux_table; static struct interrupt irq_config_psaux = { 0, "psaux", &irq_psaux, NULL }; extern volatile unsigned char ack; static unsigned char status[3] = { 0, 0, 0}; static unsigned char is_ps2 = 0; static char id = -1; static int psaux_command_write(const unsigned char byte) { ps2_write(PS2_COMMAND, PS2_CMD_CH2_PREFIX); ps2_write(PS2_DATA, byte); if(ps2_wait_ack()) { printk("WARNING: %s(): ACK not received on %x command!\n", __FUNCTION__, byte); return 1; } return 0; } static void psaux_identify(void) { /* disable */ psaux_command_write(PS2_AUX_DISABLE); /* status information */ psaux_command_write(PS2_DEV_GETINFO); status[0] = ps2_read(PS2_DATA); /* status */ status[2] = ps2_read(PS2_DATA); /* resolution */ status[2] = ps2_read(PS2_DATA); /* sample rate */ /* identify */ psaux_command_write(PS2_DEV_RATE); psaux_command_write(200); psaux_command_write(PS2_DEV_RATE); psaux_command_write(100); psaux_command_write(PS2_DEV_RATE); psaux_command_write(80); psaux_command_write(PS2_DEV_IDENTIFY); id = ps2_read(PS2_DATA); ps2_clear_buffer(); /* enable */ psaux_command_write(PS2_DEV_ENABLE); } void irq_psaux(int num, struct sigcontext *sc) { unsigned char ch; ch = inport_b(PS2_DATA); /* aux controller said 'acknowledge!' */ if(ch == DEV_ACK) { ack = 1; } if(!psaux_table.count) { return; } charq_putchar(&psaux_table.read_q, ch); wakeup(&psaux_read); wakeup(&do_select); } int psaux_open(struct inode *i, struct fd *f) { int minor; minor = MINOR(i->rdev); if(!TEST_MINOR(psaux_device.minors, minor)) { return -ENXIO; } if(psaux_table.count++) { return 0; } memset_b(&psaux_table.read_q, 0, sizeof(struct clist)); memset_b(&psaux_table.write_q, 0, sizeof(struct clist)); return 0; } int psaux_close(struct inode *i, struct fd *f) { int minor; minor = MINOR(i->rdev); if(!TEST_MINOR(psaux_device.minors, minor)) { return -ENXIO; } psaux_table.count--; return 0; } int psaux_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { int minor, bytes_read; unsigned char ch; minor = MINOR(i->rdev); if(!TEST_MINOR(psaux_device.minors, minor)) { return -ENXIO; } while(!psaux_table.read_q.count) { if(f->flags & O_NONBLOCK) { return -EAGAIN; } if(sleep(&psaux_read, PROC_INTERRUPTIBLE)) { return -EINTR; } } bytes_read = 0; while(bytes_read < count) { if(psaux_table.read_q.count) { ch = charq_getchar(&psaux_table.read_q); buffer[bytes_read++] = ch; continue; } break; } if(bytes_read) { i->i_atime = CURRENT_TIME; } return bytes_read; } int psaux_write(struct inode *i, struct fd *f, const char *buffer, __size_t count) { int minor, bytes_written; unsigned char ch; minor = MINOR(i->rdev); if(!TEST_MINOR(psaux_device.minors, minor)) { return -ENXIO; } bytes_written = 0; while(bytes_written < count) { ch = buffer[bytes_written++]; psaux_command_write(ch); } if(bytes_written) { i->i_mtime = CURRENT_TIME; } return bytes_written; } int psaux_select(struct inode *i, struct fd *f, int flag) { int minor; minor = MINOR(i->rdev); if(!TEST_MINOR(psaux_device.minors, minor)) { return -ENXIO; } switch(flag) { case SEL_R: if(psaux_table.read_q.count) { return 1; } break; } return 0; } void psaux_init(void) { int errno; /* reset device */ psaux_command_write(PS2_DEV_RESET); if((errno = ps2_read(PS2_DATA)) != DEV_RESET_OK) { printk("psaux 0x%04x-0x%04x \tdevice not detected\n", 0x60, 0x64); return; } if(ps2_read(PS2_DATA) == 0) { is_ps2 = 1; } if(!register_irq(PSAUX_IRQ, &irq_config_psaux)) { enable_irq(PSAUX_IRQ); } ps2_clear_buffer(); psaux_identify(); printk("psaux 0x%04x-0x%04x %d", 0x60, 0x64, PSAUX_IRQ); printk("\ttype=%s", is_ps2 ? "PS/2" : "unknown"); switch(id) { case -1: printk(", unknown ID %x", id & 0xFF); break; case 0: printk(", standard mouse"); break; case 2: printk(", track ball"); break; case 3: printk(", 3-button wheel mouse"); break; case 4: printk(", 5-button wheel mouse"); break; default: printk(", unknown mouse"); break; } printk("\n"); memset_b(&psaux_table, 0, sizeof(struct psaux)); SET_MINOR(psaux_device.minors, PSAUX_MINOR); if(register_device(CHR_DEV, &psaux_device)) { printk("WARNING: %s(): unable to register psaux device.\n", __FUNCTION__); } } #endif /* CONFIG_PSAUX */ ================================================ FILE: drivers/char/pty.c ================================================ /* * fiwix/drivers/char/pty.c * * Copyright 2025, 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 #ifdef CONFIG_UNIX98_PTYS extern struct devpts_files *devpts_list; static struct fs_operations pty_master_driver_fsop = { 0, 0, tty_open, tty_close, pty_read, pty_write, tty_ioctl, tty_llseek, NULL, /* readdir */ NULL, /* readdir64 */ NULL, /* mmap */ pty_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 pty_slave_driver_fsop = { 0, 0, tty_open, tty_close, tty_read, tty_write, tty_ioctl, tty_llseek, NULL, /* readdir */ NULL, /* readdir64 */ NULL, /* mmap */ tty_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 pty_master_device = { "ptmx", PTY_MASTER_MAJOR, { 0, 0, 0, 0, 0, 0, 0, 0 }, NULL, NULL, &pty_master_driver_fsop, NULL, NULL, NULL }; static struct device pty_slave_device = { "pts", PTY_SLAVE_MAJOR, { 0, 0, 0, 0, 0, 0, 0, 0 }, NULL, NULL, &pty_slave_driver_fsop, NULL, NULL, NULL }; void pty_wakeup_read(struct tty *tty) { wakeup(&pty_read); wakeup(&do_select); } int pty_open(struct tty *tty) { struct filesystems *fs; struct inode *i; int n, minor; if(tty->flags & TTY_PTY_LOCK) { return -EIO; } if(MAJOR(tty->dev) == PTY_SLAVE_MAJOR) { return 0; } minor = MINOR(tty->dev); if(!TEST_MINOR(pty_master_device.minors, minor)) { return -ENXIO; } if(!(fs = get_filesystem("devpts"))) { printk("WARNING: %s(): devpts filesystem is not registered!\n", __FUNCTION__); return -EINVAL; } if(!(i = ialloc(&fs->mp->sb, S_IFCHR))) { return -EINVAL; } for(n = 0; n < NR_PTYS; n++) { if(devpts_list[n].inode == i) { break; } } tty->driver_data = (void *)&devpts_list[n]; i->i_uid = current->uid; i->i_gid = current->gid; minor = i->inode - (DEVPTS_ROOT_INO + 1); i->rdev = MKDEV(PTY_SLAVE_MAJOR, minor); SET_MINOR(pty_slave_device.minors, minor); register_device(CHR_DEV, &pty_slave_device); tty->flags &= ~TTY_OTHER_CLOSED; return 0; } int pty_close(struct tty *tty) { struct devpts_files *dp; struct inode *i; int minor; if(MAJOR(tty->dev) == PTY_SLAVE_MAJOR) { if(tty->count > 2) { return 0; } } tty->flags |= TTY_OTHER_CLOSED; wakeup(&tty->read_q); wakeup(&pty_read); wakeup(&do_select); if(MAJOR(tty->dev) == PTY_SLAVE_MAJOR) { minor = MINOR(tty->dev); CLEAR_MINOR(pty_slave_device.minors, minor); unregister_device(CHR_DEV, &pty_slave_device); dp = (struct devpts_files *)tty->driver_data; i = (struct inode *)dp->inode; if(tty->count < 2) { if(tty->link) { tty->link->count--; /* /dev/ptmx */ } unregister_tty(tty); i->i_nlink--; iput(i); } } return 0; } int pty_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { struct tty *tty; unsigned char ch; int n; tty = f->private_data; n = 0; while(n < count) { if(tty->write_q.count > 0) { ch = charq_getchar(&tty->write_q); buffer[n++] = ch; continue; } if(n) { break; } if(tty->flags & TTY_OTHER_CLOSED) { n = -EIO; break; } if(sleep(&pty_read, PROC_INTERRUPTIBLE)) { n = -EINTR; break; } } wakeup(&tty->write_q); wakeup(&do_select); return n; } int pty_write(struct inode *i, struct fd *f, const char *buffer, __size_t count) { struct tty *tty; unsigned char ch; int n; tty = f->private_data; n = 0; while(n < count) { ch = buffer[n++]; if(charq_room(&tty->read_q) > 0) { charq_putchar(&tty->read_q, ch); } else { break; } } tty->input(tty); wakeup(&do_select); return n; } int pty_ioctl(struct tty *tty, struct fd *f, int cmd, unsigned int arg) { switch(cmd) { case TIOCGPTN: { unsigned int *val = (unsigned int *)arg; *val = MINOR(tty->dev); break; } case TIOCSPTLCK: { int val = *(unsigned int *)arg; if(val) { tty->flags |= TTY_PTY_LOCK; } else { tty->flags &= ~TTY_PTY_LOCK; } break; } default: return -EINVAL; } return 0; } int pty_select(struct inode *i, struct fd *f, int flag) { struct tty *tty; tty = f->private_data; switch(flag) { case SEL_R: if(tty->write_q.count) { return 1; } if(tty->flags & TTY_OTHER_CLOSED) { return 1; } break; case SEL_W: if(!tty->read_q.count) { return 1; } break; } return 0; } void pty_init(void) { struct tty *tty; SET_MINOR(pty_master_device.minors, PTY_MASTER_MINOR); if((tty = register_tty(MKDEV(PTY_MASTER_MAJOR, PTY_MASTER_MINOR)))) { tty->open = pty_open; tty->close = pty_close; if(register_device(CHR_DEV, &pty_master_device)) { printk("WARNING: %s(): unable to register '%s' device.\n", __FUNCTION__, pty_master_device.name); unregister_tty(tty); return; } printk("ptmx -\t\t -\ttype=UNIX98, ptys=%d\n", NR_PTYS); } else { printk("WARNING: %s(): unable to register %s.\n", __FUNCTION__, pty_master_device.name); } } #endif /* CONFIG_UNIX98_PTYS */ ================================================ FILE: drivers/char/serial.c ================================================ /* * fiwix/drivers/char/serial.c * * Copyright 2020-2022, 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 static struct fs_operations serial_driver_fsop = { 0, 0, tty_open, tty_close, tty_read, tty_write, tty_ioctl, tty_llseek, NULL, /* readdir */ NULL, /* readdir64 */ NULL, /* mmap */ tty_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 */ }; /* FIXME: this should be allocated dynamically */ struct serial serial_table[NR_SERIAL]; static struct device serial_device = { "ttyS", SERIAL_MAJOR, { 0, 0, 0, 0, 0, 0, 0, 0 }, NULL, NULL, &serial_driver_fsop, NULL, NULL, NULL }; static int isa_ioports[] = { 0x3F8, 0x2F8, 0x3E8, 0x2E8, 0 }; char *serial_chip[] = { NULL, "8250", "16450", "16550", "16550A", }; static int baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57200, 115200, 0 }; static struct serial *serial_active = NULL; static struct bh serial_bh = { 0, &irq_serial_bh, NULL }; /* FIXME: this should be allocated dynamically */ static struct interrupt irq_config_serial0 = { 0, "serial", &irq_serial, NULL }; /* ISA irq4 */ static struct interrupt irq_config_serial1 = { 0, "serial", &irq_serial, NULL }; /* ISA irq3 */ static struct interrupt irq_config_serial2 = { 0, "serial", &irq_serial, NULL }; /* first PCI device */ static int is_serial(__dev_t dev) { if(MAJOR(dev) == SERIAL_MAJOR && ((MINOR(dev) >= (1 << SERIAL_MSF) && MINOR(dev) < (1 << SERIAL_MSF) + SERIAL_MINORS))) { return 1; } return 0; } /* FIXME: this should be removed once these structures are allocated dynamically */ static struct serial *get_serial_slot(void) { int n; for(n = 0; n < NR_SERIAL; n++) { if(!(serial_table[n].flags & UART_ACTIVE)) { return &serial_table[n]; } } printk("WARNING: %s(): no more serial slots free!\n", __FUNCTION__); return NULL; } static int serial_identify(struct serial *s) { int value; /* set all features in FCR register to test the status of FIFO */ outport_b(s->ioaddr + UART_FCR, (UART_FCR_FIFO | UART_FCR_DMA | UART_FCR_FIFO64 | UART_FCR_FIFO14)); value = inport_b(s->ioaddr + UART_IIR); if(value & UART_IIR_FIFOKO) { if(value & UART_IIR_FIFO) { if(value & UART_IIR_FIFO64) { /* 16750 chip is not supported */ } else { s->flags |= UART_IS_16550A | UART_HAS_FIFO; return 4; } } else { s->flags |= UART_IS_16550; return 3; } } else { /* * At this point we know this device don't has FIFO, * the Scratch Register will help us to know the final chip. */ value = inport_b(s->ioaddr + UART_SR); /* save its value */ outport_b(s->ioaddr + UART_SR, 0xAA); /* put a random value */ if(inport_b(s->ioaddr + UART_SR) != 0xAA) { s->flags |= UART_IS_8250; return 1; } else { outport_b(s->ioaddr + UART_SR, value); /* restore it */ s->flags |= UART_IS_16450; return 2; } } return 0; } static void serial_default(struct serial *s) { s->name = "ttyS."; /* 9600,N,8,1 by default */ s->baud = 9600; s->lctrl = UART_LCR_NP | UART_LCR_WL8 | UART_LCR_1STB; } static void serial_setup(struct serial *s) { int divisor; outport_b(s->ioaddr + UART_IER, 0); /* disable all interrupts */ divisor = 115200 / s->baud; outport_b(s->ioaddr + UART_LCR, UART_LCR_DLAB); /* enable DLAB */ outport_b(s->ioaddr + UART_DLL, divisor & 0xFF); /* LSB of divisor */ outport_b(s->ioaddr + UART_DLH, divisor >> 8); /* MSB of divisor */ outport_b(s->ioaddr + UART_LCR, s->lctrl); /* line control */ } /* disable transmitter interrupts */ static void serial_stop(struct tty *tty) { struct serial *s; unsigned int flags; SAVE_FLAGS(flags); CLI(); s = (struct serial *)tty->driver_data; outport_b(s->ioaddr + UART_IER, UART_IER_RDAI); RESTORE_FLAGS(flags); } /* enable transmitter interrupts */ static void serial_start(struct tty *tty) { struct serial *s; unsigned int flags; SAVE_FLAGS(flags); CLI(); s = (struct serial *)tty->driver_data; outport_b(s->ioaddr + UART_IER, UART_IER_RDAI | UART_IER_THREI); RESTORE_FLAGS(flags); } static void serial_errors(struct serial *s, int status) { struct tty *tty; tty = s->tty; if(!(tty->termios.c_iflag & IGNBRK) && tty->termios.c_iflag & BRKINT) { if(status & UART_LSR_BI) { printk("WARNING: break interrupt in %s.\n", s->name); } } /* this includes also overrun errors */ if(!(tty->termios.c_iflag & IGNPAR) && tty->termios.c_iflag & PARMRK) { if(status & UART_LSR_OE) { printk("WARNING: overrun error in %s.\n", s->name); } else if(status & UART_LSR_PE) { printk("WARNING: parity error in %s.\n", s->name); } else if(status & UART_LSR_FE) { printk("WARNING: framing error in %s.\n", s->name); } else if(status & UART_LSR_EFIFO) { printk("WARNING: FIFO error in %s.\n", s->name); } } } static void serial_send(struct tty *tty) { unsigned char ch; struct serial *s; int count; s = (struct serial *)tty->driver_data; if(!tty->write_q.count) { outport_b(s->ioaddr + UART_IER, UART_IER_RDAI); return; } count = 0; while(tty->write_q.count > 0 && count < UART_FIFO_SIZE) { ch = charq_getchar(&tty->write_q); outport_b(s->ioaddr + UART_TD, ch); count++; } if(!tty->write_q.count) { outport_b(s->ioaddr + UART_IER, UART_IER_RDAI); } wakeup(&tty_write); } static int serial_receive(struct serial *s) { int status, errno; unsigned char ch; struct tty *tty; errno = 0; tty = s->tty; do { if(!charq_room(&tty->read_q)) { errno = -EAGAIN; break; } ch = inport_b(s->ioaddr + UART_RD); charq_putchar(&tty->read_q, ch); status = inport_b(s->ioaddr + UART_LSR); } while(status & UART_LSR_RDA); serial_bh.flags |= BH_ACTIVE; return errno; } void irq_serial(int num, struct sigcontext *sc) { struct serial *s; int status; s = serial_active; while(s) { if(s->irq == num) { while(!(inport_b(s->ioaddr + UART_IIR) & UART_IIR_NOINT)) { status = inport_b(s->ioaddr + UART_LSR); if(status & UART_LSR_RDA) { if(serial_receive(s)) { break; } } if(status & UART_LSR_THRE) { serial_send(s->tty); } serial_errors(s, status); } } s = s->next; } } int serial_open(struct tty *tty) { struct serial *s; int minor; minor = MINOR(tty->dev); if(!TEST_MINOR(serial_device.minors, minor)) { return -ENXIO; } s = (struct serial *)tty->driver_data; /* enable FIFO */ if(s->flags & UART_HAS_FIFO) { outport_b(s->ioaddr + UART_FCR, UART_FCR_FIFO | UART_FCR_FIFO14); } outport_b(s->ioaddr + UART_MCR, UART_MCR_OUT2 | UART_MCR_RTS | UART_MCR_DTR); /* enable interrupts */ outport_b(s->ioaddr + UART_IER, UART_IER_RDAI); /* clear all input registers */ inport_b(s->ioaddr + UART_RD); inport_b(s->ioaddr + UART_IIR); inport_b(s->ioaddr + UART_LSR); inport_b(s->ioaddr + UART_MSR); return 0; } int serial_close(struct tty *tty) { struct serial *s; int minor; minor = MINOR(tty->dev); if(!TEST_MINOR(serial_device.minors, minor)) { return -ENXIO; } s = (struct serial *)tty->driver_data; if(tty->count > 1) { return 0; } /* disable all interrupts */ outport_b(s->ioaddr + UART_IER, 0); /* disable FIFO */ outport_b(s->ioaddr + UART_FCR, UART_FCR_CRCVR | UART_FCR_CXMTR); /* clear all input register */ inport_b(s->ioaddr + UART_RD); return 0; } void serial_set_termios(struct tty *tty) { short int divisor; int baud, size, stop; int lctrl; struct serial *s; s = (struct serial *)tty->driver_data; lctrl = 0; if(!(baud = baud_table[tty->termios.c_cflag & CBAUD])) { return; } divisor = 115200 / baud; outport_b(s->ioaddr + UART_LCR, UART_LCR_DLAB); /* enable DLAB */ outport_b(s->ioaddr + UART_DLL, divisor & 0xFF); /* LSB of divisor */ outport_b(s->ioaddr + UART_DLH, divisor >> 8); /* MSB of divisor */ size = tty->termios.c_cflag & CSIZE; switch(size) { case CS5: lctrl = UART_LCR_WL5; break; case CS6: lctrl = UART_LCR_WL6; break; case CS7: lctrl = UART_LCR_WL7; break; case CS8: lctrl = UART_LCR_WL8; break; default: lctrl = UART_LCR_WL5; break; } stop = tty->termios.c_cflag & CSTOPB; if(stop) { lctrl |= UART_LCR_2STB; } else { lctrl |= UART_LCR_1STB; } if(tty->termios.c_cflag & PARENB) { lctrl |= UART_LCR_EP; } else if(tty->termios.c_cflag & PARODD) { lctrl |= UART_LCR_OP; } else { lctrl |= UART_LCR_NP; } /* FIXME: flow control RTSCTS no supported */ outport_b(s->ioaddr + UART_LCR, lctrl); /* line control */ } void serial_write(struct tty *tty) { struct serial *s; unsigned int flags; SAVE_FLAGS(flags); CLI(); s = (struct serial *)tty->driver_data; outport_b(s->ioaddr + UART_IER, UART_IER_RDAI | UART_IER_THREI); RESTORE_FLAGS(flags); } void irq_serial_bh(struct sigcontext *sc) { struct tty *tty; struct serial *s; s = serial_active; while(s) { tty = s->tty; if(tty->read_q.count) { if(can_lock_area(AREA_SERIAL_READ)) { tty->input(tty); unlock_area(AREA_SERIAL_READ); } else { serial_bh.flags |= BH_ACTIVE; } } s = s->next; } } static int register_serial(struct serial *s, int minor) { struct serial **sp; struct tty *tty; int n, type; serial_default(s); if((type = serial_identify(s))) { s->name[4] = '0' + minor; printk("%s 0x%04x-0x%04x %3d\ttype=%s%s\n", s->name, s->ioaddr, s->ioaddr + s->iosize - 1, s->irq, serial_chip[type], s->flags & UART_HAS_FIFO ? " FIFO=yes" : ""); SET_MINOR(serial_device.minors, (1 << SERIAL_MSF) + minor); serial_setup(s); sp = &serial_active; if(*sp) { do { sp = &(*sp)->next; } while(*sp); } if((tty = register_tty(MKDEV(SERIAL_MAJOR, (1 << SERIAL_MSF) + minor)))) { tty->driver_data = (void *)s; tty->stop = serial_stop; tty->start = serial_start; tty->deltab = tty_deltab; tty->reset = tty_reset; tty->input = do_cook; tty->output = serial_write; tty->open = serial_open; tty->close = serial_close; tty->set_termios = serial_set_termios; tty_reset(tty); for(n = 0; n < MAX_TAB_COLS; n++) { if(!(n % TAB_SIZE)) { tty->tab_stop[n] = 1; } else { tty->tab_stop[n] = 0; } } tty->count = 0; s->tty = tty; s->flags |= UART_ACTIVE; *sp = s; return 0; } else { printk("WARNING: %s(): unable to register %s.\n", __FUNCTION__, s->name); } } return 1; } #ifdef CONFIG_PCI static int setup_serial_device(int minor, struct pci_device *pci_dev) { struct serial *s; unsigned short int cmd; if(pci_dev->flags[0] & PCI_F_ADDR_SPACE_MEM) { printk("WARNING: %s(): MMIO is not supported.\n", __FUNCTION__); return minor; } if(!(s = get_serial_slot())) { return minor; } /* enable I/O space */ cmd = (pci_dev->command | PCI_COMMAND_IO); pci_write_short(pci_dev, PCI_COMMAND, cmd); s->ioaddr = pci_dev->bar[0]; s->iosize = pci_dev->size[0]; s->irq = pci_dev->irq; if(!register_serial(s, minor)) { pci_show_desc(pci_dev); if(!register_irq(s->irq, &irq_config_serial2)) { enable_irq(s->irq); } minor++; } return minor; } static int serial_pci(int minor) { struct pci_device *pci_dev; pci_dev = pci_device_table; while(pci_dev) { if(pci_dev->class == PCI_CLASS_COMMUNICATION_SERIAL) { minor = setup_serial_device(minor, pci_dev); } if(minor < NR_SERIAL) { pci_dev = pci_dev->next; continue; } break; } return minor; } #endif /* CONFIG_PCI */ static int serial_isa(void) { struct serial *s; int n, minor; for(n = 0, minor = 0; isa_ioports[n] && minor < NR_SERIAL; n++) { if(!(s = get_serial_slot())) { return minor; } s->ioaddr = isa_ioports[n]; s->iosize = 7; if(!(minor & 1)) { s->irq = SERIAL4_IRQ; } else { s->irq = SERIAL3_IRQ; } if(!(register_serial(s, minor))) { if(!minor) { if(!register_irq(SERIAL4_IRQ, &irq_config_serial0)) { enable_irq(SERIAL4_IRQ); } } if(minor == 1) { if(!register_irq(SERIAL3_IRQ, &irq_config_serial1)) { enable_irq(SERIAL3_IRQ); } } minor++; } } return minor; } void serial_init(void) { int minor, n, syscon; struct tty *tty; memset_b(serial_table, 0, sizeof(serial_table)); minor = serial_isa(); #ifdef CONFIG_PCI minor = serial_pci(minor); #endif /* CONFIG_PCI */ if(minor) { add_bh(&serial_bh); if(register_device(CHR_DEV, &serial_device)) { printk("WARNING: %s(): unable to register serial device.\n", __FUNCTION__); } /* check if a serial tty will act as a system console */ for(n = 0, syscon = 0; n < NR_SYSCONSOLES; n++) { if(is_serial(sysconsole_table[n].dev)) { if((tty = get_tty(sysconsole_table[n].dev))) { if(!syscon) { syscon = sysconsole_table[n].dev; } register_console(tty); } } } if(syscon) { /* flush early log into the first console */ tty = get_tty(syscon); flush_log_buf(tty); } } } ================================================ FILE: drivers/char/sysrq.c ================================================ /* * fiwix/drivers/char/sysrq.c * * Copyright 2021, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include static const char *pstate[] = { "?", "R", "S", "Z", "T", "D" }; static void memory(void) { char *buf; buf = (char *)kmalloc(PAGE_SIZE); data_proc_meminfo(buf, 0); printk("%s", buf); printk("\n"); data_proc_buddyinfo(buf, 0); printk("%s", buf); printk("\n"); kfree((unsigned int)buf); } static void proc_list(void) { struct proc *p; printk("USER PID PPID RSS S SLEEP_ADDR CMD\n"); FOR_EACH_PROCESS(p) { printk("%d %5d %5d %5d %s ", p->uid, p->pid, p->ppid ? p->ppid->pid : 0, p->rss << 2, pstate[p->state]); if(p->state == PROC_SLEEPING) { printk("0x%08x ", p->sleep_address); } else { printk(" "); } printk("%s\n", p->argv0); p = p->next; } printk("List of PIDs in running queue: "); FOR_EACH_PROCESS_RUNNING(p) { printk("%d ", p->pid); p = p->next_run; } printk("\n"); } void sysrq(int op) { switch(op) { case SYSRQ_STACK: printk("sysrq: Stack backtrace.\n"); stack_backtrace(); break; case SYSRQ_MEMORY: printk("sysrq: Memory information.\n"); memory(); break; case SYSRQ_TASKS: printk("sysrq: Task list.\n"); proc_list(); break; case SYSRQ_UNDEF: printk("sysrq: Undefined operation.\n"); break; } } ================================================ FILE: drivers/char/tty.c ================================================ /* * fiwix/drivers/char/tty.c * * Copyright 2018-2022, 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 #define SYSCON_DEV MKDEV(SYSCON_MAJOR, 1) #define VCONSOLE_DEV MKDEV(VCONSOLES_MAJOR, 0) #define TTY_DEV MKDEV(SYSCON_MAJOR, 0) struct tty *tty_table; extern short int current_cons; static void wait_vtime_off(unsigned int arg) { unsigned int *fn = (unsigned int *)arg; wakeup(fn); } static void termios2termio(struct termios *termios, struct termio *termio) { int n; termio->c_iflag = termios->c_iflag; termio->c_oflag = termios->c_oflag; termio->c_cflag = termios->c_cflag; termio->c_lflag = termios->c_lflag; termio->c_line = termios->c_line; for(n = 0; n < NCC; n++) { termio->c_cc[n] = termios->c_cc[n]; } } static void termio2termios(struct termio *termio, struct termios *termios) { int n; termios->c_iflag = termio->c_iflag; termios->c_oflag = termio->c_oflag; termios->c_cflag = termio->c_cflag; termios->c_lflag = termio->c_lflag; termios->c_line = termio->c_line; for(n = 0; n < NCC; n++) { termios->c_cc[n] = termio->c_cc[n]; } } static int opost(struct tty *tty, unsigned char ch) { int status; status = 0; if(tty->termios.c_oflag & OPOST) { switch(ch) { case '\n': if(tty->termios.c_oflag & ONLCR) { if(charq_room(&tty->write_q) >= 2) { charq_putchar(&tty->write_q, '\r'); tty->column = 0; } else { return -1; } } break; case '\t': while(tty->column < (tty->winsize.ws_col - 1)) { if(tty->tab_stop[++tty->column]) { break; } } break; case '\b': if(tty->column > 0) { tty->column--; } break; default: if(tty->termios.c_oflag & OLCUC) { ch = TOUPPER(ch); } if(!ISCNTRL(ch)) { tty->column++; } break; } } if(charq_putchar(&tty->write_q, ch) < 0) { status = -1; } return status; } static void out_char(struct tty *tty, unsigned char ch) { if(ISCNTRL(ch) && !ISSPACE(ch) && (tty->termios.c_lflag & ECHOCTL)) { if(tty->flags & TTY_HAS_LNEXT || (!(tty->flags & TTY_HAS_LNEXT) && ch != tty->termios.c_cc[VEOF])) { charq_putchar(&tty->write_q, '^'); charq_putchar(&tty->write_q, ch + 64); tty->column += 2; } } else { opost(tty, ch); } } static void erase_char(struct tty *tty, unsigned char erasechar) { unsigned char ch; if(erasechar == tty->termios.c_cc[VERASE]) { if((ch = charq_unputchar(&tty->cooked_q))) { if(tty->termios.c_lflag & ECHO) { charq_putchar(&tty->write_q, '\b'); charq_putchar(&tty->write_q, ' '); charq_putchar(&tty->write_q, '\b'); if(ch == '\t') { tty->deltab(tty); } if(ISCNTRL(ch) && !ISSPACE(ch) && tty->termios.c_lflag & ECHOCTL) { charq_putchar(&tty->write_q, '\b'); charq_putchar(&tty->write_q, ' '); charq_putchar(&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)) { charq_putchar(&tty->write_q, '\n'); } } } static void set_termios(struct tty *tty, struct termios *new_termios) { memcpy_b(&tty->termios, new_termios, sizeof(struct termios)); if(tty->set_termios) { tty->set_termios(tty); } } static void set_termio(struct tty *tty, struct termio *new_termio) { struct termios new_termios; termio2termios(new_termio, &new_termios); memcpy_b(&tty->termios, &new_termios, sizeof(struct termios)); } void tty_reset(struct tty *tty) { termios_reset(tty); tty->winsize.ws_row = 25; tty->winsize.ws_col = 80; tty->winsize.ws_xpixel = 0; tty->winsize.ws_ypixel = 0; tty->flags = 0; } struct tty *register_tty(__dev_t dev) { unsigned int flags; struct tty *tty, *t; int n; if(!(tty = (struct tty *)kmalloc(sizeof(struct tty)))) { return NULL; } SAVE_FLAGS(flags); CLI(); if((t = tty_table)) { for(;;) { if(t->dev == dev) { printk("ERROR: %s(): tty device %d,%d already registered!\n", __FUNCTION__, MAJOR(dev), MINOR(dev)); RESTORE_FLAGS(flags); kfree((unsigned int)tty); return NULL; } if(t->next) { t = t->next; } else { break; } } t->next = tty; } else { tty_table = tty; } RESTORE_FLAGS(flags); memset_b(tty, 0, sizeof(struct tty)); tty_reset(tty); tty->dev = dev; for(n = 0; n < MAX_TAB_COLS; n++) { if(!(n % TAB_SIZE)) { tty->tab_stop[n] = 1; } else { tty->tab_stop[n] = 0; } } return tty; } void unregister_tty(struct tty *tty) { unsigned int flags; struct tty *t; SAVE_FLAGS(flags); CLI(); if(tty == tty_table) { tty_table = tty->next; } else { t = tty_table; while(t->next != tty) { t = t->next; } t->next = tty->next; } kfree((unsigned int)tty); RESTORE_FLAGS(flags); } struct tty *get_tty(__dev_t dev) { struct tty *tty; if(!dev) { return NULL; } /* /dev/console = system console */ if(dev == SYSCON_DEV) { dev = (__dev_t)kparms.syscondev; } /* /dev/tty0 = current virtual console */ if(dev == VCONSOLE_DEV) { dev = MKDEV(VCONSOLES_MAJOR, current_cons); } /* /dev/tty = controlling TTY device */ if(dev == TTY_DEV) { if(!current->ctty) { return NULL; } dev = current->ctty->dev; } for(tty = tty_table; tty; tty = tty->next) { if(tty->dev == dev) { return tty; } } 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->sid == current->sid) { p->ctty = NULL; } p = p->next; } kill_pgrp(current->pgid, SIGHUP, KERNEL); kill_pgrp(current->pgid, SIGCONT, KERNEL); } void termios_reset(struct tty *tty) { tty->kbd.mode = K_XLATE; tty->termios.c_iflag = ICRNL | IXON | IXOFF; tty->termios.c_oflag = OPOST | ONLCR; tty->termios.c_cflag = B9600 | 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 tty_deltab(struct tty *tty) { unsigned short int col, n, count; struct cblock *cb; unsigned char ch; 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(!tty->tab_stop[++col]); } else { col++; if(ISCNTRL(ch) && !ISSPACE(ch) && tty->termios.c_lflag & ECHOCTL) { col++; } } col %= tty->winsize.ws_col; } } cb = cb->next; } count = tty->column - col; while(count--) { charq_putchar(&tty->write_q, '\b'); tty->column--; } } void do_cook(struct tty *tty) { int n; unsigned char ch; struct cblock *cb; while(tty->read_q.count > 0) { ch = charq_getchar(&tty->read_q); if((tty->termios.c_lflag & ISIG) && !(tty->flags & TTY_HAS_LNEXT)) { if(ch == tty->termios.c_cc[VINTR]) { if(!(tty->termios.c_lflag & NOFLSH)) { charq_flush(&tty->read_q); charq_flush(&tty->cooked_q); } if(tty->pgid > 0) { kill_pgrp(tty->pgid, SIGINT, KERNEL); } break; } if(ch == tty->termios.c_cc[VQUIT]) { if(tty->pgid > 0) { kill_pgrp(tty->pgid, SIGQUIT, KERNEL); } break; } if(ch == tty->termios.c_cc[VSUSP]) { if(tty->pgid > 0) { kill_pgrp(tty->pgid, SIGTSTP, KERNEL); } break; } } if(tty->termios.c_iflag & ISTRIP) { ch = TOASCII(ch); } if(tty->termios.c_iflag & IUCLC) { if(ISUPPER(ch)) { ch = TOLOWER(ch); } } if(!(tty->flags & TTY_HAS_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->flags & TTY_HAS_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); charq_putchar(&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->flags |= TTY_HAS_LNEXT; if(tty->termios.c_lflag & ECHOCTL) { charq_putchar(&tty->write_q, '^'); charq_putchar(&tty->write_q, '\b'); } break; } if(tty->termios.c_iflag & IXON) { if(ch == tty->termios.c_cc[VSTART]) { if(tty->start) { tty->start(tty); } continue; } if(ch == tty->termios.c_cc[VSTOP]) { if(tty->stop) { tty->stop(tty); } continue; } if(tty->termios.c_iflag & IXANY) { if(tty->start) { tty->start(tty); } } } } /* FIXME: using ISSPACE here makes LNEXT working incorrectly */ if(tty->termios.c_lflag & ICANON) { if(ISCNTRL(ch) && !ISSPACE(ch) && (tty->termios.c_lflag & ECHOCTL)) { out_char(tty, ch); charq_putchar(&tty->cooked_q, ch); tty->flags &= ~TTY_HAS_LNEXT; 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); } } charq_putchar(&tty->cooked_q, ch); tty->flags &= ~TTY_HAS_LNEXT; } if(tty->output) { tty->output(tty); } if(!(tty->termios.c_lflag & ICANON) || ((tty->termios.c_lflag & ICANON) && tty->canon_data)) { wakeup(&do_select); } wakeup(&tty->read_q); } int tty_open(struct inode *i, struct fd *f) { int noctty_flag; struct tty *tty, *otty; struct inode *oi; struct devpts_files *dp; int errno; noctty_flag = f->flags & O_NOCTTY; if(i->rdev == TTY_DEV) { if(!current->ctty) { return -ENXIO; } } if(i->rdev == VCONSOLE_DEV) { noctty_flag = 1; } if(!(tty = get_tty(i->rdev))) { printk("%s(): oops! (%x)\n", __FUNCTION__, i->rdev); printk("_syscondev = %x\n", kparms.syscondev); return -ENXIO; } errno = 0; if(tty->open) { if((errno = tty->open(tty)) < 0) { return errno; } } tty->count++; tty->column = 0; f->private_data = tty; #ifdef CONFIG_UNIX98_PTYS if(i->rdev == PTMX_DEV) { dp = (struct devpts_files *)tty->driver_data; oi = (struct inode *)dp->inode; if((otty = register_tty(oi->rdev))) { otty->count++; otty->driver_data = tty->driver_data; otty->flags |= TTY_PTY_LOCK; otty->link = tty; /* FIXME otty->stop = otty->start = */ otty->deltab = tty_deltab; otty->input = do_cook; otty->output = pty_wakeup_read; otty->open = pty_open; otty->close = pty_close; otty->ioctl = pty_ioctl; f->private_data = otty; } else { printk("WARNING: %s(): unable to register pty slave (%d,%d).\n", __FUNCTION__, MAJOR(oi->rdev), MINOR(oi->rdev)); return -ENOMEM; } } #endif /* CONFIG_UNIX98_PTYS */ 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 *f) { struct proc *p; struct tty *tty; int errno; tty = f->private_data; if(tty->close) { if((errno = tty->close(tty)) < 0) { return errno; } } tty->count--; if(!tty->count) { 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->ctty == tty) { p->ctty = NULL; } p = p->next; } } return 0; } int tty_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { unsigned int min; unsigned char ch; struct tty *tty; struct callout_req creq; int n; tty = f->private_data; /* only the foreground process group is allowed to read from the tty */ if(current->ctty == tty && 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, KERNEL); return -ERESTART; } n = min = 0; while(count > 0) { if(tty->kbd.mode == K_RAW || tty->kbd.mode == K_MEDIUMRAW) { n = 0; while(n < count) { if((ch = charq_getchar(&tty->read_q))) { buffer[n++] = ch; } else { break; } } if(n) { break; } } if(tty->flags & TTY_OTHER_CLOSED) { n = -EIO; break; } 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]) { charq_unputchar(&tty->cooked_q); } while(n < count) { if((ch = charq_getchar(&tty->cooked_q))) { buffer[n++] = ch; } else { break; } } break; } } } else { if(tty->termios.c_cc[VTIME] > 0) { unsigned int ini_ticks = CURRENT_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(CURRENT_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(f->flags & O_NONBLOCK) { return -EAGAIN; } if(sleep(&tty->read_q, PROC_INTERRUPTIBLE)) { return -EINTR; } } while(n < count) { if((ch = charq_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 = charq_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(f->flags & O_NONBLOCK) { n = -EAGAIN; break; } if(sleep(&tty->read_q, PROC_INTERRUPTIBLE)) { n = -EINTR; break; } if(!tty->cooked_q.count) { break; } continue; } } } else { while(tty->cooked_q.count > 0) { if(n < count) { ch = charq_getchar(&tty->cooked_q); buffer[n++] = ch; if(--tty->canon_data < 0) { tty->canon_data = 0; } } else { break; } min++; } if(min >= tty->termios.c_cc[VMIN]) { break; } } } if(f->flags & O_NONBLOCK) { n = -EAGAIN; break; } if(sleep(&tty->read_q, PROC_INTERRUPTIBLE)) { n = -EINTR; break; } } if(n) { i->i_atime = CURRENT_TIME; } return n; } int tty_write(struct inode *i, struct fd *f, const char *buffer, __size_t count) { unsigned char ch; struct tty *tty; int n; tty = f->private_data; /* only the foreground process group is allowed to write to the tty */ if(current->ctty == tty && current->pgid != tty->pgid) { if(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, KERNEL); 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(opost(tty, ch) < 0) { break; } n++; } if(tty->output) { tty->output(tty); } if(n == count) { break; } if(f->flags & O_NONBLOCK) { n = -EAGAIN; break; } if(tty->write_q.count > 0) { if(sleep(&tty->write_q, PROC_INTERRUPTIBLE)) { n = -EINTR; break; } } if(need_resched) { do_sched(); } } if(n) { i->i_mtime = CURRENT_TIME; } return n; } /* FIXME: http://www.lafn.org/~dave/linux/termios.txt (doc/termios.txt) */ int tty_ioctl(struct inode *i, struct fd *f, int cmd, unsigned int arg) { struct proc *p; struct tty *tty; int errno; tty = f->private_data; errno = 0; 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((struct termios *)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; } set_termios(tty, (struct termios *)arg); 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; } /* not tested */ while(tty->write_q.count) { if(sleep(&tty->write_q, PROC_INTERRUPTIBLE)) { return -EINTR; } do_sched(); } set_termios(tty, (struct termios *)arg); 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; } /* not tested */ while(tty->write_q.count) { if(sleep(&tty->write_q, PROC_INTERRUPTIBLE)) { return -EINTR; } do_sched(); } set_termios(tty, (struct termios *)arg); charq_flush(&tty->read_q); break; /* * Fetch and store 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; } termios2termio(&tty->termios, (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 as 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; } /* not tested */ while(tty->write_q.count) { if(sleep(&tty->write_q, PROC_INTERRUPTIBLE)) { return -EINTR; } do_sched(); } 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; } /* not tested */ while(tty->write_q.count) { if(sleep(&tty->write_q, PROC_INTERRUPTIBLE)) { return -EINTR; } do_sched(); } set_termio(tty, (struct termio *)arg); charq_flush(&tty->read_q); break; /* Perform start/stop control */ case TCXONC: switch(arg) { case TCOOFF: if(tty->stop) { tty->stop(tty); } break; case TCOON: if(tty->start) { tty->start(tty); } break; default: return -EINVAL; } break; case TCFLSH: switch(arg) { case TCIFLUSH: charq_flush(&tty->read_q); charq_flush(&tty->cooked_q); break; case TCOFLUSH: charq_flush(&tty->write_q); break; case TCIOFLUSH: charq_flush(&tty->read_q); charq_flush(&tty->cooked_q); charq_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->ctty == tty) { p->ctty = NULL; } p = p->next; } } else { return -EPERM; } } current->ctty = tty; tty->sid = current->sid; tty->pgid = current->pgid; break; /* * Get the process group ID of the '__pid_t' pointed to by * the arg to the foreground processes group ID. */ 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; /* * Associate the process pointed to by '__pid_t' in the arg to * the value of the terminal. */ 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; /* * The session ID of the terminal is fetched and stored in * the '__pid_t' pointed to by the arg. case TIOCSID: FIXME */ /* * The terminal drivers notion of terminal size is stored in * the 'winsize' structure pointed to by the arg. */ 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; /* * The terminal drivers notion of the terminal size is set * to value in the 'winsize' structure pointed to by the arg. */ 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 || tty->winsize.ws_col != ws->ws_col || tty->winsize.ws_xpixel != ws->ws_xpixel || 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, KERNEL); } } 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; } case TIOCINQ: { int *val = (int *)arg; if(tty->termios.c_lflag & ICANON) { *val = tty->cooked_q.count; } else { *val = tty->read_q.count; } break; } default: errno = vt_ioctl(tty, cmd, arg); if(tty->ioctl) { errno = tty->ioctl(tty, f, cmd, arg); } } return errno; } __loff_t tty_llseek(struct inode *i, __loff_t offset) { return -ESPIPE; } int tty_select(struct inode *i, struct fd *f, int flag) { struct tty *tty; tty = f->private_data; 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; } } if(tty->read_q.count > 0) { return 1; } break; case SEL_W: if(!tty->write_q.count) { return 1; } break; } return 0; } void tty_init(void) { charq_init(); tty_table = NULL; } ================================================ FILE: drivers/char/vt.c ================================================ /* * fiwix/drivers/char/vt.c * * Copyright 2018-2021, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include int kbdmode = 0; int vt_ioctl(struct tty *tty, int cmd, unsigned 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; /* FIXME: implement KDGKBLED and KDSKBLED * it will need to convert 'scrlock, numlock, capslock' into led_flags. */ 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) { video.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 KDGKBMODE: if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned char)))) { return errno; } memset_b((void *)arg, tty->kbd.mode, sizeof(unsigned char)); break; case KDSKBMODE: if(arg != K_RAW && arg != K_XLATE && arg != K_MEDIUMRAW) { arg = K_XLATE; } tty->kbd.mode = arg; charq_flush(&tty->read_q); 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) || IS_SUPERUSER) { if(current_cons == MINOR(tty->dev)) { break; } if(!arg || arg > NR_VCONSOLES) { return -ENXIO; } printk("ACTIVATING another tty!! (cmd = 0x%x)\n", cmd); } else { return -EPERM; } break; default: return -EINVAL; } return 0; } ================================================ FILE: drivers/pci/Makefile ================================================ # fiwix/drivers/pci/Makefile # # Copyright 2022, Jordi Sanfeliu. All rights reserved. # Distributed under the terms of the Fiwix License. # .c.o: $(CC) $(CFLAGS) -c -o $@ $< OBJS = pci.o all: $(OBJS) clean: rm -f *.o ================================================ FILE: drivers/pci/pci.c ================================================ /* * fiwix/drivers/pci/pci.c * * Copyright 2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #ifdef CONFIG_PCI struct pci_device *pci_device_table; /* supported device classes only */ static const char *get_strclass(unsigned short int class) { switch(class) { case PCI_CLASS_STORAGE_IDE: return "IDE interface"; case PCI_CLASS_DISPLAY_VGA: return "VGA Display controller"; case PCI_CLASS_COMMUNICATION_SERIAL: return "Serial controller"; } return NULL; } static const char *get_strvendor_id(unsigned short int vendor_id) { #ifdef CONFIG_PCI_NAMES switch(vendor_id) { case PCI_VENDOR_ID_BOCHS: return "QEMU"; } #endif /* CONFIG_PCI_NAMES */ return NULL; } static const char *get_strdevice_id(unsigned short int device_id) { #ifdef CONFIG_PCI_NAMES switch(device_id) { case PCI_DEVICE_ID_BGA: return "Bochs Graphics Adapter"; } #endif /* CONFIG_PCI_NAMES */ return NULL; } static unsigned int get_addr(int bus, int dev, int func, int offset) { unsigned int addr; addr = (unsigned int)(0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (offset & 0xFC)); return addr; } static int is_mechanism_1_supported(void) { unsigned int orig, value; orig = inport_l(PCI_ADDRESS); outport_l(PCI_ADDRESS, 0x80000000); value = inport_l(PCI_ADDRESS); if(value == 0x80000000) { return 1; } outport_l(PCI_ADDRESS, orig); return 0; } static void add_pci_device(struct pci_device *pci_dev) { struct pci_device *pdt; if(!(pdt = (struct pci_device *)kmalloc(sizeof(struct pci_device)))) { return; } *pdt = *pci_dev; if(!pci_device_table) { pci_device_table = pdt; } else { pdt->prev = pci_device_table->prev; pci_device_table->prev->next = pdt; } pci_device_table->prev = pdt; } static void scan_bus(void) { int n, b, d, f; unsigned int vendor_id, device_id, class; unsigned char hdr_type, irq, prog_if; struct pci_device pci_dev, *pd; unsigned int reg, addr, val; unsigned short int cmd; for(b = 0; b < PCI_MAX_BUS; b++) { for(d = 0; d < PCI_MAX_DEV; d++) { for(f = 0; f < PCI_MAX_FUNC; f++) { memset_b(&pci_dev, 0, sizeof(struct pci_device)); pci_dev.bus = b; pci_dev.dev = d; pci_dev.func = f; vendor_id = pci_read_short(&pci_dev, PCI_VENDOR_ID); if(!vendor_id || vendor_id == 0xFFFF) { continue; } device_id = pci_read_short(&pci_dev, PCI_DEVICE_ID); prog_if = pci_read_char(&pci_dev, PCI_PROG_IF); class = pci_read_short(&pci_dev, PCI_CLASS_DEVICE); hdr_type = pci_read_char(&pci_dev, PCI_HEADER_TYPE); irq = pci_read_char(&pci_dev, PCI_INTERRUPT_LINE); printk("\t b:%02x d:%02x f:%d", b, d, f); if(irq) { printk(" %3d", irq); } else { printk(" -"); } printk("\t%04x:%04x %04x%02x", vendor_id, device_id, class, (int)prog_if); pci_dev.name = get_strclass(class); printk(" - %s\n", pci_dev.name ? pci_dev.name : "Unknown"); pci_dev.vendor_id = vendor_id; pci_dev.device_id = device_id; pci_dev.class = class; pci_dev.hdr_type = hdr_type; pci_dev.irq = irq; pci_dev.command = pci_read_short(&pci_dev, PCI_COMMAND); pci_dev.status = pci_read_short(&pci_dev, PCI_STATUS); pci_dev.rev = pci_read_char(&pci_dev, PCI_REVISION_ID); pci_dev.prog_if = prog_if; pci_dev.cline_size = pci_read_char(&pci_dev, PCI_CACHE_LINE_SIZE); pci_dev.latency = pci_read_char(&pci_dev, PCI_LATENCY_TIMER); pci_dev.bist = pci_read_char(&pci_dev, PCI_BIST); pci_dev.pin = pci_read_char(&pci_dev, PCI_INTERRUPT_PIN); pci_dev.min_gnt = pci_read_char(&pci_dev, PCI_MIN_GRANT); pci_dev.max_lat = pci_read_char(&pci_dev, PCI_MAX_LATENCY); add_pci_device(&pci_dev); if(!f && !(hdr_type & 0x80)) { break; /* no more functions in this device */ } } } } /* get BARs */ pd = pci_device_table; while(pd) { /* only normal PCI devices (bridges are not supported) */ if(pd->hdr_type != PCI_HEADER_TYPE_NORMAL) { pd = pd->next; continue; } /* disable I/O space and address space */ cmd = pci_read_short(pd, PCI_COMMAND); pci_write_short(pd, PCI_COMMAND, cmd & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER)); /* read up to 6 BARs */ for(n = 0; n < 6; n++) { reg = PCI_BASE_ADDR_0 + (n * 4); /* save the original value */ addr = pci_read_long(pd, reg); pci_write_long(pd, reg, ~0); val = pci_read_long(pd, reg); /* restore the original value */ pci_write_long(pd, reg, addr); if(!val || val == 0xFFFFFFFF) { continue; } pd->obar[n] = addr; if(addr & PCI_BASE_ADDR_SPACE_IO) { addr &= PCI_BASE_ADDR_IO_MASK; pd->flags[n] |= PCI_F_ADDR_SPACE_IO; val = (~(val & ~0x1)) + 1; } else { /* memory mapped */ if(addr & PCI_BASE_ADDR_MEM_PREF) { pd->flags[n] |= PCI_F_ADDR_SPACE_PREFET; } if((addr & PCI_BASE_ADDR_TYPE_MASK) == PCI_BASE_ADDR_TYPE_32) { pd->flags[n] |= PCI_F_ADDR_MEM_32; } if((addr & PCI_BASE_ADDR_TYPE_MASK) == PCI_BASE_ADDR_TYPE_64) { pd->flags[n] |= PCI_F_ADDR_MEM_64; } addr &= PCI_BASE_ADDR_MEM_MASK; pd->flags[n] |= PCI_F_ADDR_SPACE_MEM; val = (~(val & ~0xF)) + 1; } pd->bar[n] = addr; pd->size[n] = val; } /* restore I/O space and address space */ pci_write_short(pd, PCI_COMMAND, cmd); pd = pd->next; } } unsigned char pci_read_char(struct pci_device *pci_dev, int offset) { unsigned char retval; outport_l(PCI_ADDRESS, get_addr(pci_dev->bus, pci_dev->dev, pci_dev->func, offset)); retval = inport_l(PCI_DATA) >> ((offset & 3) * 8) & 0xFF; return retval; } unsigned short int pci_read_short(struct pci_device *pci_dev, int offset) { unsigned short int retval; outport_l(PCI_ADDRESS, get_addr(pci_dev->bus, pci_dev->dev, pci_dev->func, offset)); retval = inport_l(PCI_DATA) >> ((offset & 2) * 8) & 0xFFFF; return retval; } unsigned int pci_read_long(struct pci_device *pci_dev, int offset) { unsigned int retval; outport_l(PCI_ADDRESS, get_addr(pci_dev->bus, pci_dev->dev, pci_dev->func, offset)); retval = inport_l(PCI_DATA) >> ((offset & 2) * 8); return retval; } void pci_write_char(struct pci_device *pci_dev, int offset, unsigned char buf) { outport_l(PCI_ADDRESS, get_addr(pci_dev->bus, pci_dev->dev, pci_dev->func, offset)); outport_b(PCI_DATA, buf); } void pci_write_short(struct pci_device *pci_dev, int offset, unsigned short int buf) { outport_l(PCI_ADDRESS, get_addr(pci_dev->bus, pci_dev->dev, pci_dev->func, offset)); outport_w(PCI_DATA, buf); } void pci_write_long(struct pci_device *pci_dev, int offset, unsigned int buf) { outport_l(PCI_ADDRESS, get_addr(pci_dev->bus, pci_dev->dev, pci_dev->func, offset)); outport_l(PCI_DATA, buf); } void pci_show_desc(struct pci_device *pci_dev) { const char *name; printk("\t\t\t\tpci=b:%02x d:%02x f:%d, rev=0x%02x\n", pci_dev->bus, pci_dev->dev, pci_dev->func, pci_dev->rev); if((name = get_strdevice_id(pci_dev->device_id))) { printk("\t\t\t\t"); printk("desc=%s ", name); if((name = get_strvendor_id(pci_dev->vendor_id))) { printk("(%s)", name); } printk("\n"); } } void pci_init(void) { if(!is_mechanism_1_supported()) { return; } pci_device_table = NULL; printk("pci 0x%04x-0x%04x", PCI_ADDRESS, PCI_DATA + sizeof(unsigned int) - 1); printk(" -\tbus range=0-%d, configuration type=1\n", PCI_MAX_BUS - 1); scan_bus(); } #endif /* CONFIG_PCI */ ================================================ FILE: drivers/video/Makefile ================================================ # fiwix/drivers/video/Makefile # # Copyright 2021-2022, Jordi Sanfeliu. All rights reserved. # Distributed under the terms of the Fiwix License. # .c.o: $(CC) $(CFLAGS) -c -o $@ $< OBJS = video.o vgacon.o fbcon.o bga.o fonts.o all: $(OBJS) clean: rm -f *.o ================================================ FILE: drivers/video/bga.c ================================================ /* * fiwix/drivers/video/bga.c * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_PCI #ifdef CONFIG_BGA static void bga_write_register(int index, int data) { outport_w(VBE_DISPI_IOPORT_INDEX, index); outport_w(VBE_DISPI_IOPORT_DATA, data); } static unsigned short int bga_read_register(unsigned short int cmd) { outport_w(VBE_DISPI_IOPORT_INDEX, cmd); return inport_w(VBE_DISPI_IOPORT_DATA); } static int setup_bga_device(struct pci_device *pci_dev) { unsigned short int cmd; int xres, yres, bpp; if(!(*kparms.bgaresolution)) { return 0; } /* prepare to switch to the resolution requested */ bga_write_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED); bga_write_register(VBE_DISPI_INDEX_XRES, video.fb_width); bga_write_register(VBE_DISPI_INDEX_YRES, video.fb_height); bga_write_register(VBE_DISPI_INDEX_BPP, video.fb_bpp); /* check if the values were accepted */ xres = bga_read_register(VBE_DISPI_INDEX_XRES); yres = bga_read_register(VBE_DISPI_INDEX_YRES); bpp = bga_read_register(VBE_DISPI_INDEX_BPP); if(xres != video.fb_width || yres != video.fb_height || bpp != video.fb_bpp) { printk("WARNING: %s(): resolution %dx%dx%d is not valid.\n", __FUNCTION__, video.fb_width, video.fb_height, video.fb_bpp); return 0; } /* enable I/O space and memory space */ cmd = (pci_dev->command | PCI_COMMAND_IO | PCI_COMMAND_MEMORY); pci_write_short(pci_dev, PCI_COMMAND, cmd); video.pci_dev = pci_dev; video.address = (unsigned int *)pci_dev->bar[0]; video.port = 0; strcpy((char *)video.signature, "BGA"); video.fb_version = bga_read_register(VBE_DISPI_INDEX_ID); if(video.fb_version < VBE_DISPI_ID4) { video.memsize = 4 * 1024 * 1024; /* 4MB */ } else if(video.fb_version == VBE_DISPI_ID4) { video.memsize = 8 * 1024 * 1024; /* 8MB */ } else { video.memsize = bga_read_register(VBE_DISPI_INDEX_VIDEO_MEMORY_64K); video.memsize *= 64 * 1024; } video.fb_pixelwidth = video.fb_bpp / 8; video.fb_pitch = video.fb_width * video.fb_pixelwidth; video.fb_linesize = video.fb_pitch * video.fb_char_height; video.fb_size = video.fb_width * video.fb_height * video.fb_pixelwidth; video.fb_vsize = video.lines * video.fb_pitch * video.fb_char_height; map_kaddr(kpage_dir, (unsigned int)video.address, (unsigned int)video.address + video.memsize, 0, PAGE_PRESENT | PAGE_RW); bga_write_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED); return 1; } void bga_init(void) { struct pci_device *pci_dev; pci_dev = pci_device_table; while(pci_dev) { if(pci_dev->class == PCI_CLASS_DISPLAY_VGA) { if(setup_bga_device(pci_dev)) { /* only one device is supported */ break; } } pci_dev = pci_dev->next; } } #endif /* CONFIG_BGA */ #endif /* CONFIG_PCI */ ================================================ FILE: drivers/video/fbcon.c ================================================ /* * fiwix/drivers/video/fbcon.c * * Copyright 2021-2022, Jordi Sanfeliu. All rights reserved. * Portions Copyright 2024, Greg Haerr. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include #include #define SPACE_CHAR 32 unsigned char *font_data; unsigned char *cursor_shape; static unsigned char screen_is_off = 0; /* RGB colors */ static int color_table[] = { 0x000000, /* black */ 0x0000AA, /* blue */ 0x00AA00, /* green */ 0x00AAAA, /* cyan */ 0xAA0000, /* red */ 0xAA00AA, /* magenta */ 0xAA5000, /* brown */ 0xAAAAAA, /* gray */ 0x555555, /* dark gray */ 0x5555FF, /* light blue */ 0x55FF55, /* light green */ 0x55FFFF, /* light cyan */ 0xFF5555, /* light red */ 0xFF55FF, /* light magenta */ 0xFFFF55, /* yellow */ 0xFFFFFF, /* white */ }; static int get_fg_color(unsigned char color) { int fg, bright; fg = color & 7; bright = (color & 0xF) & 8; return color_table[bright + fg]; } static int get_bg_color(unsigned char color) { int bg; bg = (color >> 4) & 7; return color_table[bg]; } static void set_color(void *addr, int color) { unsigned int *addr32; unsigned short int *addr16; unsigned char *addr8; short int r, g, b; switch(video.fb_bpp) { case 32: addr32 = (unsigned int *)addr; *addr32 = color; break; case 24: addr8 = (unsigned char *)addr; *(addr8++) = color & 0xFF; *(addr8++) = (color >> 8) & 0xFF; *(addr8++) = (color >> 16) & 0xFF; break; case 16: /* 0:5:6:5 */ r = ((color >> 16) & 0xFF) << 8; g = ((color >> 8) & 0xFF) << 8; b = (color & 0xFF) << 8; addr16 = (unsigned short int *)addr; *addr16 = (r & 0xf800) | ((g & 0xfc00) >> 5) | ((b & 0xf800) >> 11); break; case 15: /* 1:5:5:5 */ r = ((color >> 16) & 0xFF) << 8; g = ((color >> 8) & 0xFF) << 8; b = (color & 0xFF) << 8; addr16 = (unsigned short int *)addr; *addr16 = ((r & 0xf800) >> 1) | ((g & 0xf800) >> 6) | ((b & 0xf800) >> 11); break; } } static void draw_glyph(unsigned char *addr, int x, int y, unsigned char *ch, int color) { int n, b, offset; if(screen_is_off) { return; } offset = (y * video.fb_linesize) + (x * video.fb_bpp); addr += offset; for(n = 0; n < video.fb_char_height; n++) { if(*(ch + n) == 0) { if(ch == cursor_shape) { addr += video.fb_pitch; continue; } b = video.fb_char_width - 1; do { set_color(addr, get_bg_color(color)); addr += video.fb_pixelwidth; b--; } while(b >= 0); } else { b = video.fb_char_width - 1; do { if(*(ch + n) & (1 << b)) { set_color(addr, get_fg_color(color)); } else { set_color(addr, get_bg_color(color)); } addr += video.fb_pixelwidth; b--; } while(b >= 0); } addr += (video.fb_width - video.fb_char_width) * video.fb_pixelwidth; } } static void remove_cursor(struct vconsole *vc) { int soffset; unsigned char *vidmem, *ch; short int *screen, sch; vidmem = vc->vidmem; screen = vc->screen; soffset = (vc->cursor_y * vc->columns) + vc->cursor_x; sch = screen[soffset]; if(sch & 0xFF) { ch = &font_data[(sch & 0xFF) * video.fb_char_height]; } else { ch = &font_data[SPACE_CHAR * video.fb_char_height]; } draw_glyph(vidmem, vc->cursor_x, vc->cursor_y, ch, sch >> 8); } static void draw_cursor(struct vconsole *vc) { unsigned char *vidmem; vidmem = vc->vidmem; draw_glyph(vidmem, vc->x, vc->y, cursor_shape, DEF_MODE >> 8); } void fbcon_put_char(struct vconsole *vc, unsigned char ch) { short int *screen; unsigned char *vidmem; screen = vc->screen; if(!(vc->flags & CONSOLE_HAS_FOCUS)) { screen[(vc->y * vc->columns) + vc->x] = vc->color_attr | ch; return; } vidmem = vc->vidmem; draw_glyph(vidmem, vc->x, vc->y, &font_data[ch * video.fb_char_height], vc->color_attr >> 8); screen[(vc->y * vc->columns) + vc->x] = vc->color_attr | ch; vcbuf[(video.buf_y * vc->columns) + vc->x] = vc->color_attr | ch; } void fbcon_insert_char(struct vconsole *vc) { int n, soffset; short int tmp, slast_ch; unsigned char *vidmem, *last_ch; short int *screen; vidmem = vc->vidmem; screen = vc->screen; soffset = (vc->y * vc->columns) + vc->x; n = vc->x; last_ch = &font_data[SPACE_CHAR * video.fb_char_height]; slast_ch = BLANK_MEM; while(n < vc->columns) { tmp = screen[soffset]; if(vc->flags & CONSOLE_HAS_FOCUS) { draw_glyph(vidmem, n, vc->y, last_ch, vc->color_attr >> 8); if(tmp & 0xFF) { last_ch = &font_data[(tmp & 0xFF) * video.fb_char_height]; } else { last_ch = &font_data[SPACE_CHAR * video.fb_char_height]; } } memset_w(screen + soffset, slast_ch, 1); slast_ch = tmp; soffset++; n++; } } void fbcon_delete_char(struct vconsole *vc) { int n, soffset; short int sch; unsigned char *vidmem, *ch; short int *screen; vidmem = vc->vidmem; screen = vc->screen; soffset = (vc->y * vc->columns) + vc->x; n = vc->x; while(n < vc->columns) { sch = screen[soffset + 1]; if(vc->flags & CONSOLE_HAS_FOCUS) { if(sch & 0xFF) { ch = &font_data[(sch & 0xFF) * video.fb_char_height]; } else { ch = &font_data[SPACE_CHAR * video.fb_char_height]; } draw_glyph(vidmem, n, vc->y, ch, vc->color_attr >> 8); } memset_w(screen + soffset, sch, 1); soffset++; n++; } memset_w(screen + soffset, BLANK_MEM, 1); } void fbcon_update_curpos(struct vconsole *vc) { if(!(vc->flags & CONSOLE_HAS_FOCUS)) { return; } /* remove old cursor */ if(vc->x != vc->cursor_x || vc->y != vc->cursor_y) { remove_cursor(vc); } if(video.flags & VPF_CURSOR_ON) { draw_cursor(vc); } vc->cursor_x = vc->x; vc->cursor_y = vc->y; } void fbcon_show_cursor(struct vconsole *vc, int mode) { switch(mode) { case COND: if(!(video.flags & VPF_CURSOR_ON)) { break; } /* fall through */ case ON: video.flags |= VPF_CURSOR_ON; fbcon_update_curpos(vc); break; case OFF: video.flags &= ~VPF_CURSOR_ON; fbcon_update_curpos(vc); break; } } void fbcon_get_curpos(struct vconsole *vc) { /* not used */ } void fbcon_write_screen(struct vconsole *vc, int from, int count, short int color) { int n, n2, lines, columns, x, y; unsigned char *vidmem, *ch; short int *screen; screen = vc->screen; if(!(vc->flags & CONSOLE_HAS_FOCUS)) { memset_w(screen + from, color, count); return; } vidmem = vc->vidmem; ch = &font_data[SPACE_CHAR * video.fb_char_height]; x = from % vc->columns; y = from / vc->columns; lines = count / vc->columns; columns = x + count; if(!lines) { lines = 1; } if(!columns) { columns = vc->columns; } for(n = 0; n < lines; n++) { for(n2 = x; n2 < columns; n2++) { draw_glyph(vidmem, n2, y + n, ch, color >> 8); } x = 0; columns = vc->columns; } memset_w(screen + from, color, count); } void fbcon_blank_screen(struct vconsole *vc) { unsigned char *vidmem; if(vc->flags & CONSOLE_BLANKED) { return; } vidmem = vc->vidmem; if(!(int)vidmem) { return; } memset_b(vidmem, 0, video.fb_size); vc->flags |= CONSOLE_BLANKED; fbcon_show_cursor(vc, OFF); } void fbcon_scroll_screen(struct vconsole *vc, int top, int mode) { int soffset, poffset, count; int x, y; short int *screen, sch, pch; unsigned char *vidmem, *ch; vidmem = vc->vidmem; screen = vc->screen; if(!top) { top = vc->top; } switch(mode) { case SCROLL_UP: if(vc->flags & CONSOLE_HAS_FOCUS) { for(y = top + 1; y < vc->lines; y++) { for(x = 0; x < vc->columns; x++) { soffset = (y * vc->columns) + x; poffset = ((y - 1) * vc->columns) + x; sch = screen[soffset]; pch = screen[poffset]; if(sch == pch) { continue; } if(sch & 0xFF) { ch = &font_data[(sch & 0xFF) * video.fb_char_height]; } else { ch = &font_data[SPACE_CHAR * video.fb_char_height]; } draw_glyph(vidmem, x, y - 1, ch, sch >> 8); } } if(!screen_is_off) { count = video.fb_pitch * video.fb_char_height; memset_l(vidmem + (vc->lines - 1) * count, 0, count / sizeof(unsigned int)); } } count = vc->columns * (vc->lines - top - 1); soffset = top * vc->columns; top = (top + 1) * vc->columns; if(vc->cursor_y) { vc->cursor_y--; } memcpy_w(screen + soffset, screen + top, count); memset_w(screen + soffset + count, BLANK_MEM, top); break; case SCROLL_DOWN: for(y = vc->lines - 2; y >= top; y--) { for(x = 0; x < vc->columns; x++) { if(vc->flags & CONSOLE_HAS_FOCUS) { soffset = (y * vc->columns) + x; poffset = ((y + 1) * vc->columns) + x; sch = screen[soffset]; pch = screen[poffset]; if(sch == pch) { continue; } if(sch & 0xFF) { ch = &font_data[(sch & 0xFF) * video.fb_char_height]; } else { ch = &font_data[SPACE_CHAR * video.fb_char_height]; } draw_glyph(vidmem, x, y + 1, ch, sch >> 8); } } memcpy_w(screen + (vc->columns * (y + 1)), screen + (vc->columns * y), vc->columns); } if((vc->flags & CONSOLE_HAS_FOCUS) && !screen_is_off) { count = video.fb_pitch * video.fb_char_height; memset_l(vidmem + (top * count), 0, count / sizeof(unsigned int)); } memset_w(screen + (top * vc->columns), BLANK_MEM, vc->columns); break; } return; } void fbcon_restore_screen(struct vconsole *vc) { int x, y; short int *screen, sch; unsigned char *vidmem, *ch, c; vidmem = vc->vidmem; screen = vc->screen; if(!screen_is_off && !video.buf_top) { memset_b(vidmem, 0, video.fb_size); } for(y = 0; y < video.lines; y++) { for(x = 0; x < vc->columns; x++) { sch = screen[(y * vc->columns) + x]; c = sch & 0xFF; if(!c || (c == SPACE_CHAR && !(sch >> 8))) { continue; } ch = &font_data[c * video.fb_char_height]; draw_glyph(vidmem, x, y, ch, sch >> 8); } } vc->flags &= ~CONSOLE_BLANKED; } void fbcon_screen_on(struct vconsole *vc) { unsigned int flags; struct callout_req creq; if(screen_is_off) { screen_is_off = 0; SAVE_FLAGS(flags); CLI(); fbcon_restore_screen(vc); fbcon_update_curpos(vc); RESTORE_FLAGS(flags); vc->flags &= ~CONSOLE_BLANKED; } if(BLANK_INTERVAL) { creq.fn = fbcon_screen_off; creq.arg = (unsigned int)vc; add_callout(&creq, BLANK_INTERVAL); } } void fbcon_screen_off(unsigned int arg) { struct vconsole *vc; unsigned int flags; vc = (struct vconsole *)arg; screen_is_off = 1; SAVE_FLAGS(flags); CLI(); fbcon_blank_screen(vc); RESTORE_FLAGS(flags); } void fbcon_buf_scroll(struct vconsole *vc, int mode) { short int sch; int y, x, offset; unsigned char *vidmem, *ch; if(video.buf_y <= SCREEN_LINES) { return; } vidmem = vc->vidmem; if(mode == SCROLL_UP) { if(video.buf_top < 0) { return; } if(!video.buf_top) { video.buf_top = (video.buf_y - SCREEN_LINES + 1) * SCREEN_COLS; } video.buf_top -= (SCREEN_LINES / 2) * SCREEN_COLS; if(video.buf_top < 0) { video.buf_top = 0; } for(offset = 0, y = 0; y < video.lines; y++) { for(x = 0; x < vc->columns; x++, offset++) { sch = vcbuf[video.buf_top + offset]; if(sch & 0xFF) { ch = &font_data[(sch & 0xFF) * video.fb_char_height]; } else { ch = &font_data[SPACE_CHAR * video.fb_char_height]; } draw_glyph(vidmem, x, y, ch, sch >> 8); } } if(!video.buf_top) { video.buf_top = -1; } fbcon_show_cursor(vc, OFF); return; } if(mode == SCROLL_DOWN) { if(!video.buf_top) { return; } if(video.buf_top == video.buf_y * SCREEN_COLS) { return; } if(video.buf_top < 0) { video.buf_top = 0; } video.buf_top += (SCREEN_LINES / 2) * SCREEN_COLS; if(video.buf_top >= (video.buf_y - SCREEN_LINES + 1) * SCREEN_COLS) { video.buf_top = (video.buf_y - SCREEN_LINES + 1) * SCREEN_COLS; } for(offset = 0, y = 0; y < video.lines; y++) { for(x = 0; x < vc->columns; x++, offset++) { sch = vcbuf[video.buf_top + offset]; if(sch & 0xFF) { ch = &font_data[(sch & 0xFF) * video.fb_char_height]; } else { ch = &font_data[SPACE_CHAR * video.fb_char_height]; } draw_glyph(vidmem, x, y, ch, sch >> 8); } } if(video.buf_top >= (video.buf_y - SCREEN_LINES + 1) * SCREEN_COLS) { fbcon_show_cursor(vc, ON); fbcon_update_curpos(vc); } return; } } void fbcon_cursor_blink(unsigned int arg) { struct vconsole *vc; struct callout_req creq; static int blink_on = 0; vc = (struct vconsole *)arg; if(!(vc->flags & CONSOLE_HAS_FOCUS)) { return; } if(video.flags & VPF_CURSOR_ON && !screen_is_off) { if(blink_on) { draw_cursor(vc); } else { remove_cursor(vc); } } blink_on = !blink_on; creq.fn = fbcon_cursor_blink; creq.arg = arg; add_callout(&creq, 25); /* 250ms */ } void fbcon_init(void) { struct fbcon_font_desc *font_desc; map_kaddr(kpage_dir, (unsigned int)video.address, (unsigned int)video.address + video.memsize, 0, PAGE_PRESENT | PAGE_RW); /* some parameters already set in multiboot.c */ video.put_char = fbcon_put_char; video.insert_char = fbcon_insert_char; video.delete_char = fbcon_delete_char; video.update_curpos = fbcon_update_curpos; video.show_cursor = fbcon_show_cursor; video.get_curpos = fbcon_get_curpos; video.write_screen = fbcon_write_screen; video.blank_screen = fbcon_blank_screen; video.scroll_screen = fbcon_scroll_screen; video.restore_screen = fbcon_restore_screen; video.screen_on = fbcon_screen_on; video.buf_scroll = fbcon_buf_scroll; video.cursor_blink = fbcon_cursor_blink; if(!(font_desc = fbcon_find_font(video.fb_char_height))) { font_desc = fbcon_find_font(16); } font_data = font_desc->data; cursor_shape = font_desc->cursorshape; } ================================================ FILE: drivers/video/font-lat9-8x14.c ================================================ #include /* refer to 'fiwix/docs/video/' for a visual representation of these glyphs */ static unsigned char fontdata_8x14[] = { /* 0x00 */ 0x00, 0x7e, 0xc3, 0x99, 0x99, 0xf3, 0xe7, 0xe7, 0xff, 0xe7, 0xe7, 0x7e, 0x00, 0x00, /* 0x01 */ 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x02 */ 0x00, 0x6e, 0xd8, 0xd8, 0xd8, 0xd8, 0xde, 0xd8, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00, /* 0x03 */ 0x00, 0x00, 0x00, 0x00, 0x6e, 0xdb, 0xdb, 0xdf, 0xd8, 0xdb, 0x6e, 0x00, 0x00, 0x00, /* 0x04 */ 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, /* 0x05 */ 0x00, 0x88, 0x88, 0xf8, 0x88, 0x88, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, /* 0x06 */ 0x00, 0xf8, 0x80, 0xe0, 0x80, 0x80, 0x00, 0x3e, 0x20, 0x38, 0x20, 0x20, 0x00, 0x00, /* 0x07 */ 0x00, 0x78, 0x80, 0x80, 0x80, 0x78, 0x00, 0x3c, 0x22, 0x3e, 0x24, 0x22, 0x00, 0x00, /* 0x08 */ 0x00, 0x80, 0x80, 0x80, 0x80, 0xf8, 0x00, 0x3e, 0x20, 0x38, 0x20, 0x20, 0x00, 0x00, /* 0x09 */ 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, /* 0x0a */ 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, /* 0x0b */ 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, /* 0x0c */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x0d */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x0e */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0f */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, /* 0x10 */ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, /* 0x11 */ 0x00, 0x88, 0xc8, 0xa8, 0x98, 0x88, 0x00, 0x20, 0x20, 0x20, 0x20, 0x3e, 0x00, 0x00, /* 0x12 */ 0x00, 0x88, 0x88, 0x50, 0x50, 0x20, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, /* 0x13 */ 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, /* 0x14 */ 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, /* 0x15 */ 0x00, 0x00, 0x00, 0x06, 0x0c, 0xfe, 0x38, 0xfe, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, /* 0x16 */ 0x00, 0x00, 0x02, 0x0e, 0x3e, 0x7e, 0xfe, 0x7e, 0x3e, 0x0e, 0x02, 0x00, 0x00, 0x00, /* 0x17 */ 0x00, 0x00, 0x80, 0xe0, 0xf0, 0xfc, 0xfe, 0xfc, 0xf0, 0xe0, 0x80, 0x00, 0x00, 0x00, /* 0x18 */ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, /* 0x19 */ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, /* 0x1a */ 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1b */ 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1c */ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, /* 0x1d */ 0x00, 0x00, 0x00, 0x00, 0x28, 0x6c, 0xfe, 0x6c, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1e */ 0x00, 0x00, 0x06, 0x06, 0x36, 0x66, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1f */ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x7c, 0x6e, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, /* 0x20 (' ') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x21 ('!') */ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, /* 0x22 ('"') */ 0x00, 0x00, 0x36, 0x36, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x23 ('#') */ 0x00, 0x00, 0x6c, 0xfe, 0x6c, 0x6c, 0xfe, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24 ('$') */ 0x00, 0x00, 0x10, 0x7c, 0xd6, 0x70, 0x38, 0x1c, 0xd6, 0x7c, 0x10, 0x00, 0x00, 0x00, /* 0x25 ('%') */ 0x00, 0x00, 0x00, 0x00, 0x62, 0x66, 0x0c, 0x18, 0x30, 0x66, 0xc6, 0x00, 0x00, 0x00, /* 0x26 ('&') */ 0x00, 0x00, 0x38, 0x6c, 0x38, 0x38, 0x76, 0xf6, 0xce, 0xcc, 0x76, 0x00, 0x00, 0x00, /* 0x27 (''') */ 0x00, 0x1c, 0x1c, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28 ('(') */ 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, /* 0x29 (')') */ 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, /* 0x2a ('*') */ 0x00, 0x00, 0x00, 0x00, 0x6c, 0x38, 0xfe, 0x38, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2b ('+') */ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2c (',') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x18, 0x00, 0x00, /* 0x2d ('-') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2e ('.') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, /* 0x2f ('/') */ 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, /* 0x30 ('0') */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xd6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0x31 ('1') */ 0x00, 0x00, 0x18, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, /* 0x32 ('2') */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, /* 0x33 ('3') */ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0x34 ('4') */ 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, /* 0x35 ('5') */ 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0x36 ('6') */ 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0x37 ('7') */ 0x00, 0x00, 0xfe, 0xc6, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, /* 0x38 ('8') */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0x39 ('9') */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0x3a (':') */ 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, /* 0x3b (';') */ 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x18, 0x00, 0x00, /* 0x3c ('<') */ 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, /* 0x3d ('=') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3e ('>') */ 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, /* 0x3f ('?') */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, /* 0x40 ('@') */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7e, 0x00, 0x00, 0x00, /* 0x41 ('A') */ 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, /* 0x42 ('B') */ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, /* 0x43 ('C') */ 0x00, 0x00, 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00, 0x00, 0x00, /* 0x44 ('D') */ 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, /* 0x45 ('E') */ 0x00, 0x00, 0xfe, 0x66, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, /* 0x46 ('F') */ 0x00, 0x00, 0xfe, 0x66, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, /* 0x47 ('G') */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc0, 0xc0, 0xce, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0x48 ('H') */ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, /* 0x49 ('I') */ 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, /* 0x4a ('J') */ 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, /* 0x4b ('K') */ 0x00, 0x00, 0xc6, 0xcc, 0xd8, 0xf0, 0xf0, 0xd8, 0xcc, 0xc6, 0xc6, 0x00, 0x00, 0x00, /* 0x4c ('L') */ 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, /* 0x4d ('M') */ 0x00, 0x00, 0xc6, 0xc6, 0xee, 0xfe, 0xd6, 0xd6, 0xd6, 0xc6, 0xc6, 0x00, 0x00, 0x00, /* 0x4e ('N') */ 0x00, 0x00, 0xc6, 0xc6, 0xe6, 0xe6, 0xf6, 0xde, 0xce, 0xce, 0xc6, 0x00, 0x00, 0x00, /* 0x4f ('O') */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0x50 ('P') */ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, /* 0x51 ('Q') */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0x7c, 0x06, 0x00, 0x00, /* 0x52 ('R') */ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, /* 0x53 ('S') */ 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0x54 ('T') */ 0x00, 0x00, 0x7e, 0x5a, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, /* 0x55 ('U') */ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0x56 ('V') */ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, /* 0x57 ('W') */ 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xfe, 0xee, 0xc6, 0xc6, 0x00, 0x00, 0x00, /* 0x58 ('X') */ 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x38, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, /* 0x59 ('Y') */ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, /* 0x5a ('Z') */ 0x00, 0x00, 0xfe, 0xc6, 0x8c, 0x18, 0x30, 0x60, 0xc2, 0xc6, 0xfe, 0x00, 0x00, 0x00, /* 0x5b ('[') */ 0x00, 0x00, 0x7c, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7c, 0x00, 0x00, 0x00, /* 0x5c ('\') */ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, /* 0x5d (']') */ 0x00, 0x00, 0x7c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x7c, 0x00, 0x00, 0x00, /* 0x5e ('^') */ 0x00, 0x00, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5f ('_') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, /* 0x60 ('`') */ 0x00, 0x1c, 0x1c, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x61 ('a') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xdc, 0x76, 0x00, 0x00, 0x00, /* 0x62 ('b') */ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, /* 0x63 ('c') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0x64 ('d') */ 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, 0x00, 0x00, /* 0x65 ('e') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0x66 ('f') */ 0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, /* 0x67 ('g') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xce, 0xc6, 0xc6, 0x7e, 0x06, 0xc6, 0x7c, 0x00, /* 0x68 ('h') */ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, /* 0x69 ('i') */ 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, /* 0x6a ('j') */ 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x1c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, /* 0x6b ('k') */ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, /* 0x6c ('l') */ 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, /* 0x6d ('m') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xd6, 0xd6, 0xc6, 0xc6, 0x00, 0x00, 0x00, /* 0x6e ('n') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, /* 0x6f ('o') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0x70 ('p') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, /* 0x71 ('q') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, /* 0x72 ('r') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, /* 0x73 ('s') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x70, 0x1c, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0x74 ('t') */ 0x00, 0x00, 0x30, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, /* 0x75 ('u') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, /* 0x76 ('v') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, /* 0x77 ('w') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00, /* 0x78 ('x') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, /* 0x79 ('y') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00, /* 0x7a ('z') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x8c, 0x18, 0x30, 0x62, 0xfe, 0x00, 0x00, 0x00, /* 0x7b ('{') */ 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, /* 0x7c ('|') */ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, /* 0x7d ('}') */ 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, /* 0x7e ('~') */ 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7f */ 0x00, 0x66, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, /* 0x80 */ 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x81 */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x82 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x83 */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x85 */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x86 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x87 */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x88 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x89 */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8a */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8b */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8c */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x8d */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x8e */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x8f */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, /* 0x91 */ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x92 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x93 */ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6f, 0x60, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, /* 0x95 */ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, /* 0x96 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x60, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, /* 0x97 */ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6f, 0x60, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, /* 0x98 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0c, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x99 */ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0x0c, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9a */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9b */ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xef, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9c */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0c, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, /* 0x9d */ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0x0c, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, /* 0x9e */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xef, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, /* 0x9f */ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xef, 0x00, 0xef, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, /* 0xa0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0xfe, 0x00, 0x00, /* 0xa1 */ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, /* 0xa2 */ 0x00, 0x00, 0x00, 0x10, 0x7c, 0xd6, 0xd0, 0xd0, 0xd6, 0x7c, 0x10, 0x00, 0x00, 0x00, /* 0xa3 */ 0x00, 0x00, 0x38, 0x6c, 0x60, 0x60, 0xf0, 0x60, 0x66, 0xf6, 0x6c, 0x00, 0x00, 0x00, /* 0xa4 */ 0x00, 0x00, 0x3c, 0x62, 0x60, 0xf8, 0x60, 0xf8, 0x60, 0x62, 0x3c, 0x00, 0x00, 0x00, /* 0xa5 */ 0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, /* 0xa6 */ 0x6c, 0x38, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x1c, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xa7 */ 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x7c, 0xc6, 0xc6, 0x7c, 0x0c, 0xc6, 0xc6, 0x7c, 0x00, /* 0xa8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa9 */ 0x00, 0x7e, 0x81, 0x99, 0xa5, 0xa1, 0xa5, 0x99, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, /* 0xaa */ 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xab */ 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xac */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, /* 0xad */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xae */ 0x00, 0x7e, 0x81, 0xb9, 0xa5, 0xb9, 0xa5, 0xa5, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, /* 0xaf */ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0 */ 0x00, 0x38, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb1 */ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x00, 0x00, /* 0xb2 */ 0x00, 0x38, 0x6c, 0x18, 0x30, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb3 */ 0x00, 0x38, 0x6c, 0x18, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb4 */ 0x6c, 0x38, 0x00, 0xfe, 0xc6, 0x8c, 0x18, 0x30, 0x62, 0xc6, 0xfe, 0x00, 0x00, 0x00, /* 0xb5 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xf6, 0xc0, 0xc0, 0xc0, /* 0xb6 */ 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, /* 0xb7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8 */ 0x00, 0x00, 0x6c, 0x38, 0x00, 0xfe, 0x8c, 0x18, 0x30, 0x62, 0xfe, 0x00, 0x00, 0x00, /* 0xb9 */ 0x00, 0x30, 0x70, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xba */ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xbb */ 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xbc */ 0x00, 0x00, 0x77, 0xdf, 0xd8, 0xde, 0xde, 0xd8, 0xd8, 0xdf, 0x77, 0x00, 0x00, 0x00, /* 0xbd */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0xdb, 0xdf, 0xd8, 0xdb, 0x6e, 0x00, 0x00, 0x00, /* 0xbe */ 0x00, 0x66, 0x66, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, /* 0xbf */ 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc6, 0xc6, 0x7c, 0x00, /* 0xc0 */ 0x60, 0x30, 0x18, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x00, 0x00, /* 0xc1 */ 0x0c, 0x18, 0x30, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x00, 0x00, /* 0xc2 */ 0x10, 0x38, 0x6c, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x00, 0x00, /* 0xc3 */ 0x00, 0x76, 0xdc, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x00, 0x00, /* 0xc4 */ 0x00, 0x6c, 0x6c, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x00, 0x00, /* 0xc5 */ 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x00, 0x00, /* 0xc6 */ 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xfe, 0xd8, 0xd8, 0xd8, 0xde, 0x00, 0x00, 0x00, /* 0xc7 */ 0x00, 0x00, 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x18, 0x6c, 0x38, /* 0xc8 */ 0x30, 0x18, 0x0c, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, /* 0xc9 */ 0x0c, 0x18, 0x30, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, /* 0xca */ 0x10, 0x38, 0x6c, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, /* 0xcb */ 0x00, 0x6c, 0x6c, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, /* 0xcc */ 0x30, 0x18, 0x0c, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, /* 0xcd */ 0x0c, 0x18, 0x30, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, /* 0xce */ 0x18, 0x3c, 0x66, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, /* 0xcf */ 0x00, 0x66, 0x66, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, /* 0xd0 */ 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0xf6, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, /* 0xd1 */ 0x00, 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00, 0x00, 0x00, /* 0xd2 */ 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xd3 */ 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xd4 */ 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xd5 */ 0x00, 0x76, 0xdc, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xd6 */ 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xd7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x38, 0x38, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8 */ 0x00, 0x00, 0x7e, 0xc6, 0xce, 0xde, 0xd6, 0xf6, 0xe6, 0xc6, 0xfc, 0x00, 0x00, 0x00, /* 0xd9 */ 0x60, 0x30, 0x18, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xda */ 0x0c, 0x18, 0x30, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xdb */ 0x10, 0x38, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xdc */ 0x00, 0x6c, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xdd */ 0x06, 0x0c, 0x18, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, /* 0xde */ 0x00, 0x00, 0xf0, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x60, 0xf0, 0x00, 0x00, 0x00, /* 0xdf */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xcc, 0xc6, 0xc6, 0xd6, 0xdc, 0x80, 0x00, 0x00, /* 0xe0 */ 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xdc, 0x76, 0x00, 0x00, 0x00, /* 0xe1 */ 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xdc, 0x76, 0x00, 0x00, 0x00, /* 0xe2 */ 0x00, 0x30, 0x78, 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xdc, 0x76, 0x00, 0x00, 0x00, /* 0xe3 */ 0x00, 0x00, 0x76, 0xdc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xdc, 0x76, 0x00, 0x00, 0x00, /* 0xe4 */ 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xdc, 0x76, 0x00, 0x00, 0x00, /* 0xe5 */ 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xdc, 0x76, 0x00, 0x00, 0x00, /* 0xe6 */ 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0x1b, 0x7f, 0xd8, 0xdb, 0x7e, 0x00, 0x00, 0x00, /* 0xe7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x18, 0x6c, 0x38, /* 0xe8 */ 0x00, 0x30, 0x18, 0x0c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xe9 */ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xea */ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xeb */ 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xec */ 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, /* 0xed */ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, /* 0xee */ 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, /* 0xef */ 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, /* 0xf0 */ 0x00, 0x78, 0x30, 0x78, 0x0c, 0x7e, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xf1 */ 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, /* 0xf2 */ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xf3 */ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xf4 */ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xf5 */ 0x00, 0x00, 0x76, 0xdc, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xf6 */ 0x00, 0x00, 0x6c, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xf7 */ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, /* 0xf8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xce, 0xde, 0xf6, 0xe6, 0xfc, 0x00, 0x00, 0x00, /* 0xf9 */ 0x00, 0xc0, 0x60, 0x30, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, /* 0xfa */ 0x00, 0x0c, 0x18, 0x30, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, /* 0xfb */ 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, /* 0xfc */ 0x00, 0x00, 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, /* 0xfd */ 0x00, 0x0c, 0x18, 0x30, 0x00, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00, /* 0xfe */ 0x00, 0x00, 0x00, 0xf0, 0x60, 0x60, 0x78, 0x6c, 0x6c, 0x6c, 0x78, 0x60, 0x60, 0xf0, /* 0xff */ 0x00, 0x00, 0xc6, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0xc6, 0x7c, 0x00 }; static unsigned char cursorshape_8x14[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }; struct fbcon_font_desc font_lat9_8x14 = { "VGA8x14", 8, 14, fontdata_8x14, cursorshape_8x14 }; ================================================ FILE: drivers/video/font-lat9-8x16.c ================================================ #include /* refer to 'fiwix/docs/video/' for a visual representation of these glyphs */ static unsigned char fontdata_8x16[] = { /* 0x00 */ 0x00, 0x00, 0x7e, 0xc3, 0x99, 0x99, 0xf3, 0xe7, 0xe7, 0xff, 0xe7, 0xe7, 0x7e, 0x00, 0x00, 0x00, /* 0x01 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x02 */ 0x00, 0x00, 0x6e, 0xf8, 0xd8, 0xd8, 0xdc, 0xd8, 0xd8, 0xd8, 0xf8, 0x6e, 0x00, 0x00, 0x00, 0x00, /* 0x03 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0xdb, 0xdb, 0xdf, 0xd8, 0xdb, 0x6e, 0x00, 0x00, 0x00, 0x00, /* 0x04 */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x05 */ 0x00, 0x88, 0x88, 0xf8, 0x88, 0x88, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, /* 0x06 */ 0x00, 0xf8, 0x80, 0xe0, 0x80, 0x80, 0x00, 0x3e, 0x20, 0x38, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, /* 0x07 */ 0x00, 0x70, 0x88, 0x80, 0x88, 0x70, 0x00, 0x3c, 0x22, 0x3c, 0x24, 0x22, 0x00, 0x00, 0x00, 0x00, /* 0x08 */ 0x00, 0x80, 0x80, 0x80, 0x80, 0xf8, 0x00, 0x3e, 0x20, 0x38, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, /* 0x09 */ 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, /* 0x0a */ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, /* 0x0b */ 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, /* 0x0c */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x0d */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x0e */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0f */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, /* 0x10 */ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, /* 0x11 */ 0x00, 0x88, 0xc8, 0xa8, 0x98, 0x88, 0x00, 0x20, 0x20, 0x20, 0x20, 0x3e, 0x00, 0x00, 0x00, 0x00, /* 0x12 */ 0x00, 0x88, 0x88, 0x50, 0x50, 0x20, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, /* 0x13 */ 0x00, 0x00, 0x00, 0x00, 0x0e, 0x38, 0xe0, 0x38, 0x0e, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14 */ 0x00, 0x00, 0x00, 0x00, 0xe0, 0x38, 0x0e, 0x38, 0xe0, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x15 */ 0x00, 0x00, 0x00, 0x06, 0x0c, 0xfe, 0x18, 0x30, 0xfe, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x16 */ 0x00, 0x00, 0x00, 0x00, 0x06, 0x1e, 0x7e, 0xfe, 0x7e, 0x1e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x17 */ 0x00, 0x00, 0x00, 0x00, 0xc0, 0xf0, 0xfc, 0xfe, 0xfc, 0xf0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18 */ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, /* 0x19 */ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, /* 0x1a */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1b */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1c */ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, /* 0x1d */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x6c, 0xfe, 0x6c, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1e */ 0x00, 0x00, 0x00, 0x00, 0x06, 0x36, 0x66, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1f */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, /* 0x20 (' ') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x21 ('!') */ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, /* 0x22 ('"') */ 0x00, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x23 ('#') */ 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, /* 0x24 ('$') */ 0x00, 0x10, 0x10, 0x7c, 0xd6, 0xd0, 0xd0, 0x7c, 0x16, 0x16, 0xd6, 0x7c, 0x10, 0x10, 0x00, 0x00, /* 0x25 ('%') */ 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, /* 0x26 ('&') */ 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0x27 (''') */ 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28 ('(') */ 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, /* 0x29 (')') */ 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, /* 0x2a ('*') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2b ('+') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2c (',') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, /* 0x2d ('-') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2e ('.') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, /* 0x2f ('/') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30 ('0') */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0x31 ('1') */ 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, /* 0x32 ('2') */ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, /* 0x33 ('3') */ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0x34 ('4') */ 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, /* 0x35 ('5') */ 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0x36 ('6') */ 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0x37 ('7') */ 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, /* 0x38 ('8') */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0x39 ('9') */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00, /* 0x3a (':') */ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3b (';') */ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, /* 0x3c ('<') */ 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, /* 0x3d ('=') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3e ('>') */ 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, /* 0x3f ('?') */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, /* 0x40 ('@') */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0x41 ('A') */ 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, /* 0x42 ('B') */ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, /* 0x43 ('C') */ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, /* 0x44 ('D') */ 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, /* 0x45 ('E') */ 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, /* 0x46 ('F') */ 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, /* 0x47 ('G') */ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, /* 0x48 ('H') */ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, /* 0x49 ('I') */ 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, /* 0x4a ('J') */ 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, /* 0x4b ('K') */ 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, /* 0x4c ('L') */ 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, /* 0x4d ('M') */ 0x00, 0x00, 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, /* 0x4e ('N') */ 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, /* 0x4f ('O') */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0x50 ('P') */ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, /* 0x51 ('Q') */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, /* 0x52 ('R') */ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, /* 0x53 ('S') */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x64, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0x54 ('T') */ 0x00, 0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, /* 0x55 ('U') */ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0x56 ('V') */ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, /* 0x57 ('W') */ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xfe, 0xee, 0x6c, 0x00, 0x00, 0x00, 0x00, /* 0x58 ('X') */ 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x7c, 0x38, 0x38, 0x7c, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, /* 0x59 ('Y') */ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, /* 0x5a ('Z') */ 0x00, 0x00, 0xfe, 0xc6, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc2, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, /* 0x5b ('[') */ 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, /* 0x5c ('\') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5d (']') */ 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, /* 0x5e ('^') */ 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5f ('_') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, /* 0x60 ('`') */ 0x00, 0x30, 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x61 ('a') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0x62 ('b') */ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0x63 ('c') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0x64 ('d') */ 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0x65 ('e') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0x66 ('f') */ 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, /* 0x67 ('g') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, /* 0x68 ('h') */ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, /* 0x69 ('i') */ 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, /* 0x6a ('j') */ 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, /* 0x6b ('k') */ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, /* 0x6c ('l') */ 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, /* 0x6d ('m') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, 0xd6, 0xd6, 0xd6, 0xc6, 0x00, 0x00, 0x00, 0x00, /* 0x6e ('n') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, /* 0x6f ('o') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0x70 ('p') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, /* 0x71 ('q') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, /* 0x72 ('r') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, /* 0x73 ('s') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0x74 ('t') */ 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, /* 0x75 ('u') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0x76 ('v') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, /* 0x77 ('w') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00, 0x00, /* 0x78 ('x') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x38, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, /* 0x79 ('y') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, /* 0x7a ('z') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, /* 0x7b ('{') */ 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, /* 0x7c ('|') */ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, /* 0x7d ('}') */ 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, /* 0x7e ('~') */ 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7f */ 0x00, 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, /* 0x80 */ 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x81 */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x82 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x83 */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x85 */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x86 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x87 */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x88 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x89 */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8a */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8b */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8c */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x8d */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x8e */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x8f */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x91 */ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x92 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x60, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x93 */ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6f, 0x60, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, /* 0x95 */ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, /* 0x96 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x60, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, /* 0x97 */ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6f, 0x60, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, /* 0x98 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0c, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x99 */ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0x0c, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9a */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9b */ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xef, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9c */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0c, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, /* 0x9d */ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0x0c, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, /* 0x9e */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xef, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, /* 0x9f */ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xef, 0x00, 0xef, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, /* 0xa0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0xfe, 0x00, 0x00, 0x00, 0x00, /* 0xa1 */ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, /* 0xa2 */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x7c, 0xd6, 0xd0, 0xd0, 0xd0, 0xd6, 0x7c, 0x10, 0x00, 0x00, 0x00, /* 0xa3 */ 0x00, 0x00, 0x38, 0x6c, 0x60, 0x60, 0xf0, 0x60, 0x60, 0x66, 0xf6, 0x6c, 0x00, 0x00, 0x00, 0x00, /* 0xa4 */ 0x00, 0x1c, 0x32, 0x60, 0x60, 0xfc, 0x60, 0xfc, 0x60, 0x60, 0x32, 0x1c, 0x00, 0x00, 0x00, 0x00, /* 0xa5 */ 0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, /* 0xa6 */ 0x6c, 0x38, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xa7 */ 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, /* 0xa8 */ 0x00, 0x6c, 0x38, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xa9 */ 0x00, 0x00, 0x3c, 0x42, 0x99, 0xa5, 0xa1, 0xa5, 0x99, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xaa */ 0x00, 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xab */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xac */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xad */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xae */ 0x00, 0x00, 0x3c, 0x42, 0xb9, 0xa5, 0xb9, 0xa5, 0xa5, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xaf */ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0 */ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb1 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, /* 0xb2 */ 0x38, 0x6c, 0x18, 0x30, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb3 */ 0x38, 0x6c, 0x18, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb4 */ 0x6c, 0x38, 0x00, 0xfe, 0xc6, 0x8c, 0x18, 0x30, 0x60, 0xc2, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, /* 0xb5 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xf6, 0xc0, 0xc0, 0xc0, 0x00, /* 0xb6 */ 0x00, 0x00, 0x7f, 0xd6, 0xd6, 0x76, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, /* 0xb7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8 */ 0x00, 0x00, 0x6c, 0x38, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, /* 0xb9 */ 0x30, 0x70, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xba */ 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xbb */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xbc */ 0x00, 0x00, 0x77, 0xcc, 0xcc, 0xcc, 0xcf, 0xcf, 0xcc, 0xcc, 0xcc, 0x77, 0x00, 0x00, 0x00, 0x00, /* 0xbd */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0xdb, 0xdb, 0xdf, 0xd8, 0xdb, 0x6e, 0x00, 0x00, 0x00, 0x00, /* 0xbe */ 0x00, 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, /* 0xbf */ 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x30, 0x60, 0xc6, 0xc6, 0x7c, 0x00, 0x00, /* 0xc0 */ 0x60, 0x30, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, /* 0xc1 */ 0x0c, 0x18, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, /* 0xc2 */ 0x10, 0x38, 0x6c, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, /* 0xc3 */ 0x76, 0xdc, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, /* 0xc4 */ 0x00, 0x6c, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, /* 0xc5 */ 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, /* 0xc6 */ 0x00, 0x00, 0x3e, 0x78, 0xd8, 0xd8, 0xfc, 0xd8, 0xd8, 0xd8, 0xd8, 0xde, 0x00, 0x00, 0x00, 0x00, /* 0xc7 */ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x66, 0x3c, 0x00, /* 0xc8 */ 0x60, 0x30, 0x00, 0xfe, 0x66, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, /* 0xc9 */ 0x0c, 0x18, 0x00, 0xfe, 0x66, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, /* 0xca */ 0x10, 0x38, 0x6c, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, /* 0xcb */ 0x00, 0x6c, 0x00, 0xfe, 0x66, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, /* 0xcc */ 0x60, 0x30, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, /* 0xcd */ 0x06, 0x0c, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, /* 0xce */ 0x18, 0x3c, 0x66, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, /* 0xcf */ 0x00, 0x66, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, /* 0xd0 */ 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0xf6, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, /* 0xd1 */ 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, /* 0xd2 */ 0x60, 0x30, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xd3 */ 0x0c, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xd4 */ 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xd5 */ 0x76, 0xdc, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xd6 */ 0x00, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xd7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8 */ 0x00, 0x00, 0x7e, 0xc6, 0xce, 0xce, 0xde, 0xf6, 0xe6, 0xe6, 0xc6, 0xfc, 0x00, 0x00, 0x00, 0x00, /* 0xd9 */ 0x60, 0x30, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xda */ 0x0c, 0x18, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xdb */ 0x10, 0x38, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xdc */ 0x00, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xdd */ 0x06, 0x0c, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, /* 0xde */ 0x00, 0x00, 0xf0, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, /* 0xdf */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xcc, 0xc6, 0xc6, 0xc6, 0xd6, 0xdc, 0x80, 0x00, 0x00, 0x00, /* 0xe0 */ 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0xe1 */ 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0xe2 */ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0xe3 */ 0x00, 0x00, 0x76, 0xdc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0xe4 */ 0x00, 0x00, 0x00, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0xe5 */ 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0xe6 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0x1b, 0x7f, 0xd8, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, /* 0xe7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x18, 0x6c, 0x38, 0x00, /* 0xe8 */ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xe9 */ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xea */ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xeb */ 0x00, 0x00, 0x00, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xec */ 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, /* 0xed */ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, /* 0xee */ 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, /* 0xef */ 0x00, 0x00, 0x00, 0x6c, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, /* 0xf0 */ 0x00, 0x78, 0x30, 0x78, 0x0c, 0x7e, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xf1 */ 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, /* 0xf2 */ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xf3 */ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xf4 */ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xf5 */ 0x00, 0x00, 0x76, 0xdc, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xf6 */ 0x00, 0x00, 0x00, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xf7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xce, 0xde, 0xfe, 0xf6, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, /* 0xf9 */ 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0xfa */ 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0xfb */ 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0xfc */ 0x00, 0x00, 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0xfd */ 0x00, 0x0c, 0x18, 0x30, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, /* 0xfe */ 0x00, 0x00, 0xf0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, 0x00, /* 0xff */ 0x00, 0x00, 0x00, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00 }; static unsigned char cursorshape_8x16[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }; struct fbcon_font_desc font_lat9_8x16 = { "VGA8x16", 8, 16, fontdata_8x16, cursorshape_8x16 }; ================================================ FILE: drivers/video/font-lat9-8x8.c ================================================ #include /* refer to 'fiwix/docs/video/' for a visual representation of these glyphs */ static unsigned char fontdata_8x8[] = { /* 0x00 */ 0x7e, 0xc3, 0x99, 0xf3, 0xe7, 0xff, 0xe7, 0x7e, /* 0x01 */ 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, /* 0x02 */ 0x76, 0xd8, 0xd8, 0xdc, 0xd8, 0xd8, 0x76, 0x00, /* 0x03 */ 0x00, 0x00, 0x6e, 0xd8, 0xde, 0xd8, 0x6e, 0x00, /* 0x04 */ 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, /* 0x05 */ 0xa0, 0xa0, 0xe0, 0xae, 0xa4, 0x04, 0x04, 0x04, /* 0x06 */ 0xe0, 0x80, 0xc0, 0x8e, 0x88, 0x0c, 0x08, 0x08, /* 0x07 */ 0x60, 0x80, 0x80, 0x8c, 0x6a, 0x0c, 0x0a, 0x0a, /* 0x08 */ 0x80, 0x80, 0x80, 0x8e, 0xe8, 0x0c, 0x08, 0x08, /* 0x09 */ 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, /* 0x0a */ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, /* 0x0b */ 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, /* 0x0c */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x0d */ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, /* 0x0e */ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, /* 0x0f */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, /* 0x10 */ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, /* 0x11 */ 0x90, 0xd0, 0xf0, 0xb4, 0x94, 0x04, 0x04, 0x07, /* 0x12 */ 0xa0, 0xa0, 0xa0, 0xae, 0x44, 0x04, 0x04, 0x04, /* 0x13 */ 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00, /* 0x14 */ 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00, /* 0x15 */ 0x00, 0x0c, 0xfe, 0x18, 0x30, 0xfe, 0x60, 0x00, /* 0x16 */ 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00, /* 0x17 */ 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00, /* 0x18 */ 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00, /* 0x19 */ 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, /* 0x1a */ 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, /* 0x1b */ 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, /* 0x1c */ 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18, /* 0x1d */ 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, /* 0x1e */ 0x06, 0x06, 0x36, 0x66, 0xfe, 0x60, 0x30, 0x00, /* 0x1f */ 0x00, 0xc0, 0x7c, 0x6e, 0x6c, 0x6c, 0x6c, 0x00, /* 0x20 (' ') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x21 ('!') */ 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00, /* 0x22 ('"') */ 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x23 ('#') */ 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, /* 0x24 ('$') */ 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00, /* 0x25 ('%') */ 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00, /* 0x26 ('&') */ 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00, /* 0x27 (''') */ 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28 ('(') */ 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, /* 0x29 (')') */ 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, /* 0x2a ('*') */ 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, /* 0x2b ('+') */ 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00, /* 0x2c (',') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60, /* 0x2d ('-') */ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, /* 0x2e ('.') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, /* 0x2f ('/') */ 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00, /* 0x30 ('0') */ 0x7c, 0xc6, 0xc6, 0xd6, 0xc6, 0xc6, 0x7c, 0x00, /* 0x31 ('1') */ 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00, /* 0x32 ('2') */ 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00, /* 0x33 ('3') */ 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00, /* 0x34 ('4') */ 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00, /* 0x35 ('5') */ 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00, /* 0x36 ('6') */ 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00, /* 0x37 ('7') */ 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00, /* 0x38 ('8') */ 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00, /* 0x39 ('9') */ 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00, /* 0x3a (':') */ 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, /* 0x3b (';') */ 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60, /* 0x3c ('<') */ 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00, /* 0x3d ('=') */ 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00, /* 0x3e ('>') */ 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, /* 0x3f ('?') */ 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00, /* 0x40 ('@') */ 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00, /* 0x41 ('A') */ 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00, /* 0x42 ('B') */ 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00, /* 0x43 ('C') */ 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00, /* 0x44 ('D') */ 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, /* 0x45 ('E') */ 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00, /* 0x46 ('F') */ 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00, /* 0x47 ('G') */ 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00, /* 0x48 ('H') */ 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00, /* 0x49 ('I') */ 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, /* 0x4a ('J') */ 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, /* 0x4b ('K') */ 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, /* 0x4c ('L') */ 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, /* 0x4d ('M') */ 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00, /* 0x4e ('N') */ 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00, /* 0x4f ('O') */ 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, /* 0x50 ('P') */ 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, /* 0x51 ('Q') */ 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00, /* 0x52 ('R') */ 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00, /* 0x53 ('S') */ 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00, /* 0x54 ('T') */ 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, /* 0x55 ('U') */ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, /* 0x56 ('V') */ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, /* 0x57 ('W') */ 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00, /* 0x58 ('X') */ 0xc6, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0xc6, 0x00, /* 0x59 ('Y') */ 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00, /* 0x5a ('Z') */ 0xfe, 0xc6, 0x0c, 0x18, 0x30, 0x66, 0xfe, 0x00, /* 0x5b ('[') */ 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, /* 0x5c ('\') */ 0x00, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, /* 0x5d (']') */ 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, /* 0x5e ('^') */ 0x18, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5f ('_') */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, /* 0x60 ('`') */ 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x61 ('a') */ 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, /* 0x62 ('b') */ 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00, /* 0x63 ('c') */ 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00, /* 0x64 ('d') */ 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, /* 0x65 ('e') */ 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, /* 0x66 ('f') */ 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00, /* 0x67 ('g') */ 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, /* 0x68 ('h') */ 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00, /* 0x69 ('i') */ 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, /* 0x6a ('j') */ 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, /* 0x6b ('k') */ 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00, /* 0x6c ('l') */ 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, /* 0x6d ('m') */ 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00, /* 0x6e ('n') */ 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, /* 0x6f ('o') */ 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, /* 0x70 ('p') */ 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0, /* 0x71 ('q') */ 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e, /* 0x72 ('r') */ 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00, /* 0x73 ('s') */ 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00, /* 0x74 ('t') */ 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00, /* 0x75 ('u') */ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, /* 0x76 ('v') */ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, /* 0x77 ('w') */ 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00, /* 0x78 ('x') */ 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, /* 0x79 ('y') */ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, /* 0x7a ('z') */ 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00, /* 0x7b ('{') */ 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00, /* 0x7c ('|') */ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, /* 0x7d ('}') */ 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00, /* 0x7e ('~') */ 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7f */ 0xcc, 0x00, 0xcc, 0xcc, 0x78, 0x30, 0x78, 0x00, /* 0x80 */ 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x81 */ 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, /* 0x82 */ 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, /* 0x83 */ 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, /* 0x84 */ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, /* 0x85 */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x86 */ 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, /* 0x87 */ 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, /* 0x88 */ 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, /* 0x89 */ 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, /* 0x8a */ 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, /* 0x8b */ 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, /* 0x8c */ 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, /* 0x8d */ 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, /* 0x8e */ 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, /* 0x8f */ 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, /* 0x90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, /* 0x91 */ 0x6c, 0x6c, 0x6c, 0x6c, 0x7c, 0x00, 0x00, 0x00, /* 0x92 */ 0x00, 0x00, 0x7f, 0x60, 0x7f, 0x00, 0x00, 0x00, /* 0x93 */ 0x6c, 0x6c, 0x6f, 0x60, 0x7f, 0x00, 0x00, 0x00, /* 0x94 */ 0x00, 0x00, 0x7c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, /* 0x95 */ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, /* 0x96 */ 0x00, 0x00, 0x7f, 0x60, 0x6f, 0x6c, 0x6c, 0x6c, /* 0x97 */ 0x6c, 0x6c, 0x6f, 0x60, 0x6f, 0x6c, 0x6c, 0x6c, /* 0x98 */ 0x00, 0x00, 0xfc, 0x0c, 0xfc, 0x00, 0x00, 0x00, /* 0x99 */ 0x6c, 0x6c, 0xec, 0x0c, 0xfc, 0x00, 0x00, 0x00, /* 0x9a */ 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, /* 0x9b */ 0x6c, 0x6c, 0xef, 0x00, 0xff, 0x00, 0x00, 0x00, /* 0x9c */ 0x00, 0x00, 0xfc, 0x0c, 0xec, 0x6c, 0x6c, 0x6c, /* 0x9d */ 0x6c, 0x6c, 0xec, 0x0c, 0xec, 0x6c, 0x6c, 0x6c, /* 0x9e */ 0x00, 0x00, 0xff, 0x00, 0xef, 0x6c, 0x6c, 0x6c, /* 0x9f */ 0x6c, 0x6c, 0xef, 0x00, 0xef, 0x6c, 0x6c, 0x6c, /* 0xa0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xfe, /* 0xa1 */ 0x00, 0x30, 0x00, 0x30, 0x30, 0x78, 0x78, 0x30, /* 0xa2 */ 0x30, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x30, 0x00, /* 0xa3 */ 0x38, 0x6c, 0x64, 0xf0, 0x60, 0xe6, 0xfc, 0x00, /* 0xa4 */ 0x38, 0x64, 0xf0, 0x60, 0xf0, 0x64, 0x38, 0x00, /* 0xa5 */ 0xcc, 0xcc, 0x78, 0xfc, 0x30, 0xfc, 0x30, 0x30, /* 0xa6 */ 0x48, 0x78, 0x84, 0x60, 0x18, 0x84, 0x78, 0x00, /* 0xa7 */ 0x3e, 0x61, 0x3c, 0x66, 0x66, 0x3c, 0x86, 0x7c, /* 0xa8 */ 0x78, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00, /* 0xa9 */ 0x7c, 0x82, 0x9a, 0xa2, 0xa2, 0x9a, 0x82, 0x7c, /* 0xaa */ 0x3c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, /* 0xab */ 0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00, /* 0xac */ 0x00, 0x00, 0x00, 0xfc, 0x0c, 0x0c, 0x00, 0x00, /* 0xad */ 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, /* 0xae */ 0x7c, 0x82, 0xb2, 0xaa, 0xb2, 0xaa, 0x82, 0x7c, /* 0xaf */ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0 */ 0x70, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb1 */ 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0xfc, 0x00, /* 0xb2 */ 0x70, 0xd8, 0x30, 0x60, 0xf8, 0x00, 0x00, 0x00, /* 0xb3 */ 0x70, 0xd8, 0x30, 0xd8, 0x70, 0x00, 0x00, 0x00, /* 0xb4 */ 0x6c, 0xfe, 0xcc, 0x18, 0x30, 0x66, 0xfe, 0x00, /* 0xb5 */ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xf6, 0xc0, /* 0xb6 */ 0x7f, 0xdb, 0x7b, 0x3b, 0x1b, 0x1b, 0x1b, 0x00, /* 0xb7 */ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, /* 0xb8 */ 0x78, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00, /* 0xb9 */ 0x60, 0xe0, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, /* 0xba */ 0x38, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, /* 0xbb */ 0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00, /* 0xbc */ 0x7e, 0xd8, 0xd8, 0xdc, 0xd8, 0xd8, 0x7e, 0x00, /* 0xbd */ 0x00, 0x00, 0x7e, 0xdb, 0xde, 0xd8, 0x7e, 0x00, /* 0xbe */ 0xcc, 0x00, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00, /* 0xbf */ 0x00, 0x18, 0x00, 0x18, 0x30, 0x60, 0x66, 0x3c, /* 0xc0 */ 0x60, 0x30, 0x78, 0xcc, 0xfc, 0xcc, 0xcc, 0x00, /* 0xc1 */ 0x18, 0x30, 0x78, 0xcc, 0xfc, 0xcc, 0xcc, 0x00, /* 0xc2 */ 0x78, 0x84, 0x78, 0xcc, 0xfc, 0xcc, 0xcc, 0x00, /* 0xc3 */ 0x76, 0xdc, 0x78, 0xcc, 0xfc, 0xcc, 0xcc, 0x00, /* 0xc4 */ 0xcc, 0x00, 0x78, 0xcc, 0xfc, 0xcc, 0xcc, 0x00, /* 0xc5 */ 0x30, 0x48, 0x78, 0xcc, 0xfc, 0xcc, 0xcc, 0x00, /* 0xc6 */ 0x3e, 0x78, 0xd8, 0xfc, 0xd8, 0xd8, 0xde, 0x00, /* 0xc7 */ 0x3c, 0x66, 0xc0, 0xc0, 0x66, 0x3c, 0x0c, 0x78, /* 0xc8 */ 0x60, 0x30, 0xfe, 0x62, 0x78, 0x62, 0xfe, 0x00, /* 0xc9 */ 0x0c, 0x18, 0xfe, 0x62, 0x78, 0x62, 0xfe, 0x00, /* 0xca */ 0x38, 0x6c, 0xfe, 0x62, 0x78, 0x62, 0xfe, 0x00, /* 0xcb */ 0x6c, 0x00, 0xfe, 0x62, 0x78, 0x62, 0xfe, 0x00, /* 0xcc */ 0x60, 0x30, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, /* 0xcd */ 0x18, 0x30, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, /* 0xce */ 0x78, 0xcc, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, /* 0xcf */ 0xcc, 0x00, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, /* 0xd0 */ 0xf8, 0x6c, 0x66, 0xf6, 0x66, 0x6c, 0xf8, 0x00, /* 0xd1 */ 0x76, 0xdc, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0x00, /* 0xd2 */ 0x60, 0x30, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, /* 0xd3 */ 0x18, 0x30, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, /* 0xd4 */ 0x78, 0xcc, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, /* 0xd5 */ 0x76, 0xdc, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, /* 0xd6 */ 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, /* 0xd7 */ 0x00, 0x6c, 0x38, 0x38, 0x6c, 0x00, 0x00, 0x00, /* 0xd8 */ 0x3e, 0x6c, 0xde, 0xd6, 0xf6, 0x6c, 0xf8, 0x00, /* 0xd9 */ 0x60, 0x30, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, /* 0xda */ 0x18, 0x30, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, /* 0xdb */ 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x00, /* 0xdc */ 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, /* 0xdd */ 0x18, 0x30, 0xcc, 0xcc, 0x78, 0x30, 0x78, 0x00, /* 0xde */ 0xf0, 0x60, 0x7c, 0x66, 0x7c, 0x60, 0xf0, 0x00, /* 0xdf */ 0x7c, 0xc6, 0xc6, 0xcc, 0xc6, 0xd6, 0xdc, 0x80, /* 0xe0 */ 0x60, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, /* 0xe1 */ 0x18, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, /* 0xe2 */ 0x78, 0x84, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, /* 0xe3 */ 0x76, 0xdc, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, /* 0xe4 */ 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, /* 0xe5 */ 0x38, 0x6c, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, /* 0xe6 */ 0x00, 0x00, 0x7e, 0x1b, 0x7e, 0xd8, 0x7e, 0x00, /* 0xe7 */ 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x18, 0x70, /* 0xe8 */ 0x60, 0x30, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, /* 0xe9 */ 0x0c, 0x18, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, /* 0xea */ 0x78, 0x84, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, /* 0xeb */ 0xcc, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, /* 0xec */ 0x60, 0x30, 0x00, 0x70, 0x30, 0x30, 0x78, 0x00, /* 0xed */ 0x18, 0x30, 0x00, 0x70, 0x30, 0x30, 0x78, 0x00, /* 0xee */ 0x70, 0xd8, 0x00, 0x70, 0x30, 0x30, 0x78, 0x00, /* 0xef */ 0x00, 0xd8, 0x00, 0x70, 0x30, 0x30, 0x78, 0x00, /* 0xf0 */ 0x78, 0x70, 0x18, 0x7c, 0xcc, 0xcc, 0x78, 0x00, /* 0xf1 */ 0x76, 0xdc, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, /* 0xf2 */ 0x60, 0x30, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, /* 0xf3 */ 0x18, 0x30, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, /* 0xf4 */ 0x78, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, /* 0xf5 */ 0x76, 0xdc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, /* 0xf6 */ 0x00, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, /* 0xf7 */ 0x00, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x00, 0x00, /* 0xf8 */ 0x00, 0x00, 0x7c, 0xdc, 0xfc, 0xec, 0xf8, 0x00, /* 0xf9 */ 0x60, 0x30, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, /* 0xfa */ 0x18, 0x30, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, /* 0xfb */ 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x76, 0x00, /* 0xfc */ 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, /* 0xfd */ 0x18, 0x30, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, /* 0xfe */ 0xf0, 0x60, 0x78, 0x6c, 0x6c, 0x78, 0x60, 0xf0, /* 0xff */ 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8 }; static unsigned char cursorshape_8x8[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }; struct fbcon_font_desc font_lat9_8x8 = { "VGA8x8", 8, 8, fontdata_8x8, cursorshape_8x8 }; ================================================ FILE: drivers/video/fonts.c ================================================ /* * fiwix/drivers/video/fonts.c * * Copyright 2021-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include "font-lat9-8x8.c" #include "font-lat9-8x14.c" #include "font-lat9-8x16.c" #define NR_FONTS (sizeof(fbcon_fonts) / sizeof(*fbcon_fonts)) static struct fbcon_font_desc *fbcon_fonts[] = { &font_lat9_8x8, &font_lat9_8x14, &font_lat9_8x16 }; struct fbcon_font_desc *fbcon_find_font(int height) { int n; for(n = 0; n < NR_FONTS; n++) { if(fbcon_fonts[n]->height == height) { return fbcon_fonts[n]; } } return NULL; } ================================================ FILE: drivers/video/vgacon.c ================================================ /* * fiwix/drivers/video/vgacon.c * * Copyright 2021-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include /* 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" ; static unsigned char screen_is_off = 0; void vgacon_put_char(struct vconsole *vc, unsigned char ch) { short int *vidmem, *screen; ch = iso8859[ch]; screen = vc->screen; if(vc->flags & CONSOLE_HAS_FOCUS) { vidmem = (short int *)vc->vidmem; vidmem[(vc->y * vc->columns) + vc->x] = vc->color_attr | ch; } screen[(vc->y * vc->columns) + vc->x] = vc->color_attr | ch; vcbuf[(video.buf_y * vc->columns) + vc->x] = vc->color_attr | ch; } void vgacon_insert_char(struct vconsole *vc) { int n, offset; short int tmp, last_char, *vidmem, *screen; screen = vc->screen; offset = (vc->y * vc->columns) + vc->x; n = vc->x; last_char = BLANK_MEM; while(n++ < vc->columns) { if(vc->flags & CONSOLE_HAS_FOCUS) { vidmem = (short int *)vc->vidmem; memcpy_w(&tmp, vidmem + offset, 1); memset_w(vidmem + offset, last_char, 1); } memcpy_w(&tmp, screen + offset, 1); memset_w(screen + offset, last_char, 1); last_char = tmp; offset++; } } void vgacon_delete_char(struct vconsole *vc) { int offset, count; short int *vidmem, *screen; screen = vc->screen; offset = (vc->y * vc->columns) + vc->x; count = vc->columns - vc->x; if(vc->flags & CONSOLE_HAS_FOCUS) { vidmem = (short int *)vc->vidmem; memcpy_w(vidmem + offset, vidmem + offset + 1, count); memset_w(vidmem + offset + count, BLANK_MEM, 1); } memcpy_w(screen + offset, screen + offset + 1, count); memset_w(screen + offset + count, BLANK_MEM, 1); } void vgacon_update_curpos(struct vconsole *vc) { unsigned short int curpos; if(vc->flags & CONSOLE_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)); } } void vgacon_show_cursor(struct vconsole *vc, int mode) { int status; switch(mode) { case COND: if(!(video.flags & VPF_CURSOR_ON)) { break; } /* fall through */ 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); video.flags |= VPF_CURSOR_ON; 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); video.flags &= ~VPF_CURSOR_ON; break; } } void vgacon_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; } void vgacon_write_screen(struct vconsole *vc, int from, int count, short int color) { short int *vidmem, *screen; if(vc->flags & CONSOLE_HAS_FOCUS) { vidmem = (short int *)vc->vidmem; memset_w(vidmem + from, color, count); } screen = vc->screen; memset_w(screen + from, color, count); } void vgacon_blank_screen(struct vconsole *vc) { short int *vidmem; if(vc->flags & CONSOLE_BLANKED) { return; } if(vc->flags & CONSOLE_HAS_FOCUS) { vidmem = (short int *)vc->vidmem; memset_w(vidmem, BLANK_MEM, SCREEN_SIZE); } vc->flags |= CONSOLE_BLANKED; vgacon_show_cursor(vc, OFF); } void vgacon_scroll_screen(struct vconsole *vc, int top, int mode) { int n, offset, count; short int *vidmem, *screen; vidmem = (short int *)vc->vidmem; screen = vc->screen; if(!top) { top = vc->top; } switch(mode) { case SCROLL_UP: count = vc->columns * (vc->lines - top - 1); offset = top * vc->columns; top = (top + 1) * vc->columns; if(vc->flags & CONSOLE_HAS_FOCUS) { memcpy_w(vidmem + offset, screen + top, count); memset_w(vidmem + offset + count, BLANK_MEM, vc->columns); } memcpy_w(screen + offset, screen + top, count); memset_w(screen + offset + count, BLANK_MEM, vc->columns); break; case SCROLL_DOWN: count = vc->columns; for(n = vc->lines - 1; n > top; n--) { memcpy_w(screen + (vc->columns * n), screen + (vc->columns * (n - 1)), count); if(vc->flags & CONSOLE_HAS_FOCUS) { memcpy_w(vidmem + (vc->columns * n), screen + (vc->columns * (n - 1)), count); } } memset_w(screen + (top * vc->columns), BLANK_MEM, count); if(vc->flags & CONSOLE_HAS_FOCUS) { memset_w(vidmem + (top * vc->columns), BLANK_MEM, count); } break; } return; } void vgacon_restore_screen(struct vconsole *vc) { short int *vidmem; if(vc->flags & CONSOLE_HAS_FOCUS) { vidmem = (short int *)vc->vidmem; memcpy_w(vidmem, vc->screen, SCREEN_SIZE); } } void vgacon_screen_on(struct vconsole *vc) { unsigned 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); } if(BLANK_INTERVAL) { creq.fn = vgacon_screen_off; creq.arg = 0; add_callout(&creq, BLANK_INTERVAL); } } void vgacon_screen_off(unsigned int arg) { unsigned 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 vgacon_buf_scroll(struct vconsole *vc, int mode) { short int *vidmem; if(video.buf_y <= SCREEN_LINES) { return; } vidmem = (short int *)vc->vidmem; if(mode == SCROLL_UP) { if(video.buf_top < 0) { return; } if(!video.buf_top) { video.buf_top = (video.buf_y - SCREEN_LINES + 1) * SCREEN_COLS; } video.buf_top -= (SCREEN_LINES / 2) * SCREEN_COLS; if(video.buf_top < 0) { video.buf_top = 0; } memcpy_w(vidmem, vcbuf + video.buf_top, SCREEN_SIZE); if(!video.buf_top) { video.buf_top = -1; } vgacon_show_cursor(vc, OFF); return; } if(mode == SCROLL_DOWN) { if(!video.buf_top) { return; } if(video.buf_top == video.buf_y * SCREEN_COLS) { return; } if(video.buf_top < 0) { video.buf_top = 0; } video.buf_top += (SCREEN_LINES / 2) * SCREEN_COLS; if(video.buf_top >= (video.buf_y - SCREEN_LINES + 1) * SCREEN_COLS) { vgacon_restore_screen(vc); video.buf_top = 0; vgacon_show_cursor(vc, ON); vgacon_update_curpos(vc); return; } memcpy_w(vidmem, vcbuf + video.buf_top, SCREEN_SIZE); return; } } void vgacon_cursor_blink(unsigned int arg) { /* not used */ } void vgacon_init(void) { short int *hw_detected;; /* find out the VGA type from the BIOS Data Area */ hw_detected = (short int *)(bios_data + 0x10); if((*hw_detected & 0x30) == 0x30) { /* monochrome = 0x30 */ video.address = (unsigned int *)(MONO_ADDR + PAGE_OFFSET); video.port = MONO_6845_ADDR; strcpy((char *)video.signature, "VGA monochrome 80x25"); } else { /* color = 0x00 || 0x20 */ video.address = (unsigned int *)(COLOR_ADDR + PAGE_OFFSET); video.port = COLOR_6845_ADDR; strcpy((char *)video.signature, "VGA color 80x25"); } /* some parameters already set in multiboot.c */ video.put_char = vgacon_put_char; video.insert_char = vgacon_insert_char; video.delete_char = vgacon_delete_char; video.update_curpos = vgacon_update_curpos; video.show_cursor = vgacon_show_cursor; video.get_curpos = vgacon_get_curpos; video.write_screen = vgacon_write_screen; video.blank_screen = vgacon_blank_screen; video.scroll_screen = vgacon_scroll_screen; video.restore_screen = vgacon_restore_screen; video.screen_on = vgacon_screen_on; video.buf_scroll = vgacon_buf_scroll; video.cursor_blink = vgacon_cursor_blink; memcpy_w(vcbuf, video.address, SCREEN_SIZE * 2); } ================================================ FILE: drivers/video/video.c ================================================ /* * fiwix/drivers/video/video.c * * Copyright 2021-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include void video_init(void) { memset_b(vcbuf, 0, (video.columns * video.lines * SCREENS_LOG * 2 * sizeof(short int))); if(video.flags & VPF_VGA) { vgacon_init(); return; } #ifdef CONFIG_PCI #ifdef CONFIG_BGA if(video.flags & VPF_VESAFB) { bga_init(); } #endif /* CONFIG_BGA */ #endif /* CONFIG_PCI */ if(video.flags & VPF_VESAFB) { fb_init(); fbcon_init(); } } ================================================ FILE: fiwix.ld ================================================ /* * Linker script for the Fiwix kernel. * * Copyright 2018, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include OUTPUT_FORMAT("elf32-i386") OUTPUT_ARCH("i386") ENTRY(_start) /* entry point */ vaddr = PAGE_OFFSET; /* virtual start address */ paddr = KERNEL_ADDR; /* 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) } } ================================================ FILE: fs/Makefile ================================================ # fiwix/fs/Makefile # # Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. # Distributed under the terms of the Fiwix License. # .c.o: $(CC) $(CFLAGS) -c -o $@ $< DIRS = minix ext2 pipefs iso9660 procfs sockfs devpts OBJS = filesystems.o devices.o buffer.o fd.o locks.o super.o inode.o \ namei.o elf.o script.o all: $(OBJS) @for n in $(DIRS) ; do (cd $$n ; $(MAKE)) ; done clean: @for n in $(DIRS) ; do (cd $$n ; $(MAKE) clean) ; done rm -f *.o ================================================ FILE: fs/buffer.c ================================================ /* * fiwix/fs/buffer.c * * Copyright 2018-2023, 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 NR_BUF_HASH (buffer_hash_table_size / sizeof(unsigned int)) #define BUFFER_HASH(dev, block) (((__dev_t)(dev) ^ (__blk_t)(block)) % (NR_BUF_HASH)) #define BUFHEAD_INDEX(size) ((size / BLKSIZE_1K) - 1) #define NO_GROW 0 #define GROW_IF_NEEDED 1 struct buffer *buffer_table; /* buffer pool */ struct buffer **buffer_hash_table; /* [0] = 1KB, [1] = 2KB, [2] = unused, [3] = 4KB */ struct buffer *buffer_head[4]; /* heads of free list */ struct buffer *buffer_dirty_head[4]; /* heads of dirty list */ static struct resource sync_resource = { 0, 0 }; static struct buffer *add_buffer_to_pool(void) { struct buffer *buf; if(!(buf = (struct buffer *)kmalloc(sizeof(struct buffer)))) { return NULL; } memset_b(buf, 0, sizeof(struct buffer)); if(!buffer_table) { buffer_table = buf; } else { buf->prev = buffer_table->prev; buffer_table->prev->next = buf; } buffer_table->prev = buf; kstat.nr_buffers++; return buf; } static void del_buffer_from_pool(struct buffer *buf) { struct buffer *tmp; tmp = buf; if(!buf->next && !buf->prev) { printk("WARNING: %s(): trying to delete an unexistent buffer (block %d).\n", __FUNCTION__, buf->block); return; } if(buf->next) { buf->next->prev = buf->prev; } if(buf->prev) { if(buf != buffer_table) { buf->prev->next = buf->next; } } if(!buf->next) { buffer_table->prev = buf->prev; } if(buf == buffer_table) { buffer_table = buf->next; } kfree((unsigned int)tmp); kstat.nr_buffers--; } 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 insert_on_dirty_list(struct buffer *buf) { struct buffer *h; int index; index = BUFHEAD_INDEX(buf->size); h = buffer_dirty_head[index]; if(buf->prev_dirty || buf->next_dirty) { return; } if(!h) { buffer_dirty_head[index] = buf; h = buffer_dirty_head[index]; } else { buf->prev_dirty = h->prev_dirty; h->prev_dirty->next_dirty = buf; } h->prev_dirty = buf; kstat.dirty_buffers += (index + 1); kstat.nr_dirty_buffers++; if(kstat.nr_dirty_buffers > kstat.max_dirty_buffers) { wakeup(&kbdflushd); } } static void remove_from_dirty_list(struct buffer *buf) { struct buffer *h; int index; index = BUFHEAD_INDEX(buf->size); h = buffer_dirty_head[index]; if(!h) { return; } if(buf->next_dirty) { buf->next_dirty->prev_dirty = buf->prev_dirty; } if(buf->prev_dirty) { if(buf != h) { buf->prev_dirty->next_dirty = buf->next_dirty; } } if(!buf->next_dirty) { h->prev_dirty = buf->prev_dirty; } if(buf == h) { buffer_dirty_head[index] = buf->next_dirty; } buf->prev_dirty = buf->next_dirty = NULL; kstat.dirty_buffers -= (index + 1); kstat.nr_dirty_buffers--; } static void insert_on_free_list(struct buffer *buf) { struct buffer *h; int index; index = BUFHEAD_INDEX(buf->size); h = buffer_head[index]; if(!h) { buffer_head[index] = buf; h = buffer_head[index]; } else { buf->prev_free = h->prev_free; /* * If is not marked as valid then this buffer * is placed at the beginning of the free list. */ if(!(buf->flags & BUFFER_VALID)) { buf->next_free = h; h->prev_free = buf; buffer_head[index] = buf; return; } else { h->prev_free->next_free = buf; } } h->prev_free = buf; } static void append_on_free_list(struct buffer *buf) { struct buffer *h; int index; index = BUFHEAD_INDEX(buf->size); h = buffer_head[index]; if(!h) { buffer_head[index] = buf; h = buffer_head[index]; } else { buf->prev_free = h->prev_free; h->prev_free->next_free = buf; } h->prev_free = buf; } static void remove_from_free_list(struct buffer *buf) { struct buffer *h; int index; index = BUFHEAD_INDEX(buf->size); h = buffer_head[index]; if(!h) { return; } if(buf->next_free) { buf->next_free->prev_free = buf->prev_free; } if(buf->prev_free) { if(buf != h) { buf->prev_free->next_free = buf->next_free; } } if(!buf->next_free) { h->prev_free = buf->prev_free; } if(buf == h) { buffer_head[index] = buf->next_free; } buf->prev_free = buf->next_free = NULL; } static void buffer_wait(struct buffer *buf) { unsigned int flags; for(;;) { SAVE_FLAGS(flags); CLI(); if(buf->flags & BUFFER_LOCKED) { sleep(&buffer_wait, PROC_UNINTERRUPTIBLE); } else { break; } RESTORE_FLAGS(flags); } buf->flags |= BUFFER_LOCKED; RESTORE_FLAGS(flags); } static struct buffer *create_buffers(int size) { struct buffer *buf, *prev, *first; char *data; int n; if(!(data = (char *)kmalloc(PAGE_SIZE))) { printk("%s(): returning NULL\n", __FUNCTION__); return NULL; } buf = prev = first = NULL; for(n = 0; n < (PAGE_SIZE / size); n++) { if(!(buf = add_buffer_to_pool())) { if(!n) { kfree((unsigned int)data); return NULL; } break; } if(prev) { brelse(prev); prev->next_sibling = buf; } buf->data = data + (n * size); buf->size = size; buf->first_sibling = first; kstat.buffers_size += size / 1024; prev = buf; if(!first) { first = buf; } } buf = buf ? buf : prev; if(buf) { buf->flags |= BUFFER_LOCKED; } return buf; } static struct buffer *get_free_buffer(int mode, int size) { unsigned int flags; struct buffer *buf; int index, min; index = BUFHEAD_INDEX(size); buf = buffer_head[index]; /* * We check buf->dev to see if this buffer has been already used * and, if so, then we know that there aren't more unused buffers, * so let's try to create new ones instead of reusing them. */ if(!buf || (buf && buf->dev)) { if(mode == GROW_IF_NEEDED) { min = (kstat.total_mem_pages * FREE_PAGES_RATIO) / 100; if(kstat.free_pages > min) { if((buf = create_buffers(size))) { return buf; } } } } for(;;) { SAVE_FLAGS(flags); CLI(); if(!(buf = buffer_head[index])) { /* no more buffers in this free list */ RESTORE_FLAGS(flags); return NULL; } if(buf->flags & BUFFER_LOCKED) { sleep(&buffer_wait, PROC_UNINTERRUPTIBLE); } else { break; } RESTORE_FLAGS(flags); } remove_from_free_list(buf); buf->flags |= BUFFER_LOCKED; RESTORE_FLAGS(flags); return buf; } static struct buffer *get_dirty_buffer(int size) { unsigned int flags; struct buffer *buf; int index; index = BUFHEAD_INDEX(size); for(;;) { SAVE_FLAGS(flags); CLI(); if(!(buf = buffer_dirty_head[index])) { /* no buffers in this dirty list */ RESTORE_FLAGS(flags); return NULL; } if(buf->flags & BUFFER_LOCKED) { sleep(&buffer_wait, PROC_UNINTERRUPTIBLE); } else { break; } RESTORE_FLAGS(flags); } remove_from_dirty_list(buf); buf->flags |= BUFFER_LOCKED; RESTORE_FLAGS(flags); return buf; } static int sync_one_buffer(struct buffer *buf) { struct device *d; int errno; if(!(d = get_device(BLK_DEV, buf->dev))) { printk("WARNING: %s(): block device %d,%d not registered!\n", __FUNCTION__, MAJOR(buf->dev), MINOR(buf->dev)); return 1; } if((errno = do_blk_request(d, d->fsop->write_block, buf)) < 0) { if(errno == -EROFS) { printk("WARNING: %s(): unable to write block %d, write protection on device %d,%d.\n", __FUNCTION__, buf->block, MAJOR(buf->dev), MINOR(buf->dev)); } else { printk("WARNING: %s(): unable to write block %d, I/O error on device %d,%d.\n", __FUNCTION__, buf->block, MAJOR(buf->dev), MINOR(buf->dev)); } return 1; } buf->flags &= ~BUFFER_DIRTY; return 0; } 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 int flags; struct buffer *buf; for(;;) { if((buf = search_buffer_hash(dev, block, size))) { SAVE_FLAGS(flags); CLI(); if(buf->flags & BUFFER_LOCKED) { sleep(&buffer_wait, PROC_UNINTERRUPTIBLE); RESTORE_FLAGS(flags); continue; } buf->flags |= BUFFER_LOCKED; remove_from_free_list(buf); RESTORE_FLAGS(flags); return buf; } if(!(buf = get_free_buffer(GROW_IF_NEEDED, size))) { wakeup(&kswapd); sleep(&get_free_buffer, PROC_UNINTERRUPTIBLE); continue; } if(buf->flags & BUFFER_DIRTY) { if(!sync_one_buffer(buf)) { remove_from_dirty_list(buf); } brelse(buf); continue; } SAVE_FLAGS(flags); CLI(); remove_from_hash(buf); /* remove it from its old hash */ buf->dev = dev; buf->block = block; insert_to_hash(buf); buf->flags &= ~BUFFER_VALID; RESTORE_FLAGS(flags); return buf; } } /* read a group of blocks */ int gbread(struct device *d, struct blk_request *brh) { struct blk_request *br; struct buffer *buf; br = brh->next_group; while(br) { if(!(br->flags & BRF_NOBLOCK)) { if((buf = getblk(br->dev, br->block, br->size))) { br->buffer = buf; if(buf->flags & BUFFER_VALID) { br = br->next_group; continue; } brh->left++; add_blk_request(br); } else { /* cancel the previous requests already queued */ /* FIXME: not tested!! */ br = brh->next_group; while(br) { if(!br->status) { br->status = BR_COMPLETED; } br = br->next_group; } return 1; } } br = br->next_group; } run_blk_request(d); if(brh->left) { sleep(brh, PROC_UNINTERRUPTIBLE); } return brh->errno; } /* read a single block */ struct buffer *bread(__dev_t dev, __blk_t block, int size) { struct buffer *buf; struct device *d; if((buf = getblk(dev, block, size))) { if(buf->flags & BUFFER_VALID) { return buf; } if(!(d = get_device(BLK_DEV, dev))) { return NULL; } if(do_blk_request(d, d->fsop->read_block, buf) == size) { buf->flags |= BUFFER_VALID; return buf; } brelse(buf); } printk("WARNING: %s(): returning NULL!\n", __FUNCTION__); return NULL; } void bwrite(struct buffer *buf) { buf->flags |= (BUFFER_DIRTY | BUFFER_VALID); brelse(buf); } void brelse(struct buffer *buf) { unsigned int flags; SAVE_FLAGS(flags); CLI(); if(buf->flags & BUFFER_DIRTY) { insert_on_dirty_list(buf); } insert_on_free_list(buf); buf->flags &= ~BUFFER_LOCKED; RESTORE_FLAGS(flags); wakeup(&get_free_buffer); wakeup(&buffer_wait); } void sync_buffers(__dev_t dev) { struct buffer *buf, *first; int flushed, size; lock_resource(&sync_resource); flushed = 0; for(size = BLKSIZE_1K; size <= PAGE_SIZE; size <<= 1) { first = NULL; for(;;) { if(!(buf = get_dirty_buffer(size))) { break; } if(first == buf) { insert_on_dirty_list(buf); buf->flags &= ~BUFFER_LOCKED; flushed = 1; break; } if(!dev || buf->dev == dev) { if(sync_one_buffer(buf)) { insert_on_dirty_list(buf); buf->flags &= ~BUFFER_LOCKED; continue; } flushed = 1; } else { if(!first) { first = buf; } insert_on_dirty_list(buf); } buf->flags &= ~BUFFER_LOCKED; } } if(flushed) { wakeup(&buffer_wait); } unlock_resource(&sync_resource); } void invalidate_buffers(__dev_t dev) { unsigned int flags; struct buffer *buf; buf = buffer_table; SAVE_FLAGS(flags); CLI(); while(buf) { if(!(buf->flags & BUFFER_LOCKED) && buf->dev == dev) { buffer_wait(buf); remove_from_hash(buf); buf->flags &= ~(BUFFER_VALID | BUFFER_LOCKED); wakeup(&buffer_wait); } buf = buf->next; } RESTORE_FLAGS(flags); /* FIXME: invalidate_pages(dev); */ } static int reclaim_siblings(struct buffer *buf) { struct buffer *orig, *tmp; unsigned int flags; orig = buf; if(buf->first_sibling) { buf = buf->first_sibling; } /* abort if one of the siblings is locked */ do { if(buf != orig) { if(buf->flags & BUFFER_LOCKED) { return 1; } } buf = buf->next_sibling; } while(buf); /* OK, all siblings are eligible to be freed up, let's lock them all */ SAVE_FLAGS(flags); CLI(); buf = orig; if(buf->first_sibling) { buf = buf->first_sibling; } do { if(buf != orig) { buf->flags |= BUFFER_LOCKED; } buf = buf->next_sibling; } while(buf); RESTORE_FLAGS(flags); /* now free them up */ buf = orig; if(buf->first_sibling) { buf = buf->first_sibling; } do { if(buf == orig) { buf = buf->next_sibling; continue; } if(buf->flags & BUFFER_DIRTY) { if(!sync_one_buffer(buf)) { remove_from_dirty_list(buf); } else { /* FIXME: undo all locks and give up */ } } tmp = buf; buf = buf->next_sibling; remove_from_hash(tmp); remove_from_free_list(tmp); kstat.buffers_size -= tmp->size / 1024; del_buffer_from_pool(tmp); } while(buf); return 0; } /* * When kernel runs out of pages, kswapd is awaken to call this function which * goes across the buffer cache, freeing up to NR_BUF_RECLAIM pages. */ int reclaim_buffers(void) { struct buffer *buf; int size, found, reclaimed; unsigned int flags, mark; found = reclaimed = 0; size = BLKSIZE_1K; mark = kstat.uptime; /* iterate through all buffer sizes */ STI(); for(;;) { if((buf = get_free_buffer(NO_GROW, size))) { if(buf->mark == mark) { SAVE_FLAGS(flags); CLI(); buf->flags &= ~BUFFER_LOCKED; buf->mark = 0; append_on_free_list(buf); RESTORE_FLAGS(flags); goto next; } found++; if(buf->flags & BUFFER_DIRTY) { if(!sync_one_buffer(buf)) { remove_from_dirty_list(buf); } } if(reclaim_siblings(buf)) { /* * If one of the siblings is not eligible to be * freed up, then we release this buffer without * using brelse(), otherwise get_free_buffer() * will return the same buffer again. */ SAVE_FLAGS(flags); CLI(); buf->flags &= ~BUFFER_LOCKED; buf->mark = mark; append_on_free_list(buf); RESTORE_FLAGS(flags); continue; } kfree((unsigned int)(buf->data) & PAGE_MASK); remove_from_hash(buf); kstat.buffers_size -= buf->size / 1024; del_buffer_from_pool(buf); if(++reclaimed == NR_BUF_RECLAIM) { break; } continue; } next: size <<= 1; if(size > PAGE_SIZE) { if(!found) { break; } size = BLKSIZE_1K; found = 0; } } wakeup(&get_free_buffer); wakeup(&buffer_wait); /* * If some buffers were reclaimed, then wakeup any process * waiting for a new page because release_page() won't do it. */ if(reclaimed) { wakeup(&get_free_page); } return reclaimed; } int kbdflushd(void) { struct buffer *buf, *first; int flushed, size; for(;;) { sleep(&kbdflushd, PROC_INTERRUPTIBLE); flushed = 0; lock_resource(&sync_resource); for(size = BLKSIZE_1K; size <= PAGE_SIZE; size <<= 1) { first = NULL; for(;;) { if(!(buf = get_dirty_buffer(size))) { break; } if(!(buf->flags & BUFFER_DIRTY)) { printk("WARNING: %s(): a dirty buffer (dev %x, block %d, flags = %x) is not marked as dirty!\n", __FUNCTION__, buf->dev, buf->block, buf->flags); continue; } if(first) { if(first == buf) { insert_on_dirty_list(buf); buf->flags &= ~BUFFER_LOCKED; wakeup(&buffer_wait); break; } } else { first = buf; } if(sync_one_buffer(buf)) { insert_on_dirty_list(buf); buf->flags &= ~BUFFER_LOCKED; wakeup(&buffer_wait); continue; } buf->flags &= ~BUFFER_LOCKED; wakeup(&buffer_wait); flushed++; if(flushed >= NR_BUF_RECLAIM) { if(kstat.nr_dirty_buffers < kstat.max_dirty_buffers) { break; } do_sched(); } } } unlock_resource(&sync_resource); } } void buffer_init(void) { buffer_table = NULL; memset_b(buffer_head, 0, sizeof(buffer_head)); memset_b(buffer_dirty_head, 0, sizeof(buffer_dirty_head)); kstat.max_dirty_buffers = (kstat.max_buffers_size * BUFFER_DIRTY_RATIO) / 100; memset_b(buffer_hash_table, 0, buffer_hash_table_size); } ================================================ FILE: fs/devices.c ================================================ /* * fiwix/fs/devices.c * * Copyright 2018-2022, 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, /* llseek */ NULL, /* readdir */ NULL, /* readdir64 */ 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_llseek, NULL, /* readdir */ NULL, /* readdir64 */ 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; int n, minors; 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[new_d->major]; 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[new_d->major]; break; default: printk("WARNING: %s(): invalid device type %d.\n", __FUNCTION__, type); return 1; break; } /* make sure there are minors defined */ for(n = 0, minors = 0; n < 8; n++) { minors += new_d->minors[n]; } if(!minors) { printk("WARNING: %s(): device major %d with no defined minors.\n", __FUNCTION__, new_d->major); return 1; } if(*d) { if(&(*d)->minors == &new_d->minors || ((*d)->next && &(*d)->next->minors == &new_d->minors)) { return 1; } do { d = &(*d)->next; } while(*d); } *d = new_d; return 0; } void unregister_device(int type, struct device *device) { struct device **d, **tmp; int n; /* make sure there are not minors defined */ for(n = 0; n < 8; n++) { if(device->minors[n]) { return; } } switch(type) { case CHR_DEV: d = &chr_device_table[device->major]; break; default: printk("WARNING: %s(): invalid device type %d.\n", __FUNCTION__, type); return; } tmp = NULL; while(*d) { if(&(*d)->minors == &device->minors) { if(!tmp) { *d = (*d)->next; } else { *tmp = (*d)->next; } return; } tmp = d; d = &(*d)->next; } } struct device *get_device(int type, __dev_t dev) { char *name; unsigned char major; struct device *d; major = MAJOR(dev); 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[major]; 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[major]; name = "block"; break; default: printk("WARNING: %s(): invalid device type %d.\n", __FUNCTION__, type); return NULL; } while(d) { if(d->major == major) { if(TEST_MINOR(d->minors, MINOR(dev))) { return d; } d = d->next; continue; } break; } printk("WARNING: %s(): %s device %d,%d not found.\n", __FUNCTION__, name, major, MINOR(dev)); return NULL; } int chr_dev_open(struct inode *i, struct fd *f) { struct device *d; if((d = get_device(CHR_DEV, i->rdev))) { i->fsop = d->fsop; return i->fsop->open(i, f); } return -ENXIO; } int blk_dev_open(struct inode *i, struct fd *f) { struct device *d; if((d = get_device(BLK_DEV, i->rdev))) { return d->fsop->open(i, f); } return -ENXIO; } int blk_dev_close(struct inode *i, struct fd *f) { struct device *d; if((d = get_device(BLK_DEV, i->rdev))) { return d->fsop->close(i, f); } return -ENXIO; } int blk_dev_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { __blk_t block; __size_t total_read; __loff_t device_size; int n, blksize, blksize_bits; unsigned int boffset, bytes; struct buffer *buf; struct device *d; if(!(d = get_device(BLK_DEV, i->rdev))) { return -ENXIO; } 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; } blksize = ((unsigned int *)d->blksize)[MINOR(i->rdev)]; device_size = ((unsigned int *)d->device_data)[MINOR(i->rdev)]; device_size *= 1024LLU; for(n = blksize, blksize_bits = 0; n != 1; n >>= 1) { blksize_bits++; } count = (f->offset + count > device_size) ? device_size - f->offset : count; if(!count || f->offset > device_size) { return 0; } while(count) { boffset = f->offset & (blksize - 1); /* mod blksize */ block = (f->offset >> blksize_bits); 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; f->offset += bytes; brelse(buf); } return total_read; } int blk_dev_write(struct inode *i, struct fd *f, const char *buffer, __size_t count) { __blk_t block; __size_t total_written; __loff_t device_size; int n, blksize, blksize_bits; unsigned int boffset, bytes; struct buffer *buf; struct device *d; if(!(d = get_device(BLK_DEV, i->rdev))) { return -ENXIO; } 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; } blksize = ((unsigned int *)d->blksize)[MINOR(i->rdev)]; device_size = ((unsigned int *)d->device_data)[MINOR(i->rdev)]; device_size *= 1024LLU; for(n = blksize, blksize_bits = 0; n != 1; n >>= 1) { blksize_bits++; } count = (f->offset + count > device_size) ? device_size - f->offset : count; if(!count || f->offset > device_size) { return -ENOSPC; } while(count) { boffset = f->offset & (blksize - 1); /* mod blksize */ block = (f->offset >> blksize_bits); 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; f->offset += bytes; bwrite(buf); } return total_written; } int blk_dev_ioctl(struct inode *i, struct fd *f, int cmd, unsigned int arg) { struct device *d; if((d = get_device(BLK_DEV, i->rdev))) { return d->fsop->ioctl(i, f, cmd, arg); } return -ENXIO; } __loff_t blk_dev_llseek(struct inode *i, __loff_t offset) { struct device *d; if((d = get_device(BLK_DEV, i->rdev))) { return d->fsop->llseek(i, offset); } return -ENXIO; } void dev_init(void) { memset_b(chr_device_table, 0, sizeof(chr_device_table)); memset_b(blk_device_table, 0, sizeof(blk_device_table)); } ================================================ FILE: fs/devpts/Makefile ================================================ # fiwix/fs/devpts/Makefile # # Copyright 2025, Jordi Sanfeliu. All rights reserved. # Distributed under the terms of the Fiwix License. # .c.o: $(CC) $(CFLAGS) -c -o $@ $< OBJS = super.o inode.o namei.o dir.o all: $(OBJS) clean: rm -f *.o ================================================ FILE: fs/devpts/dir.c ================================================ /* * fiwix/fs/devpts/dir.c * * Copyright 2025, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #ifdef CONFIG_UNIX98_PTYS struct fs_operations devpts_dir_fsop = { 0, 0, devpts_dir_open, devpts_dir_close, devpts_dir_read, NULL, /* write */ NULL, /* ioctl */ NULL, /* llseek */ devpts_readdir, NULL, /* readdir64 */ NULL, /* mmap */ NULL, /* select */ NULL, /* readlink */ NULL, /* followlink */ NULL, /* bmap */ devpts_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 devpts_dir_open(struct inode *i, struct fd *f) { f->offset = 0; return 0; } int devpts_dir_close(struct inode *i, struct fd *f) { return 0; } int devpts_dir_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { return -EISDIR; } int devpts_readdir(struct inode *i, struct fd *f, struct dirent *dirent, __size_t count) { unsigned int offset; int rec_len, name_len; __size_t total_read; int base_dirent_len; char *name, numstr[10 + 1]; base_dirent_len = sizeof(dirent->d_ino) + sizeof(dirent->d_off) + sizeof(dirent->d_reclen); offset = f->offset; total_read = 0; memset_b(numstr, 0, sizeof(numstr)); while(offset < NR_PTYS && count > 0) { if(offset == 0) { name = "."; dirent->d_ino = DEVPTS_ROOT_INO; } else if(offset == 1) { name = ".."; dirent->d_ino = DEVPTS_ROOT_INO; } else { if(devpts_list[offset - 2].count) { dirent->d_ino = devpts_list[offset - 2].inode->inode; sprintk(numstr, "%d", offset - 2); name = numstr; } else { offset++; continue; } } name_len = strlen(name); rec_len = (base_dirent_len + (name_len + 1)) + 3; rec_len &= ~3; /* round up */ if(total_read + rec_len < count) { dirent->d_off = offset; dirent->d_reclen = rec_len; memcpy_b(dirent->d_name, name, name_len); dirent->d_name[name_len] = 0; dirent = (struct dirent *)((char *)dirent + rec_len); total_read += rec_len; count -= rec_len; } else { count = 0; } offset++; } f->offset = offset; return total_read; } #endif /* CONFIG_UNIX98_PTYS */ ================================================ FILE: fs/devpts/inode.c ================================================ /* * fiwix/fs/devpts/inode.c * * Copyright 2025, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #ifdef CONFIG_UNIX98_PTYS int devpts_read_inode(struct inode *i) { __mode_t mode; __nlink_t nlink; if(i->inode == DEVPTS_ROOT_INO) { mode = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; nlink = 2; i->fsop = &devpts_dir_fsop; } else { mode = S_IFCHR | S_IRUSR | S_IWUSR; nlink = 1; i->fsop = &def_chr_fsop; } 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->state = INODE_LOCKED; i->count = 1; return 0; } void devpts_statfs(struct superblock *sb, struct statfs *statfsbuf) { statfsbuf->f_type = DEVPTS_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; } #endif /* CONFIG_UNIX98_PTYS */ ================================================ FILE: fs/devpts/namei.c ================================================ /* * fiwix/fs/devpts/namei.c * * Copyright 2025, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #ifdef CONFIG_UNIX98_PTYS int devpts_lookup(const char *name, struct inode *dir, struct inode **i_res) { __ino_t inode; int numpty; if(name[0] == '.' && name[1] == '\0') { *i_res = dir; return 0; } else if(name[0] == '.' && name[1] == '.') { return 0; } if((numpty = atoi(name)) >= NR_PTYS) { iput(dir); return -ENOENT; } if(devpts_list[numpty].count) { inode = devpts_list[numpty].inode->inode; if(!(*i_res = iget(dir->sb, inode))) { iput(dir); return -EACCES; } iput(dir); return 0; } iput(dir); return -ENOENT; } #endif /* CONFIG_UNIX98_PTYS */ ================================================ FILE: fs/devpts/super.c ================================================ /* * fiwix/fs/devpts/super.c * * Copyright 2025, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_UNIX98_PTYS struct devpts_files *devpts_list; struct fs_operations devpts_fsop = { 0, DEVPTS_DEV, NULL, /* open */ NULL, /* close */ NULL, /* read */ NULL, /* write */ NULL, /* ioctl */ NULL, /* llseek */ NULL, /* readdir */ NULL, /* readdir64 */ 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 */ devpts_read_inode, NULL, /* write_inode */ devpts_ialloc, devpts_ifree, devpts_statfs, devpts_read_superblock, NULL, /* remount_fs */ NULL, /* write_superblock */ NULL /* release_superblock */ }; int devpts_ialloc(struct inode *i, int mode) { struct superblock *sb = i->sb; int n; superblock_lock(sb); for(n = 0; n < NR_PTYS; n++) { if(!devpts_list[n].count) { devpts_list[n].count = 1; break; } } superblock_unlock(sb); if(n == NR_PTYS) { return -ENOSPC; } i->i_mode = mode | S_IRUSR | S_IWUSR; i->dev = sb->dev; i->rdev = MKDEV(0, 0); i->inode = DEVPTS_ROOT_INO + 1 + n; i->count = 1; i->fsop = &def_chr_fsop; 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 = 1; i->i_blocks = 0; i->i_flags = 0; devpts_list[n].inode = i; return 0; } void devpts_ifree(struct inode *i) { int n; for(n = 0; n < NR_PTYS; n++) { if(devpts_list[n].inode == i) { devpts_list[n].count = 0; memset_b(&devpts_list[n], 0, sizeof(struct devpts_files)); return; } } } int devpts_read_superblock(__dev_t dev, struct superblock *sb) { superblock_lock(sb); sb->dev = dev; sb->fsop = &devpts_fsop; sb->s_blocksize = BLKSIZE_1K; if(!(sb->root = iget(sb, DEVPTS_ROOT_INO))) { printk("WARNING: %s(): unable to get root inode.\n", __FUNCTION__); superblock_unlock(sb); return -EINVAL; } superblock_unlock(sb); return 0; } int devpts_init(void) { if(!(devpts_list = (struct devpts_files *)kmalloc(sizeof(struct devpts_files) * NR_PTYS))) { return -ENOMEM; } memset_b(devpts_list, 0, sizeof(struct devpts_files) * NR_PTYS); return register_filesystem("devpts", &devpts_fsop); } #endif /* CONFIG_UNIX98_PTYS */ ================================================ FILE: fs/elf.c ================================================ /* * fiwix/fs/elf.c * * Copyright 2018-2022, 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 */ /* * Setup the initial process stack (UNIX 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 = PAGE_OFFSET - ((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 */ current->argc = barg->argc; *sp = barg->argc; #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++) { *sp = str_ptr; 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 */ *sp = 0; #ifdef __DEBUG__ printk("at 0x%08x -> -------------- = 0x%08x\n", sp, 0); #endif /*__DEBUG__ */ sp++; /* copy as many pointers to strings as 'envc' */ current->envc = barg->envc; current->envp = (char **)sp; for(n = 0; n < barg->envc; n++) { *sp = str_ptr; 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 */ *sp = 0; #ifdef __DEBUG__ printk("at 0x%08x -> -------------- = 0x%08x\n", sp, 0); #endif /*__DEBUG__ */ sp++; /* copy the Auxiliar Table Items (dlinfo_items) */ if(at_base) { *sp = AT_PHDR; #ifdef __DEBUG__ printk("at 0x%08x -> AT_PHDR = %d", sp, *sp); #endif /*__DEBUG__ */ sp++; *sp = (unsigned int)phdr_addr; #ifdef __DEBUG__ printk("\t\tAT_PHDR = 0x%08x\n", *sp); #endif /*__DEBUG__ */ sp++; *sp = AT_PHENT; #ifdef __DEBUG__ printk("at 0x%08x -> AT_PHENT = %d", sp, *sp); #endif /*__DEBUG__ */ sp++; *sp = sizeof(struct elf32_phdr); #ifdef __DEBUG__ printk("\t\tAT_PHENT = %d\n", *sp); #endif /*__DEBUG__ */ sp++; *sp = AT_PHNUM; #ifdef __DEBUG__ printk("at 0x%08x -> AT_PHNUM = %d", sp, *sp); #endif /*__DEBUG__ */ sp++; *sp = (unsigned int)elf32_h->e_phnum; #ifdef __DEBUG__ printk("\t\tAT_PHNUM = %d\n", *sp); #endif /*__DEBUG__ */ sp++; *sp = AT_PAGESZ; #ifdef __DEBUG__ printk("at 0x%08x -> AT_PGSIZE = %d", sp, *sp); #endif /*__DEBUG__ */ sp++; *sp = PAGE_SIZE; #ifdef __DEBUG__ printk("\t\tAT_PGSIZE = %d\n", *sp); #endif /*__DEBUG__ */ sp++; *sp = AT_BASE; #ifdef __DEBUG__ printk("at 0x%08x -> AT_BASE = %d", sp, *sp); #endif /*__DEBUG__ */ sp++; *sp = (unsigned int)at_base; #ifdef __DEBUG__ printk("\t\tAT_BASE = 0x%08x\n", sp); #endif /*__DEBUG__ */ sp++; *sp = AT_FLAGS; #ifdef __DEBUG__ printk("at 0x%08x -> AT_FLAGS = %d", sp, *sp); #endif /*__DEBUG__ */ sp++; *sp = 0; #ifdef __DEBUG__ printk("\t\tAT_FLAGS = %d\n", *sp); #endif /*__DEBUG__ */ sp++; *sp = AT_ENTRY; #ifdef __DEBUG__ printk("at 0x%08x -> AT_ENTRY = %d ", sp, *sp); #endif /*__DEBUG__ */ sp++; *sp = (unsigned int)elf32_h->e_entry; #ifdef __DEBUG__ printk("\t\tAT_ENTRY = 0x%08x\n", *sp); #endif /*__DEBUG__ */ sp++; *sp = AT_UID; #ifdef __DEBUG__ printk("at 0x%08x -> AT_UID = %d", sp, *sp); #endif /*__DEBUG__ */ sp++; *sp = current->uid; #ifdef __DEBUG__ printk("\t\tAT_UID = %d\n", *sp); #endif /*__DEBUG__ */ sp++; *sp = AT_EUID; #ifdef __DEBUG__ printk("at 0x%08x -> AT_EUID = %d", sp, *sp); #endif /*__DEBUG__ */ sp++; *sp = current->euid; #ifdef __DEBUG__ printk("\t\tAT_EUID = %d\n", *sp); #endif /*__DEBUG__ */ sp++; *sp = AT_GID; #ifdef __DEBUG__ printk("at 0x%08x -> AT_GID = %d", sp, *sp); #endif /*__DEBUG__ */ sp++; *sp = current->gid; #ifdef __DEBUG__ printk("\t\tAT_GID = %d\n", *sp); #endif /*__DEBUG__ */ sp++; *sp = AT_EGID; #ifdef __DEBUG__ printk("at 0x%08x -> AT_EGID = %d", sp, *sp); #endif /*__DEBUG__ */ sp++; *sp = current->egid; #ifdef __DEBUG__ printk("\t\tAT_EGID = %d\n", *sp); #endif /*__DEBUG__ */ sp++; } *sp = AT_NULL; #ifdef __DEBUG__ printk("at 0x%08x -> AT_NULL = %d", sp, *sp); #endif /*__DEBUG__ */ sp++; *sp = 0; #ifdef __DEBUG__ printk("\t\tAT_NULL = %d\n", *sp); #endif /*__DEBUG__ */ #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, offset; 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(PAGE_SIZE))) { 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; offset = elf32_ph->p_offset - (elf32_ph->p_vaddr & ~PAGE_MASK); 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, offset, type, O_RDONLY, NULL); if(errno < 0 && errno > -PAGE_SIZE) { kfree((unsigned int)data); send_sig(current, SIGSEGV); return -ENOEXEC; } last_ptload = elf32_ph; } } if(!last_ptload) { printk("%s(): no headers in interpreter."); kfree((unsigned int)data); return -ENOEXEC; } 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, 0, 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, NULL); 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 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; } int elf_load(struct inode *i, struct binargs *barg, struct sigcontext *sc, char *data) { int n, errno; struct elf32_hdr *elf32_h; struct elf32_phdr *elf32_ph, *last_ptload; struct inode *ii; unsigned int start, end, length, offset; unsigned int prot; char *interpreter; int at_base, phdr_addr; char type; unsigned int ae_ptr_len, ae_str_len; unsigned int sp, str; elf32_h = (struct elf32_hdr *)data; if(check_elf(elf32_h)) { 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); 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__ */ } } /* * 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; #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__ */ /* 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); iput(ii); send_sig(current, SIGKILL); return errno; } current->entry_address = errno; iput(ii); } 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; offset = elf32_ph->p_offset - (elf32_ph->p_vaddr & ~PAGE_MASK); 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; current->end_code = start + length; } errno = do_mmap(i, start, length, prot, MAP_PRIVATE | MAP_FIXED, offset, type, O_RDONLY, NULL); if(errno < 0 && errno > -PAGE_SIZE) { send_sig(current, SIGSEGV); return -ENOEXEC; } last_ptload = elf32_ph; } } if(!last_ptload) { printk("%s(): no program headers."); send_sig(current, SIGKILL); return -ENOEXEC; } 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, 0, 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, NULL); if(errno < 0 && errno > -PAGE_SIZE) { 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, NULL); if(errno < 0 && errno > -PAGE_SIZE) { send_sig(current, SIGSEGV); return -ENOEXEC; } current->brk = start; /* setup the STACK section */ sp = PAGE_OFFSET - 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 = PAGE_OFFSET - (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, NULL); if(errno < 0 && errno > -PAGE_SIZE) { send_sig(current, SIGSEGV); return -ENOEXEC; } elf_create_stack(barg, (unsigned int *)sp, str, at_base, elf32_h, phdr_addr); /* 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; } ================================================ FILE: fs/ext2/Makefile ================================================ # fiwix/fs/ext2/Makefile # # Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. # Distributed under the terms of the Fiwix License. # .c.o: $(CC) $(CFLAGS) -c -o $@ $< OBJS = inode.o super.o namei.o symlink.o dir.o file.o bitmaps.o all: $(OBJS) clean: rm -f *.o ================================================ FILE: fs/ext2/bitmaps.c ================================================ /* * fiwix/fs/ext2/bitmaps.c * * Copyright 2019, 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 find_first_zero(struct superblock *sb, __blk_t bmblock, struct buffer **buf) { unsigned char c; int n, n2; if(!(*buf = bread(sb->dev, bmblock, sb->s_blocksize))) { return -EIO; } for(n = 0; n < sb->s_blocksize; n++) { if((c = (unsigned char)(*buf)->data[n]) == 0xFF) { continue; } for(n2 = 0; n2 < 8; n2++) { if(!(c & (1 << n2))) { return (n * 8) + n2; } } } return -ENOSPC; } static int change_bit(int mode, struct superblock *sb, __blk_t bmblock, struct buffer *bmbuf, int item) { int byte, bit, mask; struct buffer *buf; byte = (item % (sb->s_blocksize * 8)) / 8; bit = (item % (sb->s_blocksize * 8)) % 8; mask = 1 << bit; if(!bmbuf) { if(!(buf = bread(sb->dev, bmblock, sb->s_blocksize))) { return -EIO; } } else { buf = bmbuf; } 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; } /* * Unlike of what Ext2 specifies/suggests, this inode allocation does NOT * try to assign inodes in the same block group of the directory in which * they will be created. */ int ext2_ialloc(struct inode *i, int mode) { __ino_t inode; __blk_t block; struct superblock *sb; struct ext2_group_desc *gd; struct buffer *buf, *bmbuf; int bg, d, errno; sb = i->sb; superblock_lock(sb); block = SUPERBLOCK + sb->u.ext2.sb.s_first_data_block; buf = bmbuf = NULL; gd = NULL; errno = -ENOSPC; /* read through all group descriptors to find the first unallocated inode */ for(bg = 0, d = 0; bg < sb->u.ext2.block_groups; bg++, d++) { if(!(bg % EXT2_DESC_PER_BLOCK(sb))) { if(buf) { brelse(buf); block++; d = 0; } if(!(buf = bread(sb->dev, block, sb->s_blocksize))) { superblock_unlock(sb); return -EIO; } } gd = (struct ext2_group_desc *)(buf->data + (d * sizeof(struct ext2_group_desc))); if(gd->bg_free_inodes_count) { if((errno = find_first_zero(sb, gd->bg_inode_bitmap, &bmbuf)) != -ENOSPC) { break; } brelse(bmbuf); } } if(errno < 0) { brelse(buf); superblock_unlock(sb); return errno; } inode = errno; errno = change_bit(SET_BIT, sb, gd->bg_inode_bitmap, bmbuf, inode); if(errno) { if(errno < 0) { printk("WARNING: %s(): unable to set inode %d.\n", __FUNCTION__, inode); brelse(buf); superblock_unlock(sb); return errno; } else { printk("WARNING: %s(): inode %d is already marked as used!\n", __FUNCTION__, inode); } } inode += (bg * EXT2_INODES_PER_GROUP(sb)) + 1; gd->bg_free_inodes_count--; sb->u.ext2.sb.s_free_inodes_count--; sb->state |= SUPERBLOCK_DIRTY; if(S_ISDIR(mode)) { gd->bg_used_dirs_count++; } bwrite(buf); i->inode = inode; i->i_atime = CURRENT_TIME; i->i_mtime = CURRENT_TIME; i->i_ctime = CURRENT_TIME; superblock_unlock(sb); return 0; } void ext2_ifree(struct inode *i) { struct ext2_group_desc *gd; struct buffer *buf; struct superblock *sb; __blk_t b, bg; int errno; if(!i->inode || i->inode > i->sb->u.ext2.sb.s_inodes_count) { printk("WARNING: %s(): invalid inode %d!\n", __FUNCTION__, i->inode); return; } if(i->i_blocks) { invalidate_inode_pages(i); ext2_truncate(i, 0); } sb = i->sb; superblock_lock(sb); b = SUPERBLOCK + sb->u.ext2.sb.s_first_data_block; bg = (i->inode - 1) / EXT2_INODES_PER_GROUP(sb); if(!(buf = bread(sb->dev, b + (bg / EXT2_DESC_PER_BLOCK(sb)), sb->s_blocksize))) { superblock_unlock(sb); return; } gd = (struct ext2_group_desc *)(buf->data + ((bg % EXT2_DESC_PER_BLOCK(sb)) * sizeof(struct ext2_group_desc))); errno = change_bit(CLEAR_BIT, sb, gd->bg_inode_bitmap, NULL, (i->inode - 1) % EXT2_INODES_PER_GROUP(sb)); if(errno) { if(errno < 0) { printk("WARNING: %s(): unable to free inode %d.\n", __FUNCTION__, i->inode); brelse(buf); superblock_unlock(sb); return; } else { printk("WARNING: %s(): inode %d is already marked as free!\n", __FUNCTION__, i->inode); } } gd->bg_free_inodes_count++; sb->u.ext2.sb.s_free_inodes_count++; sb->state |= SUPERBLOCK_DIRTY; if(S_ISDIR(i->i_mode)) { gd->bg_used_dirs_count--; } bwrite(buf); i->i_size = 0; i->i_mtime = CURRENT_TIME; i->i_ctime = CURRENT_TIME; i->state |= INODE_DIRTY; superblock_unlock(sb); return; } int ext2_balloc(struct superblock *sb) { __blk_t block; struct ext2_group_desc *gd; struct buffer *buf, *bmbuf; int bg, d, errno; superblock_lock(sb); block = SUPERBLOCK + sb->u.ext2.sb.s_first_data_block; buf = bmbuf = NULL; gd = NULL; errno = -ENOSPC; /* read through all group descriptors to find the first unallocated block */ for(bg = 0, d = 0; bg < sb->u.ext2.block_groups; bg++, d++) { if(!(bg % EXT2_DESC_PER_BLOCK(sb))) { if(buf) { brelse(buf); block++; d = 0; } if(!(buf = bread(sb->dev, block, sb->s_blocksize))) { superblock_unlock(sb); return -EIO; } } gd = (struct ext2_group_desc *)(buf->data + (d * sizeof(struct ext2_group_desc))); if(gd->bg_free_blocks_count) { if((errno = find_first_zero(sb, gd->bg_block_bitmap, &bmbuf)) != -ENOSPC) { break; } brelse(bmbuf); } } if(errno < 0) { brelse(buf); superblock_unlock(sb); return errno; } block = errno; errno = change_bit(SET_BIT, sb, gd->bg_block_bitmap, bmbuf, block); if(errno) { if(errno < 0) { printk("WARNING: %s(): unable to set block %d.\n", __FUNCTION__, block); brelse(buf); superblock_unlock(sb); return errno; } else { printk("WARNING: %s(): block %d is already marked as used!\n", __FUNCTION__, block); } } block += (bg * EXT2_BLOCKS_PER_GROUP(sb)) + sb->u.ext2.sb.s_first_data_block; gd->bg_free_blocks_count--; sb->u.ext2.sb.s_free_blocks_count--; sb->state |= SUPERBLOCK_DIRTY; bwrite(buf); superblock_unlock(sb); return block; } void ext2_bfree(struct superblock *sb, int block) { struct ext2_group_desc *gd; struct buffer *buf; __blk_t b, bg; int errno; if(!block || block > sb->u.ext2.sb.s_blocks_count) { printk("WARNING: %s(): invalid block %d!\n", __FUNCTION__, block); return; } superblock_lock(sb); b = SUPERBLOCK + sb->u.ext2.sb.s_first_data_block; bg = (block - sb->u.ext2.sb.s_first_data_block) / EXT2_BLOCKS_PER_GROUP(sb); if(!(buf = bread(sb->dev, b + (bg / EXT2_DESC_PER_BLOCK(sb)), sb->s_blocksize))) { superblock_unlock(sb); return; } gd = (struct ext2_group_desc *)(buf->data + ((bg % EXT2_DESC_PER_BLOCK(sb)) * sizeof(struct ext2_group_desc))); errno = change_bit(CLEAR_BIT, sb, gd->bg_block_bitmap, NULL, (block - sb->u.ext2.sb.s_first_data_block) % EXT2_BLOCKS_PER_GROUP(sb)); if(errno) { if(errno < 0) { printk("WARNING: %s(): unable to free block %d.\n", __FUNCTION__, block); brelse(buf); superblock_unlock(sb); return; } else { printk("WARNING: %s(): block %d is already marked as free!\n", __FUNCTION__, block); } } gd->bg_free_blocks_count++; sb->u.ext2.sb.s_free_blocks_count++; sb->state |= SUPERBLOCK_DIRTY; bwrite(buf); superblock_unlock(sb); return; } ================================================ FILE: fs/ext2/dir.c ================================================ /* * fiwix/fs/ext2/dir.c * * Copyright 2018-2022, 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, /* llseek */ ext2_readdir, ext2_readdir64, NULL, /* mmap */ NULL, /* select */ NULL, /* readlink */ NULL, /* followlink */ ext2_bmap, ext2_lookup, ext2_rmdir, ext2_link, ext2_unlink, ext2_symlink, ext2_mkdir, ext2_mknod, NULL, /* truncate */ ext2_create, ext2_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 *f) { f->offset = 0; return 0; } int ext2_dir_close(struct inode *i, struct fd *f) { return 0; } int ext2_dir_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { return -EISDIR; } int ext2_readdir(struct inode *i, struct fd *f, struct dirent *dirent, __size_t 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(f->offset > i->i_size) { f->offset = i->i_size; } base_dirent_len = sizeof(dirent->d_ino) + sizeof(dirent->d_off) + sizeof(dirent->d_reclen); offset = size = 0; while(f->offset < i->i_size && count > 0) { if((block = bmap(i, f->offset, FOR_READING)) < 0) { return block; } if(block) { if(!(buf = bread(i->dev, block, blksize))) { return -EIO; } doffset = f->offset; offset = f->offset & (blksize - 1); /* mod blksize */ while(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] = 0; dirent = (struct dirent *)((char *)dirent + dirent_len); size += dirent_len; count -= dirent_len; } else { count = 0; break; } } doffset += d->rec_len; offset += d->rec_len; if(!d->rec_len) { break; } } brelse(buf); } f->offset &= ~(blksize - 1); f->offset += offset; } return size; } int ext2_readdir64(struct inode *i, struct fd *f, struct dirent64 *dirent, __size_t 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(f->offset > i->i_size) { f->offset = i->i_size; } base_dirent_len = sizeof(dirent->d_ino) + sizeof(dirent->d_off) + sizeof(dirent->d_reclen) + sizeof(dirent->d_type); offset = size = 0; while(f->offset < i->i_size && count > 0) { if((block = bmap(i, f->offset, FOR_READING)) < 0) { return block; } if(block) { if(!(buf = bread(i->dev, block, blksize))) { return -EIO; } doffset = f->offset; offset = f->offset & (blksize - 1); /* mod blksize */ while(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) { struct inode *dirent_inode = iget(i->sb, dirent->d_ino); 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] = 0; if (S_ISREG(dirent_inode->i_mode)) { dirent->d_type = DT_REG; } else if (S_ISDIR(dirent_inode->i_mode)) { dirent->d_type = DT_DIR; } else if (S_ISCHR(dirent_inode->i_mode)) { dirent->d_type = DT_CHR; } else if (S_ISBLK(dirent_inode->i_mode)) { dirent->d_type = DT_BLK; } else if (S_ISFIFO(dirent_inode->i_mode)) { dirent->d_type = DT_FIFO; } else if (S_ISSOCK(dirent_inode->i_mode)) { dirent->d_type = DT_SOCK; } else if (S_ISLNK(dirent_inode->i_mode)) { dirent->d_type = DT_LNK; } else { dirent->d_type = DT_UNKNOWN; } iput(dirent_inode); dirent = (struct dirent64 *)((char *)dirent + dirent_len); size += dirent_len; count -= dirent_len; } else { count = 0; break; } } doffset += d->rec_len; offset += d->rec_len; if(!d->rec_len) { break; } } brelse(buf); } f->offset &= ~(blksize - 1); f->offset += offset; } return size; } ================================================ FILE: fs/ext2/file.c ================================================ /* * fiwix/fs/ext2/file.c * * Copyright 2018-2021, 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 struct fs_operations ext2_file_fsop = { 0, 0, ext2_file_open, ext2_file_close, file_read, ext2_file_write, NULL, /* ioctl */ ext2_file_llseek, NULL, /* readdir */ NULL, /* readdir64 */ NULL, /* mmap */ NULL, /* select */ NULL, /* readlink */ NULL, /* followlink */ ext2_bmap, NULL, /* lookup */ NULL, /* rmdir */ NULL, /* link */ NULL, /* unlink */ NULL, /* symlink */ NULL, /* mkdir */ NULL, /* mknod */ ext2_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 *f) { f->offset = 0; if(f->flags & O_TRUNC) { i->i_size = 0; ext2_truncate(i, 0); } return 0; } int ext2_file_close(struct inode *i, struct fd *f) { return 0; } int ext2_file_write(struct inode *i, struct fd *f, const char *buffer, __size_t count) { __blk_t block; __size_t total_written; unsigned int boffset, bytes; int blksize, retval; struct buffer *buf; struct device *d; struct blk_request brh, *br, *tmp; #ifdef CONFIG_OFFSET64 __loff_t offset; #else __off_t offset; #endif /* CONFIG_OFFSET64 */ inode_lock(i); blksize = i->sb->s_blocksize; retval = total_written = 0; if(f->flags & O_APPEND) { f->offset = i->i_size; } offset = f->offset; if(count > blksize) { if(!(d = get_device(BLK_DEV, i->dev))) { printk("WARNING: %s(): device major %d not found!\n", __FUNCTION__, MAJOR(i->dev)); inode_unlock(i); return -ENXIO; } memset_b(&brh, 0, sizeof(struct blk_request)); tmp = NULL; while(total_written < count) { if(!(br = (struct blk_request *)kmalloc(sizeof(struct blk_request)))) { printk("WARNING: %s(): no more free memory for block requests.\n", __FUNCTION__); retval = -ENOMEM; break; } if((block = bmap(i, offset, FOR_WRITING)) < 0) { retval = block; break; } memset_b(br, 0, sizeof(struct blk_request)); br->dev = i->dev; br->block = block; br->size = blksize; br->device = d; br->fn = d->fsop->read_block; br->head_group = &brh; if(!brh.next_group) { brh.next_group = br; } else { tmp->next_group = br; } tmp = br; boffset = offset & (blksize - 1); /* mod blksize */ bytes = blksize - boffset; bytes = MIN(bytes, (count - total_written)); total_written += bytes; offset += bytes; } if(!retval) { retval = gbread(d, &brh); } br = brh.next_group; offset = f->offset; total_written = 0; while(br) { if(!retval) { boffset = offset & (blksize - 1); /* mod blksize */ bytes = blksize - boffset; bytes = MIN(bytes, (count - total_written)); memcpy_b(br->buffer->data + boffset, buffer + total_written, bytes); update_page_cache(i, offset, buffer + total_written, bytes); bwrite(br->buffer); total_written += bytes; offset += bytes; } else { if(br->buffer) { brelse(br->buffer); } } tmp = br->next_group; kfree((unsigned int)br); br = tmp; } } else { while(total_written < count) { boffset = offset & (blksize - 1); /* mod blksize */ if((block = bmap(i, offset, FOR_WRITING)) < 0) { retval = block; break; } bytes = blksize - boffset; bytes = MIN(bytes, (count - total_written)); if(!(buf = bread(i->dev, block, blksize))) { retval = -EIO; break; } memcpy_b(buf->data + boffset, buffer + total_written, bytes); update_page_cache(i, offset, buffer + total_written, bytes); bwrite(buf); total_written += bytes; offset += bytes; } } if(!retval) { f->offset = offset; if(f->offset > i->i_size) { i->i_size = f->offset; } i->i_ctime = CURRENT_TIME; i->i_mtime = CURRENT_TIME; i->state |= INODE_DIRTY; } inode_unlock(i); if(retval) { return retval; } return total_written; } __loff_t ext2_file_llseek(struct inode *i, __loff_t offset) { return offset; } ================================================ FILE: fs/ext2/inode.c ================================================ /* * fiwix/fs/ext2/inode.c * * Copyright 2018-2022, 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 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 free_dblock(struct inode *i, int block, int offset) { int n; struct buffer *buf; __blk_t *dblock; if(!(buf = bread(i->dev, block, i->sb->s_blocksize))) { printk("WARNING: %s(): error reading block %d.\n", __FUNCTION__, block); return -EIO; } dblock = (__blk_t *)buf->data; for(n = offset; n < BLOCKS_PER_IND_BLOCK(i->sb); n++) { if(dblock[n]) { ext2_bfree(i->sb, dblock[n]); dblock[n] = 0; i->i_blocks -= i->sb->s_blocksize / 512; } } bwrite(buf); return 0; } static int free_indblock(struct inode *i, int block, int offset) { int n, retval; struct buffer *buf; __blk_t dblock, *indblock; if(!(buf = bread(i->dev, block, i->sb->s_blocksize))) { printk("%s(): error reading doubly indirect block %d.\n", __FUNCTION__, block); return -EIO; } indblock = (__blk_t *)buf->data; dblock = offset % BLOCKS_PER_IND_BLOCK(i->sb); for(n = offset / BLOCKS_PER_IND_BLOCK(i->sb); n < BLOCKS_PER_IND_BLOCK(i->sb); n++) { if(indblock[n]) { if((retval = free_dblock(i, indblock[n], dblock)) < 0) { brelse(buf); return retval; } if(!dblock) { ext2_bfree(i->sb, indblock[n]); indblock[n] = 0; i->i_blocks -= i->sb->s_blocksize / 512; } } dblock = 0; } bwrite(buf); return 0; } static int get_group_desc(struct superblock *sb, __blk_t block_group, struct ext2_group_desc *gd) { __blk_t group_desc_block; int group_desc; struct buffer *buf; group_desc_block = block_group / EXT2_DESC_PER_BLOCK(sb); group_desc = block_group % EXT2_DESC_PER_BLOCK(sb); if(!(buf = bread(sb->dev, SUPERBLOCK + sb->u.ext2.sb.s_first_data_block + group_desc_block, 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) { __blk_t block_group, 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; } block_group = ((i->inode - 1) / EXT2_INODES_PER_GROUP(sb)); if(get_group_desc(sb, block_group, &gd)) { return -EIO; } block = (((i->inode - 1) % EXT2_INODES_PER_GROUP(sb)) / 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) % EXT2_INODES_PER_GROUP(sb)) % EXT2_INODES_PER_BLOCK(sb)) * sizeof(struct ext2_inode)); ii = (struct ext2_inode *)(buf->data + offset); memcpy_b(&i->u.ext2.i_data, ii->i_block, sizeof(ii->i_block)); i->i_mode = ii->i_mode; i->i_uid = (ii->osd2.linux2.l_i_uid_high << 16) | 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->osd2.linux2.l_i_gid_high << 16) | 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, 0, 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: #ifdef CONFIG_NET i->fsop = &sockfs_fsop; /* it's a union so we need to clear sockfs_inode */ memset_b(&i->u.sockfs, 0, sizeof(struct sockfs_inode)); #else i->fsop = NULL; #endif /* CONFIG_NET */ 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_write_inode(struct inode *i) { __blk_t block_group, block; short 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; } block_group = ((i->inode - 1) / EXT2_INODES_PER_GROUP(sb)); if(get_group_desc(sb, block_group, &gd)) { return -EIO; } block = (((i->inode - 1) % EXT2_INODES_PER_GROUP(sb)) / 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) % EXT2_INODES_PER_GROUP(sb)) % EXT2_INODES_PER_BLOCK(sb)) * sizeof(struct ext2_inode)); ii = (struct ext2_inode *)(buf->data + offset); memset_b(ii, 0, sizeof(struct ext2_inode)); ii->i_mode = i->i_mode; ii->i_uid = i->i_uid & 0xFFFF; ii->osd2.linux2.l_i_uid_high = i->i_uid >> 16; ii->i_size = i->i_size; ii->i_atime = i->i_atime; ii->i_ctime = i->i_ctime; ii->i_mtime = i->i_mtime; ii->i_dtime = i->u.ext2.i_dtime; ii->i_gid = i->i_gid & 0xFFFF; ii->osd2.linux2.l_i_gid_high = i->i_gid >> 16; ii->i_links_count = i->i_nlink; ii->i_blocks = i->i_blocks; ii->i_flags = i->i_flags; if(S_ISCHR(i->i_mode) || S_ISBLK(i->i_mode)) { ii->i_block[0] = i->rdev; } else { memcpy_b(ii->i_block, &i->u.ext2.i_data, sizeof(i->u.ext2.i_data)); } i->state &= ~INODE_DIRTY; bwrite(buf); return 0; } int ext2_bmap(struct inode *i, __off_t offset, int mode) { unsigned char level; __blk_t *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 >> EXT2_BLOCK_SIZE_BITS(i->sb); level = 0; buf3 = NULL; /* makes GCC happy */ 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; } else if(block < ((BLOCKS_PER_IND_BLOCK(i->sb) * BLOCKS_PER_IND_BLOCK(i->sb)) + BLOCKS_PER_IND_BLOCK(i->sb) + EXT2_NDIR_BLOCKS)) { level = EXT2_DIND_BLOCK; } else { level = EXT2_TIND_BLOCK; } block -= EXT2_NDIR_BLOCKS; } if(level < EXT2_NDIR_BLOCKS) { if(!i->u.ext2.i_data[block] && mode == FOR_WRITING) { if((newblock = ext2_balloc(i->sb)) < 0) { return -ENOSPC; } /* initialize the new block */ if(!(buf = bread(i->dev, newblock, blksize))) { ext2_bfree(i->sb, newblock); return -EIO; } memset_b(buf->data, 0, blksize); bwrite(buf); i->u.ext2.i_data[block] = newblock; i->i_blocks += blksize / 512; } return i->u.ext2.i_data[block]; } if(!i->u.ext2.i_data[level]) { if(mode == FOR_WRITING) { if((newblock = ext2_balloc(i->sb)) < 0) { return -ENOSPC; } /* initialize the new block */ if(!(buf = bread(i->dev, newblock, blksize))) { ext2_bfree(i->sb, newblock); return -EIO; } memset_b(buf->data, 0, blksize); bwrite(buf); i->u.ext2.i_data[level] = newblock; i->i_blocks += blksize / 512; } else { return 0; } } if(!(buf = bread(i->dev, i->u.ext2.i_data[level], blksize))) { return -EIO; } indblock = (__blk_t *)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 == EXT2_DIND_BLOCK) { block = dblock / BLOCKS_PER_IND_BLOCK(i->sb); } if(level == EXT2_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 = ext2_balloc(i->sb)) < 0) { brelse(buf); return -ENOSPC; } /* initialize the new block */ if(!(buf2 = bread(i->dev, newblock, blksize))) { ext2_bfree(i->sb, newblock); brelse(buf); return -EIO; } memset_b(buf2->data, 0, blksize); bwrite(buf2); indblock[block] = newblock; i->i_blocks += blksize / 512; if(level == EXT2_IND_BLOCK) { bwrite(buf); return newblock; } buf->flags |= (BUFFER_DIRTY | BUFFER_VALID); } else { brelse(buf); return 0; } } if(level == EXT2_IND_BLOCK) { newblock = indblock[block]; brelse(buf); return newblock; } if(level == EXT2_TIND_BLOCK) { if(!(buf3 = bread(i->dev, indblock[block], blksize))) { printk("%s(): returning -EIO\n", __FUNCTION__); brelse(buf); return -EIO; } tindblock = (__blk_t *)buf3->data; tblock -= BLOCKS_PER_DIND_BLOCK(i->sb) * block; block = tindblock[tblock / BLOCKS_PER_IND_BLOCK(i->sb)]; if(!block) { if(mode == FOR_WRITING) { if((newblock = ext2_balloc(i->sb)) < 0) { brelse(buf); brelse(buf3); return -ENOSPC; } /* initialize the new block */ if(!(buf4 = bread(i->dev, newblock, blksize))) { ext2_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; i->i_blocks += blksize / 512; buf3->flags |= (BUFFER_DIRTY | BUFFER_VALID); 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 = (__blk_t *)buf2->data; block = dindblock[dblock - (iblock * BLOCKS_PER_IND_BLOCK(i->sb))]; if(!block && mode == FOR_WRITING) { if((newblock = ext2_balloc(i->sb)) < 0) { brelse(buf); if(level == EXT2_TIND_BLOCK) { brelse(buf3); } brelse(buf2); return -ENOSPC; } /* initialize the new block */ if(!(buf4 = bread(i->dev, newblock, blksize))) { ext2_bfree(i->sb, newblock); brelse(buf); if(level == EXT2_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; i->i_blocks += blksize / 512; buf2->flags |= (BUFFER_DIRTY | BUFFER_VALID); block = newblock; } brelse(buf); if(level == EXT2_TIND_BLOCK) { brelse(buf3); } brelse(buf2); return block; } int ext2_truncate(struct inode *i, __off_t length) { __blk_t block, indblock, *dindblock; struct buffer *buf; int n, retval, blksize; blksize = i->sb->s_blocksize; block = length >> EXT2_BLOCK_SIZE_BITS(i->sb); if(!S_ISDIR(i->i_mode) && !S_ISREG(i->i_mode) && !S_ISLNK(i->i_mode)) { return -EINVAL; } if(block < EXT2_NDIR_BLOCKS) { for(n = block; n < EXT2_NDIR_BLOCKS; n++) { if(i->u.ext2.i_data[n]) { ext2_bfree(i->sb, i->u.ext2.i_data[n]); i->u.ext2.i_data[n] = 0; i->i_blocks -= blksize / 512; } } block = 0; } if(!block || block < (BLOCKS_PER_IND_BLOCK(i->sb) + EXT2_NDIR_BLOCKS)) { if(block) { block -= EXT2_NDIR_BLOCKS; } if(i->u.ext2.i_data[EXT2_IND_BLOCK]) { if((retval = free_dblock(i, i->u.ext2.i_data[EXT2_IND_BLOCK], block)) < 0) { return retval; } if(!block) { ext2_bfree(i->sb, i->u.ext2.i_data[EXT2_IND_BLOCK]); i->u.ext2.i_data[EXT2_IND_BLOCK] = 0; i->i_blocks -= blksize / 512; } } block = 0; } if(!block || block < (BLOCKS_PER_DIND_BLOCK(i->sb) + BLOCKS_PER_IND_BLOCK(i->sb) + EXT2_NDIR_BLOCKS)) { if(block) { block -= EXT2_NDIR_BLOCKS; block -= BLOCKS_PER_IND_BLOCK(i->sb); } if(i->u.ext2.i_data[EXT2_DIND_BLOCK]) { if((retval = free_indblock(i, i->u.ext2.i_data[EXT2_DIND_BLOCK], block)) < 0) { return retval; } if(!block) { ext2_bfree(i->sb, i->u.ext2.i_data[EXT2_DIND_BLOCK]); i->u.ext2.i_data[EXT2_DIND_BLOCK] = 0; i->i_blocks -= blksize / 512; } } block = 0; } if(!block || block < (BLOCKS_PER_TIND_BLOCK(i->sb) + BLOCKS_PER_DIND_BLOCK(i->sb) + BLOCKS_PER_IND_BLOCK(i->sb) + EXT2_NDIR_BLOCKS)) { if(block) { block -= EXT2_NDIR_BLOCKS; block -= BLOCKS_PER_IND_BLOCK(i->sb); block -= BLOCKS_PER_DIND_BLOCK(i->sb); } if(i->u.ext2.i_data[EXT2_TIND_BLOCK]) { if(!(buf = bread(i->dev, i->u.ext2.i_data[EXT2_TIND_BLOCK], blksize))) { printk("%s(): error reading the triply indirect block (%d).\n", __FUNCTION__, i->u.ext2.i_data[EXT2_TIND_BLOCK]); return -EIO; } dindblock = (__blk_t *)buf->data; indblock = 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(dindblock[n]) { if((retval = free_indblock(i, dindblock[n], indblock)) < 0) { brelse(buf); return retval; } if(!indblock) { ext2_bfree(i->sb, dindblock[n]); dindblock[n] = 0; i->i_blocks -= blksize / 512; } } indblock = 0; } bwrite(buf); if(!block) { ext2_bfree(i->sb, i->u.ext2.i_data[EXT2_TIND_BLOCK]); i->u.ext2.i_data[EXT2_TIND_BLOCK] = 0; i->i_blocks -= blksize / 512; } } } i->i_mtime = CURRENT_TIME; i->i_ctime = CURRENT_TIME; i->i_size = length; i->state |= INODE_DIRTY; return 0; } ================================================ FILE: fs/ext2/namei.c ================================================ /* * fiwix/fs/ext2/namei.c * * Copyright 2018-2022, 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 /* finds a new entry to fit 'name' in the directory 'dir' */ static struct buffer *find_first_free_dir_entry(struct inode *dir, struct ext2_dir_entry_2 **d_res, char *name) { __blk_t block; unsigned int blksize; unsigned int offset, doffset; struct buffer *buf; int basesize, rlen, nlen; basesize = sizeof((*d_res)->inode) + sizeof((*d_res)->rec_len) + sizeof((*d_res)->name_len) + sizeof((*d_res)->file_type); blksize = dir->sb->s_blocksize; offset = 0; /* * nlen is the length of the new entry to be used when searching for * the first usable entry. */ nlen = basesize + strlen(name) + 3; nlen &= ~3; 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 ext2_dir_entry_2 *)(buf->data + doffset); /* calculates the real length of the current entry */ rlen = basesize + strlen((*d_res)->name) + 3; rlen &= ~3; /* returns the first entry where name can fit in */ if(!(*d_res)->inode) { if(nlen <= (*d_res)->rec_len) { return buf; } } else { if(rlen + nlen <= (*d_res)->rec_len) { int nrec_len; nrec_len = (*d_res)->rec_len - rlen; (*d_res)->rec_len = rlen; doffset += (*d_res)->rec_len; *d_res = (struct ext2_dir_entry_2 *)(buf->data + doffset); (*d_res)->rec_len = nrec_len; return buf; } } doffset += (*d_res)->rec_len; } while(doffset < blksize); brelse(buf); offset += blksize; } else { break; } } *d_res = NULL; return NULL; } /* finds an entry in 'dir' based on the 'name' and/or on the inode 'i' */ static struct buffer *find_dir_entry(struct inode *dir, struct inode *i, struct ext2_dir_entry_2 **d_res, char *name) { __blk_t block; unsigned int blksize; unsigned int offset, doffset; struct buffer *buf; int basesize, nlen; basesize = sizeof((*d_res)->inode) + sizeof((*d_res)->rec_len) + sizeof((*d_res)->name_len) + sizeof((*d_res)->file_type); blksize = dir->sb->s_blocksize; offset = 0; /* * nlen is the length of the new entry to be used when searching for * the first usable entry. */ nlen = basesize + strlen(name) + 3; nlen &= ~3; 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 ext2_dir_entry_2 *)(buf->data + doffset); if(!i) { if((*d_res)->inode) { /* returns the first matching name */ if((*d_res)->name_len == strlen(name)) { if(!strncmp((*d_res)->name, name, (*d_res)->name_len)) { 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((*d_res)->name_len == strlen(name)) { if(!strncmp((*d_res)->name, name, (*d_res)->name_len)) { return buf; } } } } doffset += (*d_res)->rec_len; } while(doffset < blksize); brelse(buf); offset += blksize; } else { break; } } *d_res = NULL; return NULL; } static struct buffer *add_dir_entry(struct inode *dir, struct ext2_dir_entry_2 **d_res, char *name) { __blk_t block; struct buffer *buf; if(!(buf = find_first_free_dir_entry(dir, d_res, name))) { 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 ext2_dir_entry_2 *)buf->data; dir->i_size += dir->sb->s_blocksize; (*d_res)->rec_len = dir->sb->s_blocksize; } return buf; } static int is_dir_empty(struct inode *dir) { __blk_t block; unsigned int blksize; unsigned int offset, doffset; struct buffer *buf; struct ext2_dir_entry_2 *d; 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 { if(doffset + offset >= dir->i_size) { break; } d = (struct ext2_dir_entry_2 *)(buf->data + doffset); doffset += d->rec_len; if(d->inode && d->name_len == 1 && d->name[0] == '.') { continue; } if(d->inode && d->name_len == 2 && d->name[0] == '.' && d->name[1] == '.') { continue; } if(d->inode) { brelse(buf); return 0; } } while(doffset < blksize); brelse(buf); offset += blksize; } else { break; } } return 1; } static int is_subdir(struct inode *dir_new, struct inode *i_old) { __ino_t inode; int errno; errno = 0; dir_new->count++; for(;;) { if(dir_new == i_old) { errno = 1; break; } inode = dir_new->inode; if(ext2_lookup("..", dir_new, &dir_new)) { break; } if(dir_new->inode == inode) { break; } } iput(dir_new); return errno; } 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; 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))) { iput(dir); return -EIO; } doffset = 0; do { d = (struct ext2_dir_entry_2 *)(buf->data + doffset); /* check dir entry */ if(d->rec_len < EXT2_DIR_REC_LEN(1)) { break; } 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; } 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))) { iput(dir); return -EACCES; } iput(dir); return 0; } } else { break; } } iput(dir); return -ENOENT; } int ext2_rmdir(struct inode *dir, struct inode *i) { struct buffer *buf; struct ext2_dir_entry_2 *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; i->u.ext2.i_dtime = CURRENT_TIME; dir->i_mtime = CURRENT_TIME; dir->i_ctime = CURRENT_TIME; i->state |= INODE_DIRTY; dir->state |= INODE_DIRTY; bwrite(buf); inode_unlock(i); inode_unlock(dir); return 0; } int ext2_link(struct inode *i_old, struct inode *dir_new, char *name) { struct buffer *buf; struct ext2_dir_entry_2 *d; char c; int n; inode_lock(i_old); inode_lock(dir_new); if(!(buf = add_dir_entry(dir_new, &d, name))) { inode_unlock(i_old); inode_unlock(dir_new); return -ENOSPC; } d->inode = i_old->inode; d->name_len = strlen(name); /* strcpy() can't be used here because it places a trailing NULL */ for(n = 0; n < NAME_MAX; n++) { if((c = name[n])) { d->name[n] = c; continue; } break; } d->file_type = 0; /* not used */ i_old->i_nlink++; i_old->i_ctime = CURRENT_TIME; dir_new->i_mtime = CURRENT_TIME; dir_new->i_ctime = CURRENT_TIME; i_old->state |= INODE_DIRTY; dir_new->state |= INODE_DIRTY; bwrite(buf); inode_unlock(i_old); inode_unlock(dir_new); return 0; } int ext2_unlink(struct inode *dir, struct inode *i, char *name) { struct buffer *buf; struct ext2_dir_entry_2 *d; inode_lock(dir); inode_lock(i); if(!(buf = find_dir_entry(dir, i, &d, name))) { inode_unlock(dir); inode_unlock(i); return -ENOENT; } /* * FIXME: in order to avoid low performance when traversing large * directories plenty of blank entries, it would be interesting * to merge every removed entry with the previous entry. */ d->inode = 0; if(!--i->i_nlink) { i->u.ext2.i_dtime = CURRENT_TIME; } i->i_ctime = CURRENT_TIME; dir->i_mtime = CURRENT_TIME; dir->i_ctime = CURRENT_TIME; i->state |= INODE_DIRTY; dir->state |= INODE_DIRTY; bwrite(buf); inode_unlock(dir); inode_unlock(i); return 0; } int ext2_symlink(struct inode *dir, char *name, char *oldname) { struct buffer *buf, *buf2; struct inode *i; struct ext2_dir_entry_2 *d; __blk_t block; char c, *data; int n; inode_lock(dir); /* check again to know if this filename already exists */ if((buf = find_dir_entry(dir, NULL, &d, name))) { brelse(buf); inode_unlock(dir); return -EEXIST; } if(!(i = ialloc(dir->sb, S_IFLNK))) { inode_unlock(dir); return -ENOSPC; } if(!(buf = add_dir_entry(dir, &d, name))) { iput(i); 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->dev = dir->dev; i->count = 1; i->fsop = &ext2_symlink_fsop; if(strlen(oldname) >= EXT2_N_BLOCKS * sizeof(__u32)) { /* this will be a slow symlink */ if((block = ext2_balloc(dir->sb)) < 0) { iput(i); brelse(buf); inode_unlock(dir); return block; } if(!(buf2 = bread(dir->dev, block, dir->sb->s_blocksize))) { iput(i); brelse(buf); ext2_bfree(dir->sb, block); inode_unlock(dir); return -EIO; } i->u.ext2.i_data[0] = block; for(n = 0; n < NAME_MAX; n++) { if((c = oldname[n])) { buf2->data[n] = c; continue; } break; } buf2->data[n] = 0; i->i_blocks = dir->sb->s_blocksize / 512; bwrite(buf2); } else { /* this will be a fast symlink */ data = (char *)i->u.ext2.i_data; for(n = 0; n < NAME_MAX; n++) { if((c = oldname[n])) { data[n] = c; continue; } break; } data[n] = 0; } i->i_size = n; i->state |= INODE_DIRTY; i->i_nlink = 1; d->inode = i->inode; d->name_len = strlen(name); /* strcpy() can't be used here because it places a trailing NULL */ for(n = 0; n < NAME_MAX; n++) { if((c = name[n])) { d->name[n] = c; continue; } break; } d->file_type = 0; /* EXT2_FT_SYMLINK not used */ dir->i_mtime = CURRENT_TIME; dir->i_ctime = CURRENT_TIME; dir->state |= INODE_DIRTY; bwrite(buf); iput(i); inode_unlock(dir); return 0; } int ext2_mkdir(struct inode *dir, char *name, __mode_t mode) { struct buffer *buf, *buf2; struct inode *i; struct ext2_dir_entry_2 *d, *d2; __blk_t block; char c; int n; inode_lock(dir); /* check again to know if this filename already exists */ if((buf = find_dir_entry(dir, NULL, &d, name))) { brelse(buf); inode_unlock(dir); return -EEXIST; } if(!(i = ialloc(dir->sb, S_IFDIR))) { 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->dev = dir->dev; i->count = 1; i->fsop = &ext2_dir_fsop; if((block = bmap(i, 0, FOR_WRITING)) < 0) { iput(i); inode_unlock(dir); return block; } if(!(buf2 = bread(i->dev, block, dir->sb->s_blocksize))) { ext2_bfree(dir->sb, block); iput(i); inode_unlock(dir); return -EIO; } if(!(buf = add_dir_entry(dir, &d, name))) { ext2_bfree(dir->sb, block); iput(i); brelse(buf2); inode_unlock(dir); return -ENOSPC; } d->inode = i->inode; d->name_len = strlen(name); /* strcpy() can't be used here because it places a trailing NULL */ for(n = 0; n < NAME_MAX; n++) { if((c = name[n])) { if(c != '/') { d->name[n] = c; continue; } } break; } d->file_type = 0; /* EXT2_FT_DIR not used */ d2 = (struct ext2_dir_entry_2 *)buf2->data; d2->inode = i->inode; d2->name[0] = '.'; d2->name[1] = 0; d2->name_len = 1; d2->rec_len = 12; d2->file_type = 0; /* EXT2_FT_DIR not used */ i->i_nlink = 1; d2 = (struct ext2_dir_entry_2 *)(buf2->data + 12); d2->inode = dir->inode; d2->name[0] = '.'; d2->name[1] = '.'; d2->name[2] = 0; d2->name_len = 2; d2->rec_len = i->sb->s_blocksize - 12; d2->file_type = 0; /* EXT2_FT_DIR not used */ i->i_nlink++; i->i_size = i->sb->s_blocksize; i->i_blocks = dir->sb->s_blocksize / 512; i->state |= INODE_DIRTY; dir->i_mtime = CURRENT_TIME; dir->i_ctime = CURRENT_TIME; dir->i_nlink++; dir->state |= INODE_DIRTY; bwrite(buf); bwrite(buf2); iput(i); inode_unlock(dir); return 0; } int ext2_mknod(struct inode *dir, char *name, __mode_t mode, __dev_t dev) { struct buffer *buf; struct inode *i; struct ext2_dir_entry_2 *d; char c; int n; inode_lock(dir); /* check again to know if this filename already exists */ if((buf = find_dir_entry(dir, NULL, &d, name))) { brelse(buf); inode_unlock(dir); return -EEXIST; } if(!(i = ialloc(dir->sb, mode & S_IFMT))) { inode_unlock(dir); return -ENOSPC; } if(!(buf = add_dir_entry(dir, &d, name))) { i->i_nlink = 0; iput(i); inode_unlock(dir); return -ENOSPC; } d->inode = i->inode; d->name_len = strlen(name); /* strcpy() can't be used here because it places a trailing NULL */ for(n = 0; n < NAME_MAX; n++) { if((c = name[n])) { d->name[n] = c; continue; } break; } 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->state |= INODE_DIRTY; switch(mode & S_IFMT) { case S_IFCHR: i->fsop = &def_chr_fsop; i->rdev = dev; i->i_mode |= S_IFCHR; d->file_type = 0; /* EXT2_FT_CHRDEV not used */ break; case S_IFBLK: i->fsop = &def_blk_fsop; i->rdev = dev; i->i_mode |= S_IFBLK; d->file_type = 0; /* EXT2_FT_BLKDEV not used */ 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, 0, sizeof(struct pipefs_inode)); d->file_type = 0; /* EXT2_FT_FIFO not used */ break; #ifdef CONFIG_NET case S_IFSOCK: i->fsop = &sockfs_fsop; i->i_mode |= S_IFSOCK; /* it's a union so we need to clear sockfs_inode */ memset_b(&i->u.sockfs, 0, sizeof(struct sockfs_inode)); d->file_type = 0; /* EXT2_FT_SOCK not used */ break; #endif /* CONFIG_NET */ } dir->i_mtime = CURRENT_TIME; dir->i_ctime = CURRENT_TIME; dir->state |= INODE_DIRTY; bwrite(buf); iput(i); inode_unlock(dir); return 0; } int ext2_create(struct inode *dir, char *name, int flags, __mode_t mode, struct inode **i_res) { struct buffer *buf; struct inode *i; struct ext2_dir_entry_2 *d; char c; int n; if(IS_RDONLY_FS(dir)) { return -EROFS; } inode_lock(dir); if(flags & O_CREAT) { /* check again to know if this filename already exists */ if((buf = find_dir_entry(dir, NULL, &d, name))) { brelse(buf); inode_unlock(dir); return -EEXIST; } } if(!(i = ialloc(dir->sb, S_IFREG))) { inode_unlock(dir); return -ENOSPC; } if(!(buf = add_dir_entry(dir, &d, name))) { i->i_nlink = 0; iput(i); inode_unlock(dir); return -ENOSPC; } d->inode = i->inode; d->name_len = strlen(name); /* strcpy() can't be used here because it places a trailing NULL */ for(n = 0; n < NAME_MAX; n++) { if((c = name[n])) { d->name[n] = c; continue; } break; } d->file_type = 0; /* EXT2_FT_REG_FILE not used */ 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->i_blocks = 0; i->dev = dir->dev; i->fsop = &ext2_file_fsop; i->count = 1; i->state |= INODE_DIRTY; i->u.ext2.i_dtime = 0; dir->i_mtime = CURRENT_TIME; dir->i_ctime = CURRENT_TIME; dir->state |= INODE_DIRTY; *i_res = i; bwrite(buf); inode_unlock(dir); return 0; } int ext2_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 ext2_dir_entry_2 *d_old, *d_new; char c; int n, errno; errno = 0; if(is_subdir(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) { /* free that buffer now to not block buf_new */ brelse(buf_old); buf_old = NULL; } if(i_new) { if(S_ISDIR(i_old->i_mode)) { if(!is_dir_empty(i_new)) { if(buf_old) { brelse(buf_old); } errno = -ENOTEMPTY; goto end; } } if(!(buf_new = find_dir_entry(dir_new, i_new, &d_new, newpath))) { if(buf_old) { brelse(buf_old); } errno = -ENOENT; goto end; } } else { if(!(buf_new = add_dir_entry(dir_new, &d_new, newpath))) { if(buf_old) { brelse(buf_old); } errno = -ENOSPC; goto end; } if(S_ISDIR(i_old->i_mode)) { dir_old->i_nlink--; dir_new->i_nlink++; } } if(i_new) { i_new->i_nlink--; } else { i_new = i_old; d_new->name_len = strlen(newpath); /* strcpy() can't be used here because it places a trailing NULL */ for(n = 0; n < NAME_MAX; n++) { if((c = newpath[n])) { d_new->name[n] = c; continue; } break; } } d_new->inode = i_old->inode; dir_new->i_mtime = CURRENT_TIME; dir_new->i_ctime = CURRENT_TIME; i_new->state |= INODE_DIRTY; dir_new->state |= INODE_DIRTY; dir_old->i_mtime = CURRENT_TIME; dir_old->i_ctime = CURRENT_TIME; i_old->state |= INODE_DIRTY; dir_old->state |= INODE_DIRTY; bwrite(buf_new); if(!buf_old) { if(!(buf_old = find_dir_entry(dir_old, i_old, &d_old, oldpath))) { errno = -ENOENT; goto end; } } d_old->inode = 0; bwrite(buf_old); /* update the parent directory */ if(S_ISDIR(i_old->i_mode)) { buf_new = find_dir_entry(i_old, dir_old, &d_new, ".."); if(buf_new) { d_new->inode = dir_new->inode; bwrite(buf_new); } } end: inode_unlock(i_old); inode_unlock(dir_old); inode_unlock(dir_new); return errno; } ================================================ FILE: fs/ext2/super.c ================================================ /* * fiwix/fs/ext2/super.c * * Copyright 2018-2022, 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, 0, NULL, /* open */ NULL, /* close */ NULL, /* read */ NULL, /* write */ NULL, /* ioctl */ NULL, /* llseek */ NULL, /* readdir */ NULL, /* readdir64 */ 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, ext2_write_inode, ext2_ialloc, ext2_ifree, ext2_statfs, ext2_read_superblock, ext2_remount_fs, ext2_write_superblock, ext2_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.sb.s_blocks_count; statfsbuf->f_bfree = sb->u.ext2.sb.s_free_blocks_count; if(statfsbuf->f_bfree >= sb->u.ext2.sb.s_r_blocks_count) { statfsbuf->f_bavail = statfsbuf->f_bfree - sb->u.ext2.sb.s_r_blocks_count; } else { statfsbuf->f_bavail = 0; } statfsbuf->f_files = sb->u.ext2.sb.s_inodes_count; statfsbuf->f_ffree = sb->u.ext2.sb.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))) { printk("WARNING: %s(): I/O error on device %d,%d.\n", __FUNCTION__, MAJOR(dev), MINOR(dev)); 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; } if(ext2sb->s_minor_rev_level || ext2sb->s_rev_level) { printk("WARNING: %s(): unsupported ext2 filesystem revision.\n", __FUNCTION__); printk("Only revision 0 (original without features) is supported.\n"); superblock_unlock(sb); brelse(buf); return -EINVAL; } sb->dev = dev; sb->fsop = &ext2_fsop; sb->s_blocksize_bits = ext2sb->s_log_block_size + EXT2_MIN_BLOCK_LOG_SIZE; sb->s_blocksize = 1 << sb->s_blocksize_bits; memcpy_b(&sb->u.ext2.sb, ext2sb, sizeof(struct ext2_super_block)); sb->u.ext2.desc_per_block = sb->s_blocksize / sizeof(struct ext2_group_desc); sb->u.ext2.block_groups = 1 + (ext2sb->s_blocks_count - 1) / EXT2_BLOCKS_PER_GROUP(sb); 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; } check_superblock(ext2sb); if(!(sb->flags & MS_RDONLY)) { sb->u.ext2.sb.s_state &= ~EXT2_VALID_FS; sb->u.ext2.sb.s_mnt_count++; sb->u.ext2.sb.s_mtime = CURRENT_TIME; memcpy_b(buf->data, &sb->u.ext2.sb, sizeof(struct ext2_super_block)); bwrite(buf); } else { brelse(buf); } superblock_unlock(sb); return 0; } int ext2_remount_fs(struct superblock *sb, int flags) { struct buffer *buf; struct ext2_super_block *ext2sb; 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; } ext2sb = (struct ext2_super_block *)buf->data; if(flags & MS_RDONLY) { /* switching from RW to RO */ sb->u.ext2.sb.s_state |= EXT2_VALID_FS; ext2sb->s_state |= EXT2_VALID_FS; } else { /* switching from RO to RW */ check_superblock(ext2sb); memcpy_b(&sb->u.ext2.sb, ext2sb, sizeof(struct ext2_super_block)); sb->u.ext2.sb.s_state &= ~EXT2_VALID_FS; sb->u.ext2.sb.s_mnt_count++; sb->u.ext2.sb.s_mtime = CURRENT_TIME; ext2sb->s_state &= ~EXT2_VALID_FS; } sb->state = SUPERBLOCK_DIRTY; superblock_unlock(sb); bwrite(buf); return 0; } int ext2_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.ext2.sb, sizeof(struct ext2_super_block)); sb->state &= ~SUPERBLOCK_DIRTY; superblock_unlock(sb); bwrite(buf); return 0; } void ext2_release_superblock(struct superblock *sb) { if(sb->flags & MS_RDONLY) { return; } superblock_lock(sb); sb->u.ext2.sb.s_state |= EXT2_VALID_FS; sb->state = SUPERBLOCK_DIRTY; superblock_unlock(sb); } int ext2_init(void) { return register_filesystem("ext2", &ext2_fsop); } ================================================ FILE: fs/ext2/symlink.c ================================================ /* * fiwix/fs/ext2/symlink.c * * Copyright 2018-2022, 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, /* llseek */ NULL, /* readdir */ NULL, /* readdir64 */ 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_data[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_data, count); } buffer[count] = 0; 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) { iput(i); 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_data[0], i->sb->s_blocksize))) { inode_unlock(i); return -EIO; } name = buf->data; } else { /* fast symlink */ buf = NULL; name = (char *)i->u.ext2.i_data; } 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; } ================================================ FILE: fs/fd.c ================================================ /* * fiwix/fs/fd.c * * Copyright 2018-2022, 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 = { 0, 0 }; 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], 0, 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); } 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; } void fd_init(void) { memset_b(fd_table, 0, fd_table_size); } ================================================ FILE: fs/filesystems.c ================================================ /* * fiwix/fs/filesystems.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include struct filesystems filesystems_table[NR_FILESYSTEMS]; 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, 0, sizeof(filesystems_table)); #ifdef CONFIG_FS_MINIX if(minix_init()) { printk("%s(): unable to register 'minix' filesystem.\n", __FUNCTION__); } #endif /* CONFIG_FS_MINIX */ 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__); } #ifdef CONFIG_NET if(sockfs_init()) { printk("%s(): unable to register 'sockfs' filesystem.\n", __FUNCTION__); } #endif /* CONFIG_NET */ #ifdef CONFIG_UNIX98_PTYS if(devpts_init()) { printk("%s(): unable to register 'devpts' filesystem.\n", __FUNCTION__); } #endif /* CONFIG_UNIX98_PTYS */ } ================================================ FILE: fs/inode.c ================================================ /* * fiwix/fs/inode.c * * Copyright 2018-2023, 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 #include #define INODE_HASH(dev, inode) (((__dev_t)(dev) ^ (__ino_t)(inode)) % (NR_INO_HASH)) #define NR_INO_HASH (inode_hash_table_size / sizeof(unsigned int)) struct inode *inode_table; /* inode pool */ struct inode *inode_head; /* head of free list */ struct inode **inode_hash_table; static struct resource sync_resource = { 0, 0 }; static struct inode *add_inode_to_pool(void) { unsigned int flags; struct inode *i; if(!(i = (struct inode *)kmalloc(sizeof(struct inode)))) { return NULL; } memset_b(i, 0, sizeof(struct inode)); SAVE_FLAGS(flags); CLI(); if(!inode_table) { inode_table = i; } else { i->prev = inode_table->prev; inode_table->prev->next = i; } inode_table->prev = i; RESTORE_FLAGS(flags); kstat.nr_inodes++; return i; } static void del_inode_from_pool(struct inode *i) { unsigned int flags; struct inode *tmp; tmp = i; if(!i->next && !i->prev) { printk("WARNING: %s(): trying to delete an unexistent inode (%d).\n", __FUNCTION__, i->inode); return; } SAVE_FLAGS(flags); CLI(); if(i->next) { i->next->prev = i->prev; } if(i->prev) { if(i != inode_table) { i->prev->next = i->next; } } if(!i->next) { inode_table->prev = i->prev; } if(i == inode_table) { inode_table = i->next; } RESTORE_FLAGS(flags); kfree((unsigned int)tmp); kstat.nr_inodes--; } 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; if(!i->inode) { return; } 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 insert_on_free_list(struct inode *i) { if(!inode_head) { inode_head = i; } else { i->prev_free = inode_head->prev_free; inode_head->prev_free->next_free = i; } inode_head->prev_free = i; } static void remove_from_free_list(struct inode *i) { if(!inode_head) { return; } if(i->next_free) { i->next_free->prev_free = i->prev_free; } if(i->prev_free) { if(i != inode_head) { i->prev_free->next_free = i->next_free; } } if(!i->next_free) { inode_head->prev_free = i->prev_free; } if(i == inode_head) { inode_head = i->next_free; } i->prev_free = i->next_free = NULL; } static struct inode *get_free_inode(void) { unsigned int flags; struct inode *i; if(kstat.nr_inodes < kstat.max_inodes) { if(!(i = add_inode_to_pool())) { return NULL; } return i; } SAVE_FLAGS(flags); CLI(); if(!(i = inode_head)) { /* no more inodes on free list */ RESTORE_FLAGS(flags); return NULL; } 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->mount_point = NULL; i->state = 0; i->dev = 0; i->inode = 0; i->count = 0; i->rdev = 0; i->fsop = NULL; i->sb = NULL; memset_b(&i->u, 0, sizeof(i->u)); RESTORE_FLAGS(flags); return i; } static int read_inode(struct inode *i) { int errno; inode_lock(i); errno = i->sb->fsop->read_inode(i); inode_unlock(i); return errno; } static int write_inode(struct inode *i) { int errno; if(i->sb && i->sb->fsop && i->sb->fsop->write_inode) { errno = i->sb->fsop->write_inode(i); } else { /* PIPE_DEV inodes can't be flushed on disk */ i->state &= ~INODE_DIRTY; errno = 0; } 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 void wait_on_inode(struct inode *i) { for(;;) { if(i->state & INODE_LOCKED) { sleep(i, PROC_UNINTERRUPTIBLE); } else { break; } } } void inode_lock(struct inode *i) { unsigned int flags; for(;;) { SAVE_FLAGS(flags); CLI(); if(i->state & INODE_LOCKED) { RESTORE_FLAGS(flags); sleep(i, PROC_UNINTERRUPTIBLE); } else { break; } } i->state |= INODE_LOCKED; RESTORE_FLAGS(flags); } void inode_unlock(struct inode *i) { unsigned int flags; SAVE_FLAGS(flags); CLI(); i->state &= ~INODE_LOCKED; wakeup(i); RESTORE_FLAGS(flags); } struct inode *ialloc(struct superblock *sb, int mode) { struct inode *i; if((i = get_free_inode())) { i->sb = sb; i->rdev = sb->dev; if(i->sb->fsop->ialloc(i, mode)) { 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 int flags; struct inode *i; if(!inode) { return NULL; } for(;;) { if((i = search_inode_hash(sb->dev, inode))) { SAVE_FLAGS(flags); CLI(); if(i->state & INODE_LOCKED) { sleep(i, PROC_UNINTERRUPTIBLE); RESTORE_FLAGS(flags); continue; } if(i->mount_point) { i = i->mount_point; } inode_lock(i); if(!i->count) { remove_from_free_list(i); } i->count++; inode_unlock(i); RESTORE_FLAGS(flags); return i; } if(!(i = get_free_inode())) { printk("WARNING: %s(): no more inodes on free list!\n", __FUNCTION__); 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)) { SAVE_FLAGS(flags); CLI(); i->count = 0; insert_on_free_list(i); RESTORE_FLAGS(flags); return NULL; } insert_to_hash(i); return i; } } int bmap(struct inode *i, __off_t offset, int mode) { return i->fsop->bmap(i, offset, mode); } int check_fs_busy(__dev_t dev, struct inode *root) { struct inode *i; i = inode_table; while(i) { if(i->dev == dev && i->count) { if(i == root && i->count == 1) { i = i->next; 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; } i = i->next; } return 0; } void iput(struct inode *i) { unsigned int flags; /* this solves the problem with rmdir('/') and iput(dir) which is NULL */ if(!i) { return; } wait_on_inode(i); if(!i->count) { printk("WARNING: %s(): trying to free an already freed inode (%d)!\n", __FUNCTION__, i->inode); return; } if(--i->count > 0) { return; } inode_lock(i); if(!i->i_nlink) { if(i->sb && i->sb->fsop && i->sb->fsop->ifree) { i->sb->fsop->ifree(i); } remove_from_hash(i); } if(i->state & INODE_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)); if(!i->i_nlink) { remove_from_hash(i); } i->count++; inode_unlock(i); return; } } SAVE_FLAGS(flags); CLI(); inode_unlock(i); if(i->rdev > FS_NODEV) { /* inodes from pseudo-filesystems don't need to be cached */ remove_from_hash(i); del_inode_from_pool(i); } else { insert_on_free_list(i); } RESTORE_FLAGS(flags); } void sync_inodes(__dev_t dev) { struct inode *i; i = inode_table; lock_resource(&sync_resource); while(i) { if(i->state & INODE_DIRTY) { if(!dev || i->dev == dev) { inode_lock(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)); } inode_unlock(i); } } i = i->next; } unlock_resource(&sync_resource); } void invalidate_inodes(__dev_t dev) { unsigned int flags; struct inode *i; i = inode_table; SAVE_FLAGS(flags); CLI(); while(i) { if(i->dev == dev) { inode_lock(i); remove_from_hash(i); inode_unlock(i); } i = i->next; } RESTORE_FLAGS(flags); } void inode_init(void) { inode_table = inode_head = NULL; memset_b(inode_hash_table, 0, inode_hash_table_size); } ================================================ FILE: fs/iso9660/Makefile ================================================ # fiwix/fs/iso9660/Makefile # # Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. # Distributed under the terms of the Fiwix License. # .c.o: $(CC) $(CFLAGS) -c -o $@ $< OBJS = inode.o super.o namei.o dir.o file.o rrip.o symlink.o all: $(OBJS) clean: rm -f *.o ================================================ FILE: fs/iso9660/dir.c ================================================ /* * fiwix/fs/iso9660/dir.c * * Copyright 2018-2022, 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, /* llseek */ iso9660_readdir, NULL, /* readdir64 */ 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 *f) { f->offset = 0; return 0; } int iso9660_dir_close(struct inode *i, struct fd *f) { return 0; } int iso9660_dir_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { return -EISDIR; } int iso9660_readdir(struct inode *i, struct fd *f, struct dirent *dirent, __size_t 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(f->offset > i->i_size) { f->offset = i->i_size; } base_dirent_len = sizeof(dirent->d_ino) + sizeof(dirent->d_off) + sizeof(dirent->d_reclen); doffset = size = 0; while(doffset < count) { if((block = bmap(i, f->offset, FOR_READING)) < 0) { return block; } if(block) { if(!(buf = bread(i->dev, block, blksize))) { return -EIO; } doffset = f->offset; offset = f->offset & (blksize - 1); /* mod 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 + dirent_len) < 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] = 0; } else if(isonum_711(d->name_len) == 1 && d->name[0] == 1) { dirent->d_name[0] = '.'; dirent->d_name[1] = '.'; dirent->d_name[2] = 0; 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) { dirent->d_reclen = (base_dirent_len + nm_len + 1) + 3; dirent->d_reclen &= ~3; /* round up */ dirent_len = dirent->d_reclen; if((size + dirent_len) < count) { dirent->d_name[nm_len] = 0; memcpy_b(dirent->d_name, nm_name, nm_len); } else { break; } } else { memcpy_b(dirent->d_name, d->name, isonum_711(d->name_len)); dirent->d_name[isonum_711(d->name_len)] = 0; } } 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 { doffset &= ~(blksize - 1); doffset += blksize; break; } } brelse(buf); } f->offset = doffset; } return size; } ================================================ FILE: fs/iso9660/file.c ================================================ /* * 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_llseek, NULL, /* readdir */ NULL, /* readdir64 */ 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 *f) { if(f->flags & (O_WRONLY | O_RDWR | O_TRUNC | O_APPEND)) { return -ENOENT; } f->offset = 0; return 0; } int iso9660_file_close(struct inode *i, struct fd *f) { return 0; } __loff_t iso9660_file_llseek(struct inode *i, __loff_t offset) { return offset; } ================================================ FILE: fs/iso9660/inode.c ================================================ /* * fiwix/fs/iso9660/inode.c * * Copyright 2018-2022, 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(PAGE_SIZE))) { 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(PAGE_SIZE))) { 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->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, 0, 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; } ================================================ FILE: fs/iso9660/namei.c ================================================ /* * fiwix/fs/iso9660/namei.c * * Copyright 2018-2022, 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); 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] == '\0') { if(isonum_711(d->name_len) == 1 && d->name[0] == 0) { inode = dir->inode; } } } if(len == 2) { if(name[0] == '.' && name[1] == '.' && name[2] == '\0') { if(isonum_711(d->name_len) == 1 && d->name[0] == 1) { inode = dir->u.iso9660.i_parent->inode; } } } if(!(nm_name = (char *)kmalloc(PAGE_SIZE))) { 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; } ================================================ FILE: fs/iso9660/rrip.c ================================================ /* * fiwix/fs/iso9660/rrip.c * * Copyright 2018-2022, 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] = 0; 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] = 0; break; } len += rrip->len; } if(ce_block) { brelse(buf2); } brelse(buf); return nm_len; } ================================================ FILE: fs/iso9660/super.c ================================================ /* * fiwix/fs/iso9660/super.c * * Copyright 2018-2022, 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, 0, NULL, /* open */ NULL, /* close */ NULL, /* read */ NULL, /* write */ NULL, /* ioctl */ NULL, /* llseek */ NULL, /* readdir */ NULL, /* readdir64 */ 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 int isodate(const char *p) { struct tm tm; if(!p[0]) { return 0; } tm.tm_sec = p[5]; tm.tm_min = p[4]; tm.tm_hour = p[3]; tm.tm_mday = p[2]; tm.tm_month = p[1]; tm.tm_year = p[0]; tm.tm_year += 1900; tm.tm_min += p[6] * 15; return mktime(&tm); } /* 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] = 0; if(p[n - 1] == '.') { filename[n - 1] = 0; } 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 = (struct iso9660_super_block *)kmalloc(sizeof(struct iso9660_super_block)))) { 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); } ================================================ FILE: fs/iso9660/symlink.c ================================================ /* * fiwix/fs/iso9660/symlink.c * * Copyright 2018-2022, 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, /* llseek */ NULL, /* readdir */ NULL, /* readdir64 */ 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(PAGE_SIZE))) { return -ENOMEM; } inode_lock(i); name[0] = 0; 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; __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(PAGE_SIZE))) { return -ENOMEM; } name[0] = 0; if(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; } ================================================ FILE: fs/locks.c ================================================ /* * fiwix/fs/locks.c * * Copyright 2018-2022, 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 flock_file *flock_file_table = NULL; static struct resource flock_resource = { 0, 0 }; static struct flock_file *get_new_flock(struct inode *i) { struct flock_file *ff; if(kstat.nr_flocks + 1 > NR_FLOCKS) { printk("WARNING: tried to exceed NR_FLOCKS (%d).\n", NR_FLOCKS); return NULL; } if(!(ff = (struct flock_file *)kmalloc(sizeof(struct flock_file)))) { return NULL; } memset_b(ff, 0, sizeof(struct flock_file)); lock_resource(&flock_resource); ff->inode = i; /* mark it as busy */ if(!flock_file_table) { flock_file_table = ff; } else { ff->prev = flock_file_table->prev; flock_file_table->prev->next = ff; } flock_file_table->prev = ff; unlock_resource(&flock_resource); kstat.nr_flocks++; return ff; } static void release_flock(struct flock_file *ff) { unsigned int flags; struct flock_file *tmp; tmp = ff; if(!ff->next && !ff->prev) { printk("WARNING: %s(): trying to release an unexistent flock.\n", __FUNCTION__); return; } SAVE_FLAGS(flags); CLI(); if(ff->next) { ff->next->prev = ff->prev; } if(ff->prev) { if(ff != flock_file_table) { ff->prev->next = ff->next; } } if(!ff->next) { flock_file_table->prev = ff->prev; } if(ff == flock_file_table) { flock_file_table = ff->next; } RESTORE_FLAGS(flags); kfree((unsigned int)tmp); kstat.nr_flocks--; } static struct flock_file *get_flock_file(struct inode *i, struct proc *p) { struct flock_file *ff; lock_resource(&flock_resource); ff = flock_file_table; while(ff) { if(ff->inode == i && p && p == ff->proc) { break; } ff = ff->next; } unlock_resource(&flock_resource); return ff; } int posix_lock(int ufd, int cmd, struct flock *fl) { struct flock_file *ff; struct inode *i; unsigned char type; lock_resource(&flock_resource); ff = flock_file_table; i = fd_table[current->fd[ufd]].inode; while(ff) { if(ff->inode == i) { break;; } ff = ff->next; } unlock_resource(&flock_resource); if(cmd == F_GETLK) { if(ff && 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) { struct flock_file *ff; lock_resource(&flock_resource); ff = flock_file_table; while(ff) { if(ff->inode == i && ff->proc == current) { wakeup(ff); release_flock(ff); } ff = ff->next; } unlock_resource(&flock_resource); } int flock_inode(struct inode *i, int op) { struct flock_file *ff, *new; if(op & LOCK_UN) { if((ff = get_flock_file(i, current))) { wakeup(ff); release_flock(ff); } return 0; } loop: lock_resource(&flock_resource); ff = flock_file_table; new = NULL; while(ff) { if(ff->inode == i) { 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; ff = ff->next; continue; } unlock_resource(&flock_resource); if(op & LOCK_NB) { return -EWOULDBLOCK; } if(sleep(ff, PROC_INTERRUPTIBLE)) { return -EINTR; } goto loop; } } ff = ff->next; } 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; } ================================================ FILE: fs/minix/Makefile ================================================ # fiwix/fs/minix/Makefile # # Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. # Distributed under the terms of the Fiwix License. # .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 all: $(OBJS) clean: rm -f *.o ================================================ FILE: fs/minix/bitmaps.c ================================================ /* * fiwix/fs/minix/bitmaps.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #ifdef CONFIG_FS_MINIX #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, 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]; for(n2 = 0; n2 < 8; 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); superblock_unlock(sb); return errno; } 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); } #endif /* CONFIG_FS_MINIX */ ================================================ FILE: fs/minix/dir.c ================================================ /* * fiwix/fs/minix/dir.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_FS_MINIX struct fs_operations minix_dir_fsop = { 0, 0, minix_dir_open, minix_dir_close, minix_dir_read, NULL, /* write */ NULL, /* ioctl */ NULL, /* llseek */ minix_readdir, NULL, /* readdir64 */ 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 *f) { f->offset = 0; return 0; } int minix_dir_close(struct inode *i, struct fd *f) { return 0; } int minix_dir_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { return -EISDIR; } int minix_readdir(struct inode *i, struct fd *f, struct dirent *dirent, __size_t 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(f->offset > i->i_size) { f->offset = i->i_size; } base_dirent_len = sizeof(dirent->d_ino) + sizeof(dirent->d_off) + sizeof(dirent->d_reclen); offset = size = 0; while(f->offset < i->i_size && count > 0) { if((block = bmap(i, f->offset, FOR_READING)) < 0) { return block; } if(block) { if(!(buf = bread(i->dev, block, blksize))) { return -EIO; } doffset = f->offset; offset = f->offset & (blksize - 1); /* mod blksize */ while(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)] = 0; dirent = (struct dirent *)((char *)dirent + dirent_len); size += dirent_len; count -= dirent_len; } else { count = 0; break; } } doffset += i->sb->u.minix.dirsize; offset += i->sb->u.minix.dirsize; } brelse(buf); } f->offset &= ~(blksize - 1); f->offset += offset; } return size; } #endif /* CONFIG_FS_MINIX */ ================================================ FILE: fs/minix/file.c ================================================ /* * fiwix/fs/minix/file.c * * Copyright 2018-2021, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_FS_MINIX struct fs_operations minix_file_fsop = { 0, 0, minix_file_open, minix_file_close, file_read, minix_file_write, NULL, /* ioctl */ minix_file_llseek, NULL, /* readdir */ NULL, /* readdir64 */ 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 *f) { f->offset = 0; if(f->flags & O_TRUNC) { i->i_size = 0; minix_truncate(i, 0); } return 0; } int minix_file_close(struct inode *i, struct fd *f) { return 0; } int minix_file_write(struct inode *i, struct fd *f, const char *buffer, __size_t count) { __blk_t block; __size_t total_written; unsigned int boffset, bytes; int blksize; struct buffer *buf; inode_lock(i); blksize = i->sb->s_blocksize; total_written = 0; if(f->flags & O_APPEND) { f->offset = i->i_size; } while(total_written < count) { boffset = f->offset & (blksize - 1); /* mod blksize */ if((block = bmap(i, f->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, f->offset, buffer + total_written, bytes); bwrite(buf); total_written += bytes; f->offset += bytes; } if(f->offset > i->i_size) { i->i_size = f->offset; } i->i_ctime = CURRENT_TIME; i->i_mtime = CURRENT_TIME; i->state |= INODE_DIRTY; inode_unlock(i); return total_written; } __loff_t minix_file_llseek(struct inode *i, __loff_t offset) { return offset; } #endif /* CONFIG_FS_MINIX */ ================================================ FILE: fs/minix/inode.c ================================================ /* * 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 #ifdef CONFIG_FS_MINIX 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, int mode) { if(i->sb->u.minix.version == 1) { return v1_minix_ialloc(i, mode); } return v2_minix_ialloc(i, mode); } 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); } #endif /* CONFIG_FS_MINIX */ ================================================ FILE: fs/minix/namei.c ================================================ /* * fiwix/fs/minix/namei.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_FS_MINIX /* finds a new entry to fit 'name' in the directory 'dir' */ static struct buffer *find_first_free_dir_entry(struct inode *dir, 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); /* returns the first empty entry */ if(!(*d_res)->inode || (doffset + offset >= dir->i_size)) { /* the directory grows by directory entry size */ if(doffset + offset >= dir->i_size) { dir->i_size += dir->sb->u.minix.dirsize; } return buf; } doffset += dir->sb->u.minix.dirsize; } while(doffset < blksize); brelse(buf); offset += blksize; } else { break; } } *d_res = NULL; return NULL; } /* finds an entry in 'dir' based on the 'name' and/or on the inode 'i' */ 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) { if((*d_res)->inode) { /* returns the first matching name */ if(!strcmp((*d_res)->name, name)) { 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_first_free_dir_entry(dir, 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_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; } static int is_subdir(struct inode *dir_new, struct inode *i_old) { __ino_t inode; int errno; errno = 0; dir_new->count++; for(;;) { if(dir_new == i_old) { errno = 1; break; } inode = dir_new->inode; if(minix_lookup("..", dir_new, &dir_new)) { break; } if(dir_new->inode == inode) { break; } } iput(dir_new); 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; 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->state |= INODE_DIRTY; dir->state |= INODE_DIRTY; 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->state |= INODE_DIRTY; dir_new->state |= INODE_DIRTY; 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->state |= INODE_DIRTY; dir->state |= INODE_DIRTY; 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); /* check again to know if this filename already exists */ if((buf = find_dir_entry(dir, NULL, &d, name))) { brelse(buf); inode_unlock(dir); return -EEXIST; } if(!(i = ialloc(dir->sb, S_IFLNK))) { 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->state |= INODE_DIRTY; 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->state |= INODE_DIRTY; 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); /* check again to know if this filename already exists */ if((buf = find_dir_entry(dir, NULL, &d, name))) { brelse(buf); inode_unlock(dir); return -EEXIST; } if(!(i = ialloc(dir->sb, S_IFDIR))) { 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->state |= INODE_DIRTY; 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->state |= INODE_DIRTY; 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); /* check again to know if this filename already exists */ if((buf = find_dir_entry(dir, NULL, &d, name))) { brelse(buf); inode_unlock(dir); return -EEXIST; } if(!(i = ialloc(dir->sb, mode & S_IFMT))) { 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->state |= INODE_DIRTY; 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, 0, sizeof(struct pipefs_inode)); break; #ifdef CONFIG_NET case S_IFSOCK: i->fsop = &sockfs_fsop; i->i_mode |= S_IFSOCK; /* it's a union so we need to clear sockfs_inode */ memset_b(&i->u.sockfs, 0, sizeof(struct sockfs_inode)); break; #endif /* CONFIG_NET */ } dir->i_mtime = CURRENT_TIME; dir->i_ctime = CURRENT_TIME; dir->state |= INODE_DIRTY; bwrite(buf); iput(i); inode_unlock(dir); return 0; } int minix_create(struct inode *dir, char *name, int flags, __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(flags & O_CREAT) { /* check again to know if this filename already exists */ if((buf = find_dir_entry(dir, NULL, &d, name))) { brelse(buf); inode_unlock(dir); return -EEXIST; } } if(!(i = ialloc(dir->sb, S_IFREG))) { 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->state |= INODE_DIRTY; dir->i_mtime = CURRENT_TIME; dir->i_ctime = CURRENT_TIME; dir->state |= INODE_DIRTY; *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; if(is_subdir(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) { /* free that buffer now to not block buf_new */ brelse(buf_old); buf_old = NULL; } if(i_new) { if(S_ISDIR(i_old->i_mode)) { if(!is_dir_empty(i_new)) { if(buf_old) { brelse(buf_old); } errno = -ENOTEMPTY; goto end; } } if(!(buf_new = find_dir_entry(dir_new, i_new, &d_new, newpath))) { if(buf_old) { brelse(buf_old); } errno = -ENOENT; goto end; } } else { if(!(buf_new = add_dir_entry(dir_new, &d_new))) { if(buf_old) { brelse(buf_old); } errno = -ENOSPC; goto end; } if(S_ISDIR(i_old->i_mode)) { dir_old->i_nlink--; dir_new->i_nlink++; } } if(i_new) { i_new->i_nlink--; } else { i_new = i_old; strcpy(d_new->name, newpath); } d_new->inode = i_old->inode; dir_new->i_mtime = CURRENT_TIME; dir_new->i_ctime = CURRENT_TIME; i_new->state |= INODE_DIRTY; dir_new->state |= INODE_DIRTY; dir_old->i_mtime = CURRENT_TIME; dir_old->i_ctime = CURRENT_TIME; i_old->state |= INODE_DIRTY; dir_old->state |= INODE_DIRTY; bwrite(buf_new); if(!buf_old) { if(!(buf_old = find_dir_entry(dir_old, i_old, &d_old, oldpath))) { errno = -ENOENT; goto end; } } d_old->inode = 0; bwrite(buf_old); /* update the parent directory */ if(S_ISDIR(i_old->i_mode)) { buf_new = find_dir_entry(i_old, dir_old, &d_new, ".."); if(buf_new) { d_new->inode = dir_new->inode; bwrite(buf_new); } } end: inode_unlock(i_old); inode_unlock(dir_old); inode_unlock(dir_new); return errno; } #endif /* CONFIG_FS_MINIX */ ================================================ FILE: fs/minix/super.c ================================================ /* * fiwix/fs/minix/super.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_FS_MINIX struct fs_operations minix_fsop = { FSOP_REQUIRES_DEV, 0, NULL, /* open */ NULL, /* close */ NULL, /* read */ NULL, /* write */ NULL, /* ioctl */ NULL, /* llseek */ NULL, /* readdir */ NULL, /* readdir64 */ 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 *minixsb; 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; } minixsb = (struct minix_super_block *)buf->data; if(flags & MS_RDONLY) { /* switching from RW to RO */ sb->u.minix.sb.s_state |= MINIX_VALID_FS; minixsb->s_state |= MINIX_VALID_FS; } else { /* switching from RO to RW */ check_superblock(minixsb); memcpy_b(&sb->u.minix.sb, minixsb, sizeof(struct minix_super_block)); sb->u.minix.sb.s_state &= ~MINIX_VALID_FS; minixsb->s_state &= ~MINIX_VALID_FS; } sb->state = SUPERBLOCK_DIRTY; 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->state &= ~SUPERBLOCK_DIRTY; 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->state = SUPERBLOCK_DIRTY; superblock_unlock(sb); } int minix_init(void) { return register_filesystem("minix", &minix_fsop); } #endif /* CONFIG_FS_MINIX */ ================================================ FILE: fs/minix/symlink.c ================================================ /* * fiwix/fs/minix/symlink.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #ifdef CONFIG_FS_MINIX struct fs_operations minix_symlink_fsop = { 0, 0, NULL, /* open */ NULL, /* close */ NULL, /* read */ NULL, /* write */ NULL, /* ioctl */ NULL, /* llseek */ NULL, /* readdir */ NULL, /* readdir64 */ 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] = 0; 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) { iput(i); 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; } #endif /* CONFIG_FS_MINIX */ ================================================ FILE: fs/minix/v1_inode.c ================================================ /* * fiwix/fs/minix/v1_inode.c * * Copyright 2018-2022, 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 #ifdef CONFIG_FS_MINIX #define BLOCKS_PER_IND_BLOCK(sb) (sb->s_blocksize / sizeof(__u16)) #define BLOCKS_PER_DIND_BLOCK(sb) (BLOCKS_PER_IND_BLOCK(sb) * BLOCKS_PER_IND_BLOCK(sb)) #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 int 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 -EIO; } 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); return 0; } static int free_indblock(struct inode *i, int block, int offset) { int n, retval; struct buffer *buf; __u16 *zone; __blk_t dblock; if(!(buf = bread(i->dev, block, i->sb->s_blocksize))) { printk("%s(): error reading doubly indirect block %d.\n", __FUNCTION__, block); return -EIO; } zone = (__u16 *)buf->data; dblock = offset % BLOCKS_PER_IND_BLOCK(i->sb); for(n = offset / BLOCKS_PER_IND_BLOCK(i->sb); n < BLOCKS_PER_IND_BLOCK(i->sb); n++) { if(zone[n]) { if((retval = free_zone(i, zone[n], dblock)) < 0) { brelse(buf); return retval; } if(!dblock) { minix_bfree(i->sb, zone[n]); zone[n] = 0; } } dblock = 0; } bwrite(buf); return 0; } 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, 0, 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: #ifdef CONFIG_NET i->fsop = &sockfs_fsop; /* it's a union so we need to clear sockfs_inode */ memset_b(&i->u.sockfs, 0, sizeof(struct sockfs_inode)); #else i->fsop = NULL; #endif /* CONFIG_NET */ 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->state &= ~INODE_DIRTY; bwrite(buf); return 0; } int v1_minix_ialloc(struct inode *i, int mode) { __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__, inode); superblock_unlock(sb); return errno; } else { printk("WARNING: %s(): inode %d is already marked as used!\n", __FUNCTION__, 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; invalidate_inode_pages(i); 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->state |= INODE_DIRTY; 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->flags |= (BUFFER_DIRTY | BUFFER_VALID); } 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->flags |= (BUFFER_DIRTY | BUFFER_VALID); block = newblock; } brelse(buf); brelse(buf2); return block; } int v1_minix_truncate(struct inode *i, __off_t length) { __blk_t block; int n, retval; 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]) { if((retval = free_zone(i, i->u.minix.u.i1_zone[MINIX_IND_BLOCK], block)) < 0) { return retval; } 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 < (BLOCKS_PER_DIND_BLOCK(i->sb) + BLOCKS_PER_IND_BLOCK(i->sb) + MINIX_NDIR_BLOCKS)) { if(block) { block -= MINIX_NDIR_BLOCKS; block -= BLOCKS_PER_IND_BLOCK(i->sb); } if(i->u.minix.u.i1_zone[MINIX_DIND_BLOCK]) { if((retval = free_indblock(i, i->u.minix.u.i1_zone[MINIX_DIND_BLOCK], block)) < 0) { return retval; } 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; } } block = 0; } i->i_mtime = CURRENT_TIME; i->i_ctime = CURRENT_TIME; i->i_size = length; i->state |= INODE_DIRTY; return 0; } #endif /* CONFIG_FS_MINIX */ ================================================ FILE: fs/minix/v2_inode.c ================================================ /* * fiwix/fs/minix/v2_inode.c * * Copyright 2018-2022, 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 #ifdef CONFIG_FS_MINIX #define BLOCKS_PER_IND_BLOCK(sb) (sb->s_blocksize / sizeof(__u32)) #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 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 int 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 -EIO; } 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); return 0; } static int free_indblock(struct inode *i, int block, int offset) { int n, retval; struct buffer *buf; __u32 *zone; __blk_t dblock; if(!(buf = bread(i->dev, block, i->sb->s_blocksize))) { printk("%s(): error reading doubly indirect block %d.\n", __FUNCTION__, block); return -EIO; } zone = (__u32 *)buf->data; dblock = offset % BLOCKS_PER_IND_BLOCK(i->sb); for(n = offset / BLOCKS_PER_IND_BLOCK(i->sb); n < BLOCKS_PER_IND_BLOCK(i->sb); n++) { if(zone[n]) { if((retval = free_zone(i, zone[n], dblock)) < 0) { brelse(buf); return retval; } if(!dblock) { minix_bfree(i->sb, zone[n]); zone[n] = 0; } } dblock = 0; } bwrite(buf); return 0; } 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, 0, 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: #ifdef CONFIG_NET i->fsop = &sockfs_fsop; /* it's a union so we need to clear sockfs_inode */ memset_b(&i->u.sockfs, 0, sizeof(struct sockfs_inode)); #else i->fsop = NULL; #endif /* CONFIG_NET */ 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->state &= ~INODE_DIRTY; bwrite(buf); return 0; } int v2_minix_ialloc(struct inode *i, int mode) { __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__, inode); superblock_unlock(sb); return errno; } else { printk("WARNING: %s(): inode %d is already marked as used!\n", __FUNCTION__, 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; invalidate_inode_pages(i); 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->state |= INODE_DIRTY; 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->flags |= (BUFFER_DIRTY | BUFFER_VALID); } 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; tblock -= BLOCKS_PER_DIND_BLOCK(i->sb) * block; 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->flags |= (BUFFER_DIRTY | BUFFER_VALID); 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->flags |= (BUFFER_DIRTY | BUFFER_VALID); 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) { __blk_t block, indblock, *dindblock; struct buffer *buf; int n, retval; 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]) { if((retval = free_zone(i, i->u.minix.u.i2_zone[MINIX_IND_BLOCK], block)) < 0) { return retval; } 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 < (BLOCKS_PER_DIND_BLOCK(i->sb) + BLOCKS_PER_IND_BLOCK(i->sb) + MINIX_NDIR_BLOCKS)) { if(block) { block -= MINIX_NDIR_BLOCKS; block -= BLOCKS_PER_IND_BLOCK(i->sb); } if(i->u.minix.u.i2_zone[MINIX_DIND_BLOCK]) { if((retval = free_indblock(i, i->u.minix.u.i2_zone[MINIX_DIND_BLOCK], block)) < 0) { return retval; } 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; } } block = 0; } if(!block || block < (BLOCKS_PER_TIND_BLOCK(i->sb) + BLOCKS_PER_DIND_BLOCK(i->sb) + BLOCKS_PER_IND_BLOCK(i->sb) + EXT2_NDIR_BLOCKS)) { if(block) { block -= MINIX_NDIR_BLOCKS; block -= BLOCKS_PER_IND_BLOCK(i->sb); block -= BLOCKS_PER_DIND_BLOCK(i->sb); } if(i->u.minix.u.i2_zone[MINIX_TIND_BLOCK]) { if(!(buf = bread(i->dev, i->u.minix.u.i2_zone[MINIX_TIND_BLOCK], i->sb->s_blocksize))) { printk("%s(): error reading the triply indirect block (%d).\n", __FUNCTION__, i->u.minix.u.i2_zone[MINIX_TIND_BLOCK]); return -EIO; } dindblock = (__blk_t *)buf->data; indblock = 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(dindblock[n]) { if((retval = free_indblock(i, dindblock[n], indblock)) < 0) { brelse(buf); return retval; } if(!indblock) { minix_bfree(i->sb, dindblock[n]); dindblock[n] = 0; } } indblock = 0; } bwrite(buf); if(!block) { minix_bfree(i->sb, i->u.minix.u.i2_zone[MINIX_TIND_BLOCK]); i->u.minix.u.i2_zone[MINIX_TIND_BLOCK] = 0; } } } i->i_mtime = CURRENT_TIME; i->i_ctime = CURRENT_TIME; i->i_size = length; i->state |= INODE_DIRTY; return 0; } #endif /* CONFIG_FS_MINIX */ ================================================ FILE: fs/namei.c ================================================ /* * fiwix/fs/namei.c * * Copyright 2018-2022, 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 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 == '\0') { return 0; } /* extracts the next component of the path */ if(!(name = (char *)kmalloc(NAME_MAX + 1))) { return -ENOMEM; } ptr_name = name; while(*path != '\0' && *path != '/') { if(ptr_name > (name + NAME_MAX)) { break; } *ptr_name++ = *path++; } *ptr_name = 0; /* * 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] == '\0') { if(dir == dir->sb->root) { sb = dir->sb; iput(dir); dir = sb->dir; dir->count++; } } if((errno = check_permission(TO_EXEC, dir))) { break; } dir->count++; if((errno = dir->fsop->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->followlink) { if((errno = i->fsop->followlink(dir, i, &i))) { iput(dir); return errno; } } } } else { if(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 == '\0') { *d_res = dir; dir->count++; } else { /* that's a non-existent directory */ *d_res = NULL; errno = -ENOENT; } 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 == '\0') { 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); } ================================================ FILE: fs/pipefs/Makefile ================================================ # fiwix/fs/pipefs/Makefile # # Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. # Distributed under the terms of the Fiwix License. # .c.o: $(CC) $(CFLAGS) -c -o $@ $< OBJS = super.o fifo.o pipe.o all: $(OBJS) clean: rm -f *.o ================================================ FILE: fs/pipefs/fifo.c ================================================ /* * 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 *f) { /* first open */ if(i->count == 1) { if(!(i->u.pipefs.i_data = (void *)kmalloc(PAGE_SIZE))) { return -ENOMEM; } i->u.pipefs.i_readoff = 0; i->u.pipefs.i_writeoff = 0; } if((f->flags & O_ACCMODE) == O_RDONLY) { i->u.pipefs.i_readers++; wakeup(&pipefs_write); if(!(f->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((f->flags & O_ACCMODE) == O_WRONLY) { if((f->flags & O_NONBLOCK) && !i->u.pipefs.i_readers) { return -ENXIO; } i->u.pipefs.i_writers++; wakeup(&pipefs_read); if(!(f->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((f->flags & O_ACCMODE) == O_RDWR) { i->u.pipefs.i_readers++; i->u.pipefs.i_writers++; wakeup(&pipefs_write); wakeup(&pipefs_read); } return 0; } ================================================ FILE: fs/pipefs/pipe.c ================================================ /* * fiwix/fs/pipefs/pipe.c * * Copyright 2018-2022, 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 = { 0, 0 }; int pipefs_close(struct inode *i, struct fd *f) { if((f->flags & O_ACCMODE) == O_RDONLY) { if(!--i->u.pipefs.i_readers) { wakeup(&do_select); wakeup(&pipefs_write); } } if((f->flags & O_ACCMODE) == O_WRONLY) { if(!--i->u.pipefs.i_writers) { wakeup(&do_select); wakeup(&pipefs_read); } } if((f->flags & O_ACCMODE) == O_RDWR) { if(!--i->u.pipefs.i_readers) { wakeup(&do_select); wakeup(&pipefs_write); } if(!--i->u.pipefs.i_writers) { wakeup(&do_select); wakeup(&pipefs_read); } } return 0; } int pipefs_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { __size_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(&do_select); wakeup(&pipefs_write); break; } else { if(i->u.pipefs.i_writers) { if(f->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 *f, const char *buffer, __size_t count) { __size_t bytes_written; __size_t n; char *data; int limit; bytes_written = 0; data = i->u.pipefs.i_data; while(bytes_written < count) { /* if there are no readers 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 less than * or equal to 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(&do_select); wakeup(&pipefs_read); continue; } wakeup(&do_select); wakeup(&pipefs_read); if(!(f->flags & O_NONBLOCK)) { if(sleep(&pipefs_write, PROC_INTERRUPTIBLE)) { return -EINTR; } } else { return -EAGAIN; } } return bytes_written; } int pipefs_ioctl(struct inode *i, struct fd *f, int cmd, unsigned 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; } __loff_t pipefs_llseek(struct inode *i, __loff_t offset) { return -ESPIPE; } int pipefs_select(struct inode *i, struct fd *f, int flag) { switch(flag) { case SEL_R: /* * if !i->i_size && !i->u.pipefs.i_writers * should also return 1? */ 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 * should also return 1? */ if(i->i_size < PIPE_BUF || !i->u.pipefs.i_readers) { return 1; } break; } return 0; } ================================================ FILE: fs/pipefs/super.c ================================================ /* * 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_llseek, NULL, /* readdir */ NULL, /* readdir64 */ 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_ialloc(struct inode *i, int mode) { struct superblock *sb = i->sb; superblock_lock(sb); i_counter++; superblock_unlock(sb); i->i_mode = mode; 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(PAGE_SIZE))) { 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_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_init(void) { return register_filesystem("pipefs", &pipefs_fsop); } ================================================ FILE: fs/procfs/Makefile ================================================ # fiwix/fs/procfs/Makefile # # Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. # Distributed under the terms of the Fiwix License. # .c.o: $(CC) $(CFLAGS) -c -o $@ $< OBJS = super.o inode.o namei.o dir.o file.o symlink.o tree.o data.o kmsg.o all: $(OBJS) clean: rm -f *.o ================================================ FILE: fs/procfs/data.c ================================================ /* * fiwix/fs/procfs/data.c * * Copyright 2018-2023, 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 #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_buddyinfo(char *buffer, __pid_t pid) { int n, level, size; size = sprintk(buffer, "Sizes:"); for(level = 32, n = 0; n < BUDDY_MAX_LEVEL; n++, level <<= 1) { size += sprintk(buffer + size, "\t%d", level); } size += sprintk(buffer + size, "\n"); size += sprintk(buffer + size, "------------------------------------------------------------\n"); for(n = 0; n < BUDDY_MAX_LEVEL; n++) { size += sprintk(buffer + size, "\t%d", kstat.buddy_low_count[n]); } size += sprintk(buffer + size, "\n\n"); size += sprintk(buffer + size, "Memory requested (used): %d KB (%d KB)\n", kstat.buddy_low_mem_requested / 1024, (kstat.buddy_low_num_pages * PAGE_SIZE / 1024)); return size; } int data_proc_cmdline(char *buffer, __pid_t pid) { return sprintk(buffer, "%s\n", kernel_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; size = sprintk(buffer, "Character devices:\n"); for(n = 0; n < NR_CHRDEV; n++) { d = chr_device_table[n]; while(d) { size += sprintk(buffer + size, "%3d %s\n", d->major, d->name); d = d->next; } } size += sprintk(buffer + size, "\nBlock devices:\n"); for(n = 0; n < NR_BLKDEV; n++) { d = blk_device_table[n]; while(d) { size += sprintk(buffer + size, "%3d %s\n", d->major, d->name); d = d->next; } } 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) { struct interrupt *irq; int n, size; size = 0; for(n = 0; n < NR_IRQS; n++) { if((irq = irq_table[n])) { size += sprintk(buffer + size, "%3d: %9u %s", n, irq->ticks, irq->name); while((irq = irq->next)) { size += sprintk(buffer + size, ",%s", irq->name); } size += sprintk(buffer + size, "\n"); } } 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) { nprocs++; if(p->state == PROC_RUNNING) { nrun++; } p = p->next; } 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) { struct flock_file *ff; int n, size; n = size = 0; ff = flock_file_table; while(ff) { 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); } n++; ff = ff->next; } 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_size * 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); 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); size += sprintk(buffer + size, "Dirty: %9d kB\n", kstat.dirty_buffers); return size; } int data_proc_mounts(char *buffer, __pid_t pid) { int size; char *flag; struct mount *mp; size = 0; mp = mount_table; while(mp) { if(mp->fs->fsop->flags != FSOP_KERN_MOUNT) { flag = mp->sb.flags & MS_RDONLY ? "ro" : "rw"; size += sprintk(buffer + size, "%s %s %s %s 0 0\n", mp->devname, mp->dirname, mp->fs->name, flag); } mp = mp->next; } 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 ata_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_ATA_DRVS; drv++) { drive = &ide->drive[drv]; if(!drive->nr_sects) { continue; } if(drive->flags & DRIVE_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_pci(char *buffer, __pid_t pid) { #ifdef CONFIG_PCI int size, n; struct pci_device *pd; size = sprintk(buffer, "PCI devices found:\n"); pd = pci_device_table; while(pd) { size += sprintk(buffer + size, " Bus %2d, device %3d, function %2d:\n", pd->bus, pd->dev, pd->func); size += sprintk(buffer + size, " Class %04x: PCI device %04x:%04x (rev %d).\n", pd->class, pd->vendor_id, pd->device_id, pd->rev); if(pd->irq) { size += sprintk(buffer + size, " IRQ %d.\n", pd->irq); } if(pd->hdr_type == PCI_HEADER_TYPE_NORMAL) { for(n = 0; n < 6; n++) { if(pd->bar[n]) { if(pd->flags[n] & PCI_BASE_ADDR_SPACE_IO) { size += sprintk(buffer + size, " I/O at 0x%x [0x%x].\n", pd->bar[n], pd->bar[n] + (pd->size[n] - 1)); } else { if(pd->flags[n] & PCI_F_ADDR_SPACE_PREFET) { size += sprintk(buffer + size, " Prefetchable "); } else { size += sprintk(buffer + size, " Non-prefetchable "); } if(pd->flags[n] & PCI_F_ADDR_MEM_32) { size += sprintk(buffer + size, "32"); } else if(pd->flags[n] & PCI_F_ADDR_MEM_64) { size += sprintk(buffer + size, "64"); } else { size += sprintk(buffer + size, "??"); } size += sprintk(buffer + size, " bit memory at 0x%x [0x%x].\n", pd->bar[n], pd->bar[n] + (pd->size[n] - 1)); } } } } pd = pd->next; } return size; #else return 0; #endif /* CONFIG_PCI */ } 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; struct interrupt *irq; idle = CURRENT_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++) { irq = irq_table[n]; if(irq) { size += sprintk(buffer + size, " %u", irq->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 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, CURRENT_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_unix(char *buffer, __pid_t pid) { #ifdef CONFIG_NET struct unix_info *u; struct sockaddr_un *sun; struct socket *s; struct fd *fd; int size; size = sprintk(buffer, "Num RefCount Protocol Flags Type St Inode Path\n"); u = unix_socket_head; while(u) { sun = u->sun; s = u->socket; fd = s->fd; size += sprintk(buffer + size, "%08x: %08d %08d %08x %04d %02d % 5d %s\n", &s->u.unix_info, u->count, /* FIXME s->fd->count, */ 0, s->flags, s->type, s->state, fd->inode->inode, sun ? sun->sun_path : ""); u = u->next; } return size; #else return 0; #endif /* CONFIG_NET */ } int data_proc_pci_devices(char *buffer, __pid_t pid) { #ifdef CONFIG_PCI int size, n; struct pci_device *pd; size = 0; pd = pci_device_table; while(pd) { size += sprintk(buffer + size, "%02x%02x\t%04x%04x\t%x", pd->bus, PCI_DEVFN(pd->dev, pd->func), pd->vendor_id, pd->device_id, pd->irq); for(n = 0; n < 6;n ++) { size += sprintk(buffer + size, "\t%08x", pd->obar[n]); } size += sprintk(buffer + size, "\t%08x", 0); for(n = 0; n < 6;n ++) { size += sprintk(buffer + size, "\t%08x", pd->size[n]); } size += sprintk(buffer + size, "\t%08x", 0); if(pd->name) { size += sprintk(buffer + size, " %s", pd->name); } size += sprintk(buffer + size, "\n"); pd = pd->next; } return size; #else return 0; #endif /* CONFIG_PCI */ } int data_proc_buffernr(char *buffer, __pid_t pid) { return sprintk(buffer, "%d\n", kstat.nr_buffers); } 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", kstat.max_inodes); } int data_proc_inodenr(char *buffer, __pid_t pid) { return sprintk(buffer, "%d\n", kstat.nr_inodes); } 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); } int data_proc_dirty_background_ratio(char *buffer, __pid_t pid) { return sprintk(buffer, "%d\n", BUFFER_DIRTY_RATIO); } /* * PID directory related functions * ------------------------------- */ int data_proc_pid_fd(char *buffer, __pid_t pid, __ino_t inode) { int size, ufd; struct proc *p; struct inode *i; size = 0; ufd = inode & 0xFFF; if((p = get_proc_by_pid(pid))) { i = fd_table[p->fd[ufd]].inode; size = sprintk(buffer, "[%02d%02d]:%d", MAJOR(i->dev), MINOR(i->dev), i->inode); } return size; } int data_proc_pid_cmdline(char *buffer, __pid_t pid) { int n, size; char *arg, **argv; unsigned int addr,offset; struct proc *p; size = 0; if((p = get_proc_by_pid(pid))) { offset = (int)p->argv & ~PAGE_MASK; addr = get_mapped_addr(p, (int)p->argv) & PAGE_MASK; addr = P2V(addr); argv = (char **)(addr + offset); for(n = 0; n < p->argc && (int)argv[n]; n++) { offset = (int)argv[n] & ~PAGE_MASK; addr = get_mapped_addr(p, (int)argv[n]) & PAGE_MASK; addr = P2V(addr); arg = (char *)(addr + offset); if(size + strlen(arg) < (PAGE_SIZE - 1)) { size += sprintk(buffer + size, "%s", arg); buffer[size++] = 0; } else { break; } } } 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, **envp; unsigned int addr, offset; struct proc *p; size = 0; if((p = get_proc_by_pid(pid))) { offset = (int)p->envp & ~PAGE_MASK; addr = get_mapped_addr(p, (int)p->envp) & PAGE_MASK; addr = P2V(addr); envp = (char **)(addr + offset); for(n = 0; n < p->envc && (int)envp[n]; n++) { offset = (int)envp[n] & ~PAGE_MASK; addr = get_mapped_addr(p, (int)envp[n]) & PAGE_MASK; addr = P2V(addr); env = (char *)(addr + offset); if(size + strlen(env) < (PAGE_SIZE - 1)) { size += sprintk(buffer + size, "%s", env); buffer[size++] = 0; } else { break; } } } 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 */ /* * This assumes that the first entry in the vma_table * contains the program's inode. */ if(!p->vma_table || !p->vma_table->inode) { return -ENOENT; } i = p->vma_table->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) { 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))) { vma = p->vma_table; while(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; case P_SHM: section = "shm"; 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; vma = vma->next; } } return size; } int data_proc_pid_mountinfo(char *buffer, __pid_t pid) { int n, size; char *flag, *devname; struct mount *mp; size = n = 0; mp = mount_table; while(mp) { if(mp->fs->fsop->flags != FSOP_KERN_MOUNT) { flag = mp->sb.flags & MS_RDONLY ? "ro" : "rw"; devname = mp->devname; if(!strcmp(devname, "/dev/root")) { devname = kparms.rootdevname; } size += sprintk(buffer + size, "%d 0 %d:%d %s %s %s - %s %s %s\n", n, MAJOR(mp->dev), MINOR(mp->dev), "/", mp->dirname, flag, mp->fs->name, devname, flag); } n++; mp = mp->next; } 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) { unsigned int esp, eip; int signum, mask; __sigset_t sigignored, sigcaught; struct proc *p; struct sigcontext *sc; struct vma *vma; int size, text, data, stack, mmap; size = text = data = stack = mmap = 0; if((p = get_proc_by_pid(pid))) { vma = p->vma_table; while(vma) { switch(vma->s_type) { case P_TEXT: text += vma->end - vma->start; break; case P_HEAP: data += vma->end - vma->start; break; case P_STACK: stack += vma->end - vma->start; break; case P_MMAP: case P_SHM: mmap += vma->end - vma->start; break; } vma = vma->next; } 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->pid, 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, text + data + stack + mmap, p->rss, 0x7FFFFFFF, /* rlim */ p->entry_address & PAGE_MASK, p->end_code, PAGE_OFFSET - 1, /* startstack */ esp, /* kstkesp */ eip, /* kstkeip */ p->sigpending, p->sigblocked, sigignored, sigcaught, p->sleep_address ); } return size; } int data_proc_pid_statm(char *buffer, __pid_t pid) { int size; struct proc *p; struct vma *vma; int text, data, stack, mmap; size = text = data = stack = mmap = 0; if((p = get_proc_by_pid(pid))) { vma = p->vma_table; while(vma) { switch(vma->s_type) { case P_TEXT: text += vma->end - vma->start; break; case P_HEAP: data += vma->end - vma->start; break; case P_STACK: stack += vma->end - vma->start; break; case P_MMAP: case P_SHM: mmap += vma->end - vma->start; break; } vma = vma->next; } size = sprintk(buffer, "%d", (text + data + stack + mmap) / PAGE_SIZE); size += sprintk(buffer + size, " %d", p->rss); size += sprintk(buffer + size, " 0"); /* shared mappings */ size += sprintk(buffer + size, " %d", text / PAGE_SIZE); size += sprintk(buffer + size, " 0"); size += sprintk(buffer + size, " %d", (data + stack) / PAGE_SIZE); size += sprintk(buffer + size, " 0\n"); } return size; } int data_proc_pid_status(char *buffer, __pid_t pid) { int size; int signum, mask; __sigset_t sigignored, sigcaught; struct proc *p; struct vma *vma; int text, data, stack, mmap; size = text = data = stack = mmap = 0; if((p = get_proc_by_pid(pid))) { vma = p->vma_table; while(vma) { switch(vma->s_type) { case P_TEXT: text += vma->end - vma->start; break; case P_HEAP: data += vma->end - vma->start; break; case P_STACK: stack += vma->end - vma->start; break; case P_MMAP: case P_SHM: mmap += vma->end - vma->start; break; } vma = vma->next; } 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->pid); 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%8d kB\n", (text + data + stack + mmap) / 1024); size += sprintk(buffer + size, "VmLck:\t%8d kB\n", 0); size += sprintk(buffer + size, "VmRSS:\t%8d kB\n", p->rss << 2); size += sprintk(buffer + size, "VmData:\t%8d kB\n", data / 1024); size += sprintk(buffer + size, "VmStk:\t%8d kB\n", stack / 1024); size += sprintk(buffer + size, "VmExe:\t%8d kB\n", text / 1024); size += sprintk(buffer + size, "VmLib:\t%8d kB\n", 0); 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; } ================================================ FILE: fs/procfs/dir.c ================================================ /* * fiwix/fs/procfs/dir.c * * Copyright 2018-2022, 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, /* llseek */ procfs_readdir, NULL, /* readdir64 */ 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 void free_all_fdstr(struct procfs_dir_entry *d) { while(d) { if((d->inode & 0xF0000000) == PROC_FD_INO) { kfree((unsigned int)d->name); } d++; } } static int proc_listdir(char *buffer, int count) { 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; FOR_EACH_PROCESS(p) { 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.nlink = 1; d.lev = -1; d.name_len = 1; n = p->pid; while(n / 10) { n /= 10; d.name_len++; } d.name = p->pidstr; d.data_fn = NULL; if(size + sizeof(struct procfs_dir_entry) > (count - 1)) { printk("WARNING: kmalloc() is limited to 4096 bytes.\n"); break; } size += sizeof(struct procfs_dir_entry); memcpy_b((void *)pd, (void *)&d, sizeof(struct procfs_dir_entry)); pd++; p = p->next; } return size; } static int proc_listfd(struct inode *i, char *buffer, int count) { int n; struct proc *p; struct procfs_dir_entry *pd; struct procfs_dir_entry d; char *fdstr; 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_FD_INO + (p->pid << 12) + n; d.mode = S_IFLNK | S_IRWXU; d.nlink = 1; d.lev = -1; if(!(fdstr = (char *)kmalloc(4 + 1))) { break; } d.name_len = sprintk(fdstr, "%d", n); d.name = fdstr; d.data_fn = NULL; if(size + sizeof(struct procfs_dir_entry) > (count - 1)) { printk("WARNING: kmalloc() is limited to 4096 bytes.\n"); break; } size += sizeof(struct procfs_dir_entry); memcpy_b((void *)pd, (void *)&d, sizeof(struct procfs_dir_entry)); pd++; } } memset_b((void *)pd + size, 0, sizeof(struct procfs_dir_entry)); return size; } static int dir_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { __size_t total_read; unsigned int bytes; int len, lev; char *buf; if(!(buf = (void *)kmalloc(PAGE_SIZE))) { return -ENOMEM; } memset_b(buf, 0, PAGE_SIZE); /* create the list of directories for each process */ len = 0; if(i->inode == PROC_ROOT_INO) { len = proc_listdir(buf, count); } /* create the list of fds used for each process */ if((i->inode & 0xF0000FFF) == PROC_PID_FD) { len = proc_listfd(i, buf, count); } /* add the rest of static files in the main directory */ lev = i->u.procfs.i_lev; /* calculate the size of the level without the last entry (NULL) */ bytes = sizeof(procfs_array[lev]) - sizeof(struct procfs_dir_entry); if((len + bytes) > (count - 1)) { printk("WARNING: %s(): len (%d) > count (%d).\n", __FUNCTION__, len, count); free_all_fdstr((struct procfs_dir_entry *)buf); kfree((unsigned int)buf); return 0; } memcpy_b(buf + len, (char *)&procfs_array[lev], bytes); len += bytes; total_read = f->offset = len; memcpy_b(buffer, buf, len); kfree((unsigned int)buf); return total_read; } int procfs_dir_open(struct inode *i, struct fd *f) { f->offset = 0; return 0; } int procfs_dir_close(struct inode *i, struct fd *f) { return 0; } int procfs_dir_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { return -EISDIR; } int procfs_readdir(struct inode *i, struct fd *f, struct dirent *dirent, __size_t count) { unsigned int offset, boffset, dirent_offset, doffset; int dirent_len; __size_t total_read; struct procfs_dir_entry *d; int base_dirent_len; char *buffer; if(!(buffer = (void *)kmalloc(PAGE_SIZE))) { return -ENOMEM; } base_dirent_len = sizeof(dirent->d_ino) + sizeof(dirent->d_off) + sizeof(dirent->d_reclen); offset = f->offset; boffset = dirent_offset = doffset = 0; boffset = offset & (PAGE_SIZE - 1); /* mod PAGE_SIZE */ total_read = dir_read(i, f, buffer, PAGE_SIZE); if((count = MIN(total_read, count)) == 0) { kfree((unsigned int)buffer); return dirent_offset; } while(boffset < f->offset) { 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 + dirent_len) <= count) { boffset += sizeof(struct procfs_dir_entry); offset += sizeof(struct procfs_dir_entry); doffset += dirent_len; 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] = 0; dirent = (struct dirent *)((char *)dirent + dirent_len); dirent_offset += dirent_len; } else { break; } if((d->inode & 0xF0000000) == PROC_FD_INO) { kfree((unsigned int)d->name); } } f->offset = boffset; kfree((unsigned int)buffer); return dirent_offset; } ================================================ FILE: fs/procfs/file.c ================================================ /* * fiwix/fs/procfs/file.c * * Copyright 2018-2021, 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_llseek, NULL, /* readdir */ NULL, /* readdir64 */ 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 *f) { if(f->flags & (O_WRONLY | O_RDWR | O_TRUNC | O_APPEND)) { return -EINVAL; } f->offset = 0; return 0; } int procfs_file_close(struct inode *i, struct fd *f) { return 0; } int procfs_file_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { __size_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(PAGE_SIZE))) { return -ENOMEM; } size = d->data_fn(buf, (i->inode >> 12) & 0xFFFF); blksize = i->sb->s_blocksize; if(f->offset > size) { f->offset = size; } total_read = 0; for(;;) { count = (f->offset + count > size) ? size - f->offset : count; if(!count) { break; } boffset = f->offset & (blksize - 1); /* mod blksize */ bytes = blksize - boffset; bytes = MIN(bytes, count); memcpy_b(buffer + total_read, buf + boffset, bytes); total_read += bytes; count -= bytes; f->offset += bytes; } kfree((unsigned int)buf); return total_read; } __loff_t procfs_file_llseek(struct inode *i, __loff_t offset) { return offset; } ================================================ FILE: fs/procfs/inode.c ================================================ /* * fiwix/fs/procfs/inode.c * * Copyright 2018-2021, 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((i->inode & 0xF0000000) == PROC_FD_INO) { /* dynamic FD symlink */ mode = S_IFLNK | S_IRWXU; nlink = 1; lev = PROC_FD_LEV; } else { if(!(d = get_procfs_by_inode(i))) { return -ENOENT; } mode = d->mode; nlink = d->nlink; lev = d->lev; } } i->i_mode = mode; i->i_uid = 0; i->i_size = 0; if(S_ISLNK(i->i_mode)) { i->i_size = 64; } 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->state = INODE_LOCKED; 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); } switch(i->inode) { case PROC_KMSG_INO: i->fsop = &procfs_kmsg_fsop; break; } 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; } ================================================ FILE: fs/procfs/kmsg.c ================================================ /* * fiwix/fs/procfs/kmsg.c * * Copyright 2025, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include static int kmsg_open(struct inode *i, struct fd *f) { return sys_syslog(SYSLOG_OPEN, NULL, 0); } static int kmsg_close(struct inode *i, struct fd *f) { return sys_syslog(SYSLOG_CLOSE, NULL, 0); } static int kmsg_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { return sys_syslog(SYSLOG_READ, buffer, count); } static int kmsg_select(struct inode *i, struct fd *f, int flag) { switch(flag) { case SEL_R: if(log_new_chars) { return 1; } break; } return 0; } struct fs_operations procfs_kmsg_fsop = { 0, 0, kmsg_open, kmsg_close, kmsg_read, NULL, /* write */ NULL, /* ioctl */ NULL, /* llseek */ NULL, /* readdir */ NULL, /* readdir64 */ NULL, /* mmap */ kmsg_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 */ }; ================================================ FILE: fs/procfs/namei.c ================================================ /* * fiwix/fs/procfs/namei.c * * Copyright 2018-2021, 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, ufd; __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; } lev = bmap(dir, 0, FOR_READING); /* /fd directory */ if(lev == 2) { if(name[0] == '[') { iput(dir); return -ENOENT; } pid = (dir->inode >> 12) & 0xFFFF; if(!(p = get_proc_by_pid(pid))) { iput(dir); return -ENOENT; } if(name[0] == '.' && name[1] == '\0') { *i_res = dir; return 0; } if(name[0] == '.' && name[1] == '.') { inode = PROC_PID_INO + (p->pid << 12); if(!(*i_res = iget(dir->sb, inode))) { iput(dir); return -EACCES; } iput(dir); return 0; } ufd = atoi(name); if(p->fd[ufd]) { inode = (PROC_FD_INO + (pid << 12)) + ufd; if(!(*i_res = iget(dir->sb, inode))) { iput(dir); return -EACCES; } iput(dir); return 0; } } 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))) { iput(dir); return -EACCES; } iput(dir); return 0; } pdirent++; } FOR_EACH_PROCESS(p) { 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))) { iput(dir); return -EACCES; } iput(dir); return 0; } p = p->next; } iput(dir); return -ENOENT; } ================================================ FILE: fs/procfs/super.c ================================================ /* * 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, /* llseek */ NULL, /* readdir */ NULL, /* readdir64 */ 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); } ================================================ FILE: fs/procfs/symlink.c ================================================ /* * fiwix/fs/procfs/symlink.c * * Copyright 2018-2022, 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_symlink_fsop = { 0, 0, NULL, /* open */ NULL, /* close */ NULL, /* read */ NULL, /* write */ NULL, /* ioctl */ NULL, /* llseek */ NULL, /* readdir */ NULL, /* readdir64 */ 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) { int size_read; struct procfs_dir_entry *d; char *buf; if((i->inode & 0xF0000000) == PROC_FD_INO) { if(!(buf = (char *)kmalloc(PAGE_SIZE))) { return -ENOMEM; } if((size_read = data_proc_pid_fd(buf, (i->inode >> 12) & 0xFFFF, i->inode))) { if(size_read > count) { size_read = count; } memcpy_b(buffer, buf, size_read); } kfree((unsigned int)buf); return size_read; } if(!(d = get_procfs_by_inode(i))) { return -EINVAL; } if(!d->data_fn) { return -EINVAL; } if(!(buf = (char *)kmalloc(PAGE_SIZE))) { return -ENOMEM; } if((size_read = d->data_fn(buf, (i->inode >> 12) & 0xFFFF)) > 0) { if(size_read > count) { size_read = count; } memcpy_b(buffer, buf, size_read); } kfree((unsigned int)buf); 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; int ufd; 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))) { iput(i); return -ENOENT; } } if((i->inode & 0xF0000000) == PROC_FD_INO) { ufd = i->inode & 0xFFF; *i_res = fd_table[p->fd[ufd]].inode; fd_table[p->fd[ufd]].inode->count++; iput(i); return 0; } 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: /* * This assumes that the first entry in the vma_table * contains the program's inode. */ if(!p->vma_table || !p->vma_table->inode) { return -ENOENT; } *i_res = p->vma_table->inode; p->vma_table->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; } ================================================ FILE: fs/procfs/tree.c ================================================ /* * fiwix/fs/procfs/tree.c * * Copyright 2018-2023, 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 DIRFD S_IFDIR | S_IRUSR | S_IXUSR /* dr-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] = { { /* [lev 0] / */ { 1, DIR, 2, 0, 1, ".", NULL }, { 2, DIR, 2, 0, 2, "..", NULL }, { 3, DIR, 3, 3, 3, "bus", NULL }, { 4, DIR, 2, 4, 3, "net", NULL }, { 5, DIR, 4, 5, 3, "sys", NULL }, { 6, REG, 1, 0, 9, "buddyinfo", data_proc_buddyinfo }, { 7, REG, 1, 0, 7, "cmdline", data_proc_cmdline }, { 8, REG, 1, 0, 7, "cpuinfo", data_proc_cpuinfo }, { 9, REG, 1, 0, 7, "devices", data_proc_devices }, { 10, REG, 1, 0, 3, "dma", data_proc_dma }, { 11, REG, 1, 0, 11, "filesystems",data_proc_filesystems }, { 12, REG, 1, 0, 10, "interrupts", data_proc_interrupts }, { PROC_KMSG_INO, REGUSR, 1, 0, 4, "kmsg", NULL }, { 14, REG, 1, 0, 7, "loadavg", data_proc_loadavg }, { 15, REG, 1, 0, 5, "locks", data_proc_locks }, { 16, REG, 1, 0, 7, "meminfo", data_proc_meminfo }, { 17, REG, 1, 0, 6, "mounts", data_proc_mounts }, { 18, REG, 1, 0, 10, "partitions", data_proc_partitions }, { 19, REG, 1, 0, 3, "pci", data_proc_pci }, { 20, REG, 1, 0, 3, "rtc", data_proc_rtc }, { 21, LNK, 1, 0, 4, "self", data_proc_self }, { 22, REG, 1, 0, 4, "stat", data_proc_stat }, { 23, REG, 1, 0, 6, "uptime", data_proc_uptime }, { 24, REG, 1, 0, 7, "version", data_proc_fullversion }, { 0, 0, 0, 0, 0, NULL, NULL } }, { /* [lev 1] /PID/ */ { 1000, DIR, 2, 1, 1, ".", NULL }, { 1, DIR, 2, 0, 2, "..", NULL }, { PROC_PID_FD, DIRFD, 2, 2, 2, "fd", NULL }, { 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_STATM, REG, 1, 1, 5, "statm", data_proc_pid_statm }, { PROC_PID_STATUS, REG, 1, 1, 6, "status", data_proc_pid_status }, { 0, 0, 0, 0, 0, NULL, NULL } }, { /* [lev 2] /PID/fd/ */ { 2000, DIRFD, 2, 2, 1, ".", NULL }, { 1000, DIR, 2, 2, 2, "..", NULL }, { 0, 0, 0, 0, 0, NULL, NULL } }, { /* [lev 3] /bus/ */ { 3, DIR, 3, 3, 1, ".", NULL }, { 1, DIR, 2, 0, 2, "..", NULL }, { 3001, DIR, 2, 6, 3, "pci", NULL }, { 0, 0, 0, 0, 0, NULL, NULL } }, { /* [lev 4] /net/ */ { 4, DIR, 2, 4, 1, ".", NULL }, { 1, DIR, 2, 0, 2, "..", NULL }, { 4001, REG, 1, 4, 4, "unix", data_proc_unix }, { 0, 0, 0, 0, 0, NULL, NULL } }, { /* [lev 5] /sys/ */ { 5, DIR, 2, 5, 1, ".", NULL }, { 1, DIR, 2, 0, 2, "..", NULL }, { 5001, DIR, 2, 7, 6, "kernel", NULL }, { 5002, DIR, 2, 8, 2, "vm", NULL }, { 0, 0, 0, 0, 0, NULL, NULL } }, { /* [3001] /bus/pci/ */ { 3001, DIR, 2, 4, 1, ".", NULL }, { 3, DIR, 3, 3, 2, "..", NULL }, { 6001, REG, 1, 6, 7, "devices", data_proc_pci_devices }, { 0, 0, 0, 0, 0, NULL, NULL } }, { /* [5001] /sys/kernel/ */ { 5001, DIR, 2, 7, 1, ".", NULL }, { 5, DIR, 2, 3, 2, "..", NULL }, { 7001, REG, 1, 7, 9, "buffer-nr", data_proc_buffernr }, { 7002, REG, 1, 7, 10, "domainname", data_proc_domainname }, { 7003, REG, 1, 7, 8, "file-max", data_proc_filemax }, { 7004, REG, 1, 7, 7, "file-nr", data_proc_filenr }, { 7005, REG, 1, 7, 8, "hostname", data_proc_hostname }, { 7006, REG, 1, 7, 9, "inode-max", data_proc_inodemax }, { 7007, REG, 1, 7, 8, "inode-nr", data_proc_inodenr }, { 7008, REG, 1, 7, 9, "osrelease", data_proc_osrelease }, { 7009, REG, 1, 7, 6, "ostype", data_proc_ostype }, { 7010, REG, 1, 7, 7, "version", data_proc_version }, { 0, 0, 0, 0, 0, NULL, NULL } }, { /* [5002] /sys/vm/ */ { 5002, DIR, 2, 8, 1, ".", NULL }, { 5, DIR, 2, 3, 2, "..", NULL }, { 8001, REG, 1, 8, 22, "dirty_background_ratio", data_proc_dirty_background_ratio }, { 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][0].inode; 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; } ================================================ FILE: fs/script.c ================================================ /* * fiwix/fs/script.c * * Copyright 2019-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include int script_load(char *interpreter, char *args, char *data) { char *p; int n, noargs; /* has shebang? */ if(data[0] != '#' || data[1] != '!') { return -ENOEXEC; } /* discard possible blanks before the interpreter name */ for(n = 2; n < NAME_MAX; n++) { if(data[n] != ' ' && data[n] != '\t') { break; } } /* get the interpreter name */ p = interpreter; noargs = 0; while(n < NAME_MAX) { if(data[n] == '\n' || data[n] == '\0') { noargs = 1; break; } if(data[n] == ' ' || data[n] == '\t') { break; } *p = data[n]; n++; p++; } if(!interpreter) { return -ENOEXEC; } /* get the interpreter arguments */ if(!noargs) { p = args; /* discard possible blanks before the arguments */ while(n < NAME_MAX) { if(data[n] != ' ' && data[n] != '\t') { break; } n++; } while(n < NAME_MAX) { if(data[n] == '\n' || data[n] == '\0') { break; } *p = data[n]; n++; p++; } } return 0; } ================================================ FILE: fs/sockfs/Makefile ================================================ # fiwix/fs/sockfs/Makefile # # Copyright 2023, Jordi Sanfeliu. All rights reserved. # Distributed under the terms of the Fiwix License. # .c.o: $(CC) $(CFLAGS) -c -o $@ $< OBJS = super.o socket.o all: $(OBJS) clean: rm -f *.o ================================================ FILE: fs/sockfs/socket.c ================================================ /* * fiwix/fs/sockfs/socket.c * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #ifdef CONFIG_NET int sockfs_open(struct inode *i, struct fd *f) { return -ENXIO; } int sockfs_close(struct inode *i, struct fd *f) { struct socket *s; s = &i->u.sockfs.sock; sock_free(s); return 0; } int sockfs_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { struct socket *s; s = &i->u.sockfs.sock; return s->ops->read(s, f, buffer, count); } int sockfs_write(struct inode *i, struct fd *f, const char *buffer, __size_t count) { struct socket *s; s = &i->u.sockfs.sock; return s->ops->write(s, f, buffer, count); } int sockfs_ioctl(struct inode *i, struct fd *f, int cmd, unsigned int arg) { struct socket *s; s = &i->u.sockfs.sock; return s->ops->ioctl(s, f, cmd, arg); } __loff_t sockfs_llseek(struct inode *i, __loff_t offset) { return -ESPIPE; } int sockfs_select(struct inode *i, struct fd *f, int flag) { struct socket *s; s = &i->u.sockfs.sock; return s->ops->select(s, flag); } #endif /* CONFIG_NET */ ================================================ FILE: fs/sockfs/super.c ================================================ /* * fiwix/fs/sockfs/super.c * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include #ifdef CONFIG_NET static unsigned int i_counter; struct fs_operations sockfs_fsop = { FSOP_KERN_MOUNT, SOCK_DEV, sockfs_open, sockfs_close, sockfs_read, sockfs_write, sockfs_ioctl, sockfs_llseek, NULL, /* readdir */ NULL, /* readdir64 */ NULL, /* mmap */ sockfs_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 */ sockfs_ialloc, sockfs_ifree, NULL, /* statfs */ sockfs_read_superblock, NULL, /* remount_fs */ NULL, /* write_superblock */ NULL /* release_superblock */ }; int sockfs_ialloc(struct inode *i, int mode) { struct superblock *sb = i->sb; superblock_lock(sb); i_counter++; superblock_unlock(sb); i->i_mode = mode; i->dev = i->rdev = sb->dev; i->fsop = &sockfs_fsop; i->inode = i_counter; i->count = 1; /* if(!(i->u.sockfs.sock = (struct socket *)kmalloc(sizeof(struct socket)))) { return -ENOMEM; } */ memset_b(&i->u.sockfs.sock, 0, sizeof(struct socket)); return 0; } void sockfs_ifree(struct inode *i) { /* if(i->u.sockfs.sock) { kfree((unsigned int)i->u.sockfs.sock); } */ } int sockfs_read_superblock(__dev_t dev, struct superblock *sb) { superblock_lock(sb); sb->dev = dev; sb->fsop = &sockfs_fsop; sb->s_blocksize = BLKSIZE_1K; i_counter = 0; superblock_unlock(sb); return 0; } int sockfs_init(void) { return register_filesystem("sockfs", &sockfs_fsop); } #endif /* CONFIG_NET */ ================================================ FILE: fs/super.c ================================================ /* * fiwix/fs/super.c * * Copyright 2018-2022, 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 struct mount *mount_table = NULL; static struct resource sync_resource = { 0, 0 }; void superblock_lock(struct superblock *sb) { unsigned int flags; for(;;) { SAVE_FLAGS(flags); CLI(); if(sb->state & SUPERBLOCK_LOCKED) { RESTORE_FLAGS(flags); sleep(sb, PROC_UNINTERRUPTIBLE); } else { break; } } sb->state |= SUPERBLOCK_LOCKED; RESTORE_FLAGS(flags); } void superblock_unlock(struct superblock *sb) { unsigned int flags; SAVE_FLAGS(flags); CLI(); sb->state &= ~SUPERBLOCK_LOCKED; wakeup(sb); RESTORE_FLAGS(flags); } struct mount *add_mount_point(__dev_t dev, const char *devname, const char *dirname) { unsigned int flags; struct mount *mp; if(kstat.mount_points + 1 > NR_MOUNT_POINTS) { printk("WARNING: tried to exceed NR_MOUNT_POINTS (%d).\n", NR_MOUNT_POINTS); return NULL; } /* check if this device is already mounted */ if(get_superblock(dev)) { return NULL; } if(!(mp = (struct mount *)kmalloc(sizeof(struct mount)))) { return NULL; } memset_b(mp, 0, sizeof(struct mount)); if(!(mp->devname = (char *)kmalloc(strlen(devname) + 1))) { kfree((unsigned int)mp); return NULL; } if(!(mp->dirname = (char *)kmalloc(strlen(dirname) + 1))) { kfree((unsigned int)mp->devname); kfree((unsigned int)mp); return NULL; } SAVE_FLAGS(flags); CLI(); if(!mount_table) { mount_table = mp; } else { mp->prev = mount_table->prev; mount_table->prev->next = mp; } mount_table->prev = mp; RESTORE_FLAGS(flags); mp->dev = dev; strcpy(mp->devname, devname); strcpy(mp->dirname, dirname); kstat.mount_points++; return mp; } void del_mount_point(struct mount *mp) { unsigned int flags; struct mount *tmp; tmp = mp; if(!mp->next && !mp->prev) { printk("WARNING: %s(): trying to umount an unexistent mount point (%x, '%s', '%s').\n", __FUNCTION__, mp->dev, mp->devname, mp->dirname); return; } SAVE_FLAGS(flags); CLI(); if(mp->next) { mp->next->prev = mp->prev; } if(mp->prev) { if(mp != mount_table) { mp->prev->next = mp->next; } } if(!mp->next) { mount_table->prev = mp->prev; } if(mp == mount_table) { mount_table = mp->next; } RESTORE_FLAGS(flags); kfree((unsigned int)tmp->devname); kfree((unsigned int)tmp->dirname); kfree((unsigned int)tmp); kstat.mount_points--; } struct mount *get_mount_point(struct inode *i) { struct mount *mp; mp = mount_table; while(mp) { if(S_ISDIR(i->i_mode)) { if(mp->sb.root == i) { return mp; } } if(S_ISBLK(i->i_mode)) { if(mp->dev == i->rdev) { return mp; } } mp = mp->next; } return NULL; } struct superblock *get_superblock(__dev_t dev) { struct mount *mp; mp = mount_table; while(mp) { if(mp->dev == dev) { return &mp->sb; } mp = mp->next; } return NULL; } void sync_superblocks(__dev_t dev) { struct superblock *sb; struct mount *mp; mp = mount_table; lock_resource(&sync_resource); while(mp) { if(!dev || mp->dev == dev) { sb = &mp->sb; if((sb->state & SUPERBLOCK_DIRTY) && !(sb->flags & MS_RDONLY)) { if(sb->fsop->write_superblock(sb)) { printk("WARNING: %s(): I/O error on device %d,%d while syncing superblock.\n", __FUNCTION__, MAJOR(sb->dev), MINOR(sb->dev)); } } } mp = mp->next; } unlock_resource(&sync_resource); } /* pseudo-filesystems are only mountable by the kernel */ int kern_mount(__dev_t dev, struct filesystems *fs) { struct mount *mp; if(!(mp = add_mount_point(dev, "none", "none"))) { return -EBUSY; } if(fs->fsop->read_superblock(dev, &mp->sb)) { del_mount_point(mp); return -EINVAL; } mp->sb.dir = NULL; mp->fs = fs; fs->mp = mp; return 0; } int mount_root(void) { struct filesystems *fs; struct mount *mp; /* * FIXME: before trying to mount the filesystem, we should first * check if '_rootdev' is a device successfully registered. */ if(!kparms.rootdev) { PANIC("root device not defined.\n"); } if(!(fs = get_filesystem(kparms.rootfstype))) { printk("WARNING: %s(): '%s' is not a registered filesystem. Defaulting to 'ext2'.\n", __FUNCTION__, kparms.rootfstype); if(!(fs = get_filesystem("ext2"))) { PANIC("ext2 filesystem is not registered!\n"); } } if(!(mp = add_mount_point(kparms.rootdev, "/dev/root", "/"))) { PANIC("unable to get a free mount point.\n"); } if(kparms.ro) { mp->sb.flags = MS_RDONLY; } if(fs->fsop->read_superblock(kparms.rootdev, &mp->sb)) { PANIC("unable to mount root filesystem on %s.\n", kparms.rootdevname); } mp->sb.root->mount_point = mp->sb.root; mp->sb.root->count++; mp->sb.dir = mp->sb.root; mp->sb.dir->count++; mp->fs = fs; current->root = mp->sb.root; current->root->count++; current->pwd = mp->sb.root; current->pwd->count++; iput(mp->sb.root); printk("mounted root device (%s filesystem)", fs->name); if(mp->sb.flags & MS_RDONLY) { printk(" in readonly mode"); } printk(".\n"); return 0; } ================================================ FILE: include/fiwix/asm.h ================================================ /* * fiwix/include/fiwix/asm.h * * Copyright 2018-2022, 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 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 except20(void); extern void except21(void); extern void except22(void); extern void except23(void); extern void except24(void); extern void except25(void); extern void except26(void); extern void except27(void); extern void except28(void); extern void except29(void); extern void except30(void); extern void except31(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 get_cpu_vendor_id(void); int signature_flags(void); int brand_str(void); int tlbinfo(void); unsigned char inport_b(unsigned int); unsigned short int inport_w(unsigned int); unsigned int inport_l(unsigned int); void inport_sw(unsigned int, void *, unsigned int); void inport_sl(unsigned int, void *, unsigned int); void outport_b(unsigned int, unsigned char); void outport_w(unsigned int, unsigned short int); void outport_l(unsigned int, unsigned int); void outport_sw(unsigned int, void *, unsigned int); void outport_sl(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 GET_GS(gs) __asm__ __volatile__ ("movl %%gs, %0" : "=r" (gs)); #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" \ ); #ifdef __TINYC__ /* * tcc loads "r" (register) arguments automatically into registers using this order: * eax, ecx, edx, ebx * Therefore, we rearrange the arguments so they go into the correct registers. */ #define USER_SYSCALL(num, arg1, arg2, arg3) \ __asm__ __volatile__( \ "int $0x80\n\t" \ : /* no output */ \ : "r"((unsigned int)num), "r"((unsigned int)arg2), "r"((unsigned int)arg3), "r"((unsigned int)arg1) \ ); #else #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) \ ); #endif /* 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 */ ================================================ FILE: include/fiwix/ata.h ================================================ /* * fiwix/include/fiwix/ata.h * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_ATA_H #define _FIWIX_ATA_H #include #include #include #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 GET_DRIVE_NUM(_dev_) ((MINOR(_dev_) & (1 < #define ATA_HD_SECTSIZE 512 /* sector size (in bytes) */ int ata_hd_open(struct inode *, struct fd *); int ata_hd_close(struct inode *, struct fd *); int ata_hd_read(__dev_t, __blk_t, char *, int); int ata_hd_write(__dev_t, __blk_t, char *, int); int ata_hd_ioctl(struct inode *, struct fd *, int, unsigned int); __loff_t ata_hd_llseek(struct inode *, __loff_t); int ata_hd_init(struct ide *, struct ata_drv *); #endif /* _FIWIX_ATA_HD_H */ ================================================ FILE: include/fiwix/ata_pci.h ================================================ /* * fiwix/include/fiwix/ata_pci.h * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_ATA_PCI_H #define _FIWIX_ATA_PCI_H #ifdef CONFIG_PCI #include void ata_setup_dma(struct ide *, struct ata_drv *, char *, int, int); void ata_start_dma(struct ide *, struct ata_drv *); void ata_stop_dma(struct ide *, struct ata_drv *); int ata_pci(struct ide *); #endif /* CONFIG_PCI */ #endif /* _FIWIX_ATA_PCI_H */ ================================================ FILE: include/fiwix/atapi.h ================================================ /* * fiwix/include/fiwix/atapi.h * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_ATAPI_H #define _FIWIX_ATAPI_H #include 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 }; int send_packet_command(unsigned char *, struct ide *, struct ata_drv *, int); int atapi_cmd_testunit(struct ide *, struct ata_drv *); int atapi_cmd_reqsense(struct ide *, struct ata_drv *); int atapi_cmd_startstop(int, struct ide *, struct ata_drv *); int atapi_cmd_mediumrm(int, struct ide *, struct ata_drv *); int atapi_cmd_get_capacity(struct ide *, struct ata_drv *); int atapi_cmd_read10(struct ide *, struct ata_drv *); #endif /* _FIWIX_ATAPI_H */ ================================================ FILE: include/fiwix/atapi_cd.h ================================================ /* * fiwix/include/fiwix/atapi_cd.h * * Copyright 2018, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_ATAPI_CD_H #define _FIWIX_ATAPI_CD_H #include #define ATAPI_CD_SECTSIZE BLKSIZE_2K /* sector size (in bytes) */ int atapi_cd_open(struct inode *, struct fd *); int atapi_cd_close(struct inode *, struct fd *); int atapi_cd_read(__dev_t, __blk_t, char *, int); int atapi_cd_ioctl(struct inode *, struct fd *, int, unsigned int); __loff_t atapi_cd_llseek(struct inode *, __loff_t); int atapi_cd_init(struct ide *, struct ata_drv *); #endif /* _FIWIX_ATAPI_CD_H */ ================================================ FILE: include/fiwix/bga.h ================================================ /* * fiwix/include/fiwix/bga.h * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifdef CONFIG_BGA #ifndef _FIWIX_BGA_H #define _FIWIX_BGA_H #define VBE_DISPI_IOPORT_INDEX 0x01CE #define VBE_DISPI_IOPORT_DATA 0x01CF #define VBE_DISPI_INDEX_ID 0x0 #define VBE_DISPI_INDEX_XRES 0x1 #define VBE_DISPI_INDEX_YRES 0x2 #define VBE_DISPI_INDEX_BPP 0x3 #define VBE_DISPI_INDEX_ENABLE 0x4 #define VBE_DISPI_INDEX_BANK 0x5 #define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 #define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 #define VBE_DISPI_INDEX_X_OFFSET 0x8 #define VBE_DISPI_INDEX_Y_OFFSET 0x9 #define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xA #define VBE_DISPI_ID4 0xB0C4 #define VBE_DISPI_DISABLED 0x00 #define VBE_DISPI_ENABLED 0x01 #define VBE_DISPI_LFB_ENABLED 0x40 #define VBE_DISPI_NOCLEARMEM 0x80 void bga_init(void); #endif /* _FIWIX_BGA_H */ #endif /* CONFIG_BGA */ ================================================ FILE: include/fiwix/bios.h ================================================ /* * fiwix/include/fiwix/bios.h * * Copyright 2018-2021, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_BIOS_H #define _FIWIX_BIOS_H #include #define NR_BIOS_MM_ENT 50 /* entries in BIOS memory map */ struct bios_mem_map { unsigned int from; unsigned int from_hi; unsigned int to; unsigned int to_hi; int type; }; extern struct bios_mem_map bios_mem_map[NR_BIOS_MM_ENT]; extern struct bios_mem_map kernel_mem_map[NR_BIOS_MM_ENT]; extern char bios_data[256]; int is_addr_in_bios_map(unsigned int); void bios_map_reserve(unsigned int, unsigned int); void bios_map_init(struct multiboot_mmap_entry *, unsigned int); #endif /* _FIWIX_BIOS_H */ ================================================ FILE: include/fiwix/blk_queue.h ================================================ /* * fiwix/include/fiwix/blk_queue.h * * Copyright 2024, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_BLKQUEUE_H #define _FIWIX_BLKQUEUE_H #include #include #include #define BR_PROCESSING 1 #define BR_COMPLETED 2 #define BRF_NOBLOCK 1 struct blk_request { int status; int errno; __dev_t dev; __blk_t block; int size; int flags; struct buffer *buffer; struct device *device; int (*fn)(__dev_t, __blk_t, char *, int); int left; struct blk_request *next; struct blk_request *next_group; struct blk_request *head_group; }; void add_blk_request(struct blk_request *); int do_blk_request(struct device *, void *, struct buffer *); void run_blk_request(struct device *); #endif /* _FIWIX_BLKQUEUE_H */ ================================================ FILE: include/fiwix/buffer.h ================================================ /* * fiwix/include/fiwix/buffer.h * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_BUFFER_H #define _FIWIX_BUFFER_H #include #include #include /* buffer flags */ #define BUFFER_VALID 0x01 #define BUFFER_LOCKED 0x02 #define BUFFER_DIRTY 0x04 #define BLK_READ 1 #define BLK_WRITE 2 struct buffer { __dev_t dev; /* device number */ __blk_t block; /* block number */ int size; /* block size (in bytes) */ int flags; char *data; /* block contents */ unsigned int mark; /* a mark to identify a buffer */ struct buffer *prev; struct buffer *next; struct buffer *prev_hash; struct buffer *next_hash; struct buffer *prev_free; struct buffer *next_free; struct buffer *prev_dirty; struct buffer *next_dirty; struct buffer *first_sibling; struct buffer *next_sibling; struct buffer *next_retained; }; extern struct buffer *buffer_table; extern struct buffer **buffer_hash_table; /* value to be determined during system startup */ extern unsigned int buffer_hash_table_size; /* size in bytes */ int gbread(struct device *, struct blk_request *); 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); int kbdflushd(void); void buffer_init(void); #endif /* _FIWIX_BUFFER_H */ ================================================ FILE: include/fiwix/charq.h ================================================ /* * fiwix/include/fiwix/charq.h * * Copyright 2024, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_CHARQ_H #define _FIWIX_CHARQ_H #define CBSIZE 1024 /* number of characters in cblock */ #define NR_CB_QUEUE 1 /* number of cblocks per queue */ #define LAST_CHAR(q) ((q)->tail ? (q)->tail->data[(q)->tail->end_off - 1] : '\0') 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; }; int charq_putchar(struct clist *, unsigned char); int charq_unputchar(struct clist *); unsigned char charq_getchar(struct clist *); void charq_flush(struct clist *); int charq_room(struct clist *q); void charq_init(void); #endif /* _FIWIX_CHARQ_H */ ================================================ FILE: include/fiwix/cmos.h ================================================ /* * 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 */ ================================================ FILE: include/fiwix/config.h ================================================ /* * fiwix/include/fiwix/config.h * * Copyright 2018-2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_CONFIG_H #define _FIWIX_CONFIG_H /* kernel tuning options */ #define NR_PROCS 64 /* max. number of processes */ #define NR_CALLOUTS NR_PROCS /* max. active callouts */ #define NR_MOUNT_POINTS 8 /* max. number of mounted filesystems */ #define NR_OPENS 1024 /* max. number of opened files */ #define NR_FLOCKS (NR_PROCS * 5) /* max. number of flocks */ #define FREE_PAGES_RATIO 5 /* % minimum of free memory pages */ #define PAGE_HASH_PER_10K 10 /* % of % of hash buckets relative to the number of physical pages */ #define MAX_PAGES_HASH 16 /* max. number of pages in hash table */ #define BUFFER_PERCENTAGE 100 /* % of memory for buffer cache */ #define BUFFER_HASH_PERCENTAGE 10 /* % of hash buckets relative to the size of the buffer table */ #define NR_BUF_RECLAIM 250 /* buffers reclaimed in a single shot */ #define BUFFER_DIRTY_RATIO 5 /* % of dirty buffers in buffer cache */ #define INODE_PERCENTAGE 5 /* % of memory for the inode table and hash table */ #define INODE_HASH_PERCENTAGE 10 /* % of hash buckets relative to the size of the inode table */ #define MAX_PID_VALUE 32767 /* max. value for PID */ #define SCREENS_LOG 6 /* max. number of screens in console's scroll back */ #define MAX_SPU_NOTICES 10 /* max. number of messages on spurious interrupts */ #define RAMDISK_DRIVES 1 /* num. of all-purpose ramdisk drives */ #define NR_SYSCONSOLES 1 /* max. number of system consoles */ /* toggle configuration options */ #define CONFIG_PCI #define CONFIG_PCI_NAMES #undef CONFIG_SYSCALL_6TH_ARG #define CONFIG_SYSVIPC #define CONFIG_BGA #undef CONFIG_KEXEC #define CONFIG_OFFSET64 #undef CONFIG_VM_SPLIT22 #undef CONFIG_FS_MINIX #undef CONFIG_MMAP2 #define CONFIG_NET #define CONFIG_PRINTK64 #define CONFIG_PSAUX #define CONFIG_UNIX98_PTYS /* configuration options to help debugging */ #define CONFIG_VERBOSE_SEGFAULTS #undef CONFIG_QEMU_DEBUGCON #ifdef CUSTOM_CONFIG_H #include #endif #endif /* _FIWIX_CONFIG_H */ ================================================ FILE: include/fiwix/console.h ================================================ /* * fiwix/include/fiwix/console.h * * Copyright 2018-2021, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_CONSOLE_H #define _FIWIX_CONSOLE_H #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 */ /* Graphic Rendition Combination Modes */ #define SGR_DEFAULT 0 /* back to the default rendition */ #define SGR_BOLD 1 /* set bold */ #define SGR_BLINK 5 /* set slowly blinking */ #define SGR_REVERSE 7 /* set reverse video */ #define SGR_BOLD_OFF 21 /* unset bold */ #define SGR_BLINK_OFF 25 /* unset blinking */ #define SGR_REVERSE_OFF 27 /* unset reverse video */ #define SGR_BLACK_FG 30 /* set black foreground */ #define SGR_RED_FG 31 /* set red foreground */ #define SGR_GREEN_FG 32 /* set green foreground */ #define SGR_BROWN_FG 33 /* set brown foreground */ #define SGR_BLUE_FG 34 /* set blue foreground */ #define SGR_MAGENTA_FG 35 /* set magenta foreground */ #define SGR_CYAN_FG 36 /* set cyan foreground */ #define SGR_WHITE_FG 37 /* set white foreground */ #define SGR_DEFAULT_FG_U_ON 38 /* set def. fg color (underline on) */ #define SGR_DEFAULT_FG_U_OFF 39 /* set def. fg color (underline off) */ #define SGR_BLACK_BG 40 /* set black background */ #define SGR_RED_BG 41 /* set red background */ #define SGR_GREEN_BG 42 /* set green background */ #define SGR_BROWN_BG 43 /* set brown background */ #define SGR_BLUE_BG 44 /* set blue background */ #define SGR_MAGENTA_BG 45 /* set magenta background */ #define SGR_CYAN_BG 46 /* set cyan background */ #define SGR_WHITE_BG 47 /* set white background */ #define SGR_DEFAULT_BG 49 /* set default background color */ #define NPARMS 16 #define BLANK_INTERVAL (0 * HZ) /* 0 seconds (no screen blank) */ #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 video.columns #define SCREEN_LINES video.lines #define SCREEN_SIZE (video.columns * video.lines) #define VC_BUF_LINES (video.lines * SCREENS_LOG) #define VC_BUF_SIZE (video.columns * VC_BUF_LINES) #define SCROLL_UP 1 #define SCROLL_DOWN 2 #define BS 127 /* backspace */ #define VPF_VGA 0x01 /* VGA text mode */ #define VPF_VESAFB 0x02 /* x86 framebuffer */ #define VPF_CURSOR_ON 0x04 /* draw cursor */ #define ON 1 #define OFF 0 #define COND 2 /* console flags */ #define CONSOLE_HAS_FOCUS 0x0001 #define CONSOLE_BLANKED 0x0002 extern short int current_cons; /* current console (/dev/tty1 ... /dev/tty12) */ extern short int *vc_screen[NR_VCONSOLES + 1]; /* * This is the scrollback history buffer which is used only in the active * vconsole. Everytime a vconsole is switched, the screen contents of the * new vconsole is copied back to this buffer. Only the visible screen is * copied, so switching vconsoles means losing the scrollback history. */ extern short int *vcbuf; struct vconsole { int x; /* current column */ int y; /* current line */ int top, lines, columns; short int check_x; unsigned char led_status; unsigned char scrlock, numlock, capslock; unsigned char esc, sbracket, semicolon, question; int flags; int parmv1, parmv2; int nparms, parms[NPARMS]; unsigned short int color_attr; unsigned char bold, underline, blink, reverse; unsigned char *vidmem; /* write here only when console has focus */ short int *screen; /* the back-buffer of the screen */ int saved_x, cursor_x; int saved_y, cursor_y; struct vt_mode vt_mode; unsigned char vc_mode; int switchto_tty; struct tty *tty; }; struct video_parms { struct pci_device *pci_dev; int flags; unsigned int *address; int port; int memsize; unsigned char signature[32]; int columns; int lines; int buf_y; int buf_top; int fb_version; int fb_width; int fb_height; int fb_char_width; int fb_char_height; int fb_bpp; int fb_pixelwidth; int fb_pitch; int fb_linesize; int fb_size; /* size of screen based on resolution */ int fb_vsize; /* size of screen based on columns x lines */ /* video driver operations */ void (*put_char)(struct vconsole *, unsigned char); void (*insert_char)(struct vconsole *); void (*delete_char)(struct vconsole *); void (*update_curpos)(struct vconsole *); void (*show_cursor)(struct vconsole *, int); void (*get_curpos)(struct vconsole *); void (*write_screen)(struct vconsole *, int, int, short int); void (*blank_screen)(struct vconsole *); void (*scroll_screen)(struct vconsole *, int, int); void (*restore_screen)(struct vconsole *); void (*screen_on)(struct vconsole *); void (*buf_scroll)(struct vconsole *, int); void (*cursor_blink)(unsigned int); }; extern struct video_parms video; void vconsole_reset(struct tty *); void vconsole_write(struct tty *); void vconsole_select(int); void vconsole_select_final(int); void vconsole_restore(struct vconsole *); void unblank_screen(struct vconsole *); 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 console_init(void); #endif /* _FIWIX_CONSOLE_H */ ================================================ FILE: include/fiwix/cpu.h ================================================ /* * fiwix/include/fiwix/cpu.h * * Copyright 2018-2022, 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 int hz; char *cache; char has_cpuid; char has_fpu; int flags; }; extern struct cpu cpu_table; struct cpu_type { int cpu; char *name[20]; }; int get_cpu_flags(char *); void cpu_init(void); #endif /* _FIWIX_CPU_H */ ================================================ FILE: include/fiwix/ctype.h ================================================ /* * 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 ISPRINT(ch) ((_ctype + 1)[ch] & (_P | _U | _L | _N | _S)) #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 */ ================================================ FILE: include/fiwix/devices.h ================================================ /* * fiwix/include/fiwix/devices.h * * Copyright 2018-2022, 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 255 /* maximum number of block devices */ #define NR_CHRDEV 255 /* maximum number of char devices */ #define BLK_DEV 1 /* block device */ #define CHR_DEV 2 /* character device */ #define MAX_MINORS 256 /* maximum number of minors per device */ #define MINOR_BITS (MAX_MINORS / (sizeof(unsigned int) * 8)) #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; unsigned char major; unsigned int minors[MINOR_BITS];/* bitmap of MAX_MINORS bits */ unsigned int *blksize; /* default minor blocksizes, in KB */ void *device_data; /* mostly used for minor sizes, in KB */ struct fs_operations *fsop; void *requests_queue; void *xfer_data; struct device *next; }; extern struct device *chr_device_table[NR_CHRDEV]; extern struct device *blk_device_table[NR_BLKDEV]; int register_device(int, struct device *); void unregister_device(int, struct device *); struct device *get_device(int, __dev_t); 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 *, struct fd *, int, unsigned int); __loff_t blk_dev_llseek(struct inode *, __loff_t); void dev_init(void); #endif /* _FIWIX_DEVICES_H */ ================================================ FILE: include/fiwix/dirent.h ================================================ /* * 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) */ }; struct dirent64 { __ino64_t d_ino; /* inode number */ __loff_t d_off; /* offset to next dirent */ unsigned short d_reclen; /* length of this dirent */ unsigned char d_type; /* file type */ char d_name[]; /* file name (null-terminated) */ }; #define DT_UNKNOWN 0 #define DT_FIFO 1 #define DT_CHR 2 #define DT_DIR 4 #define DT_BLK 6 #define DT_REG 8 #define DT_LNK 10 #define DT_SOCK 12 #define DT_WHT 14 #endif /* _FIWIX_DIRENT_H */ ================================================ FILE: include/fiwix/dma.h ================================================ /* * 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 extern 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 */ ================================================ FILE: include/fiwix/errno.h ================================================ #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 */ ================================================ FILE: include/fiwix/fb.h ================================================ /* * fiwix/include/fiwix/fb.h * * Copyright 2021, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_FB_H #define _FIWIX_FB_H #include #define FB_MAJOR 29 /* major number */ #define FB_MINOR 0 /* minor number */ int fb_open(struct inode *, struct fd *); int fb_close(struct inode *, struct fd *); int fb_read(struct inode *, struct fd *, char *, __size_t); int fb_write(struct inode *, struct fd *, const char *, __size_t); int fb_mmap(struct inode *, struct vma *); int fb_ioctl(struct inode *, struct fd *, int, unsigned int); __loff_t fb_llseek(struct inode *, __loff_t); void fb_init(void); #endif /* _FIWIX_FB_H */ ================================================ FILE: include/fiwix/fbcon.h ================================================ /* * fiwix/include/fiwix/fbcon.h * * Copyright 2021, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_FBCON_H #define _FIWIX_FBCON_H void fbcon_put_char(struct vconsole *, unsigned char); void fbcon_put_char(struct vconsole *, unsigned char); void fbcon_insert_char(struct vconsole *); void fbcon_delete_char(struct vconsole *); void fbcon_update_curpos(struct vconsole *); void fbcon_show_cursor(struct vconsole *, int); void fbcon_get_curpos(struct vconsole *); void fbcon_write_screen(struct vconsole *, int, int, short int); void fbcon_blank_screen(struct vconsole *); void fbcon_scroll_screen(struct vconsole *, int, int); void fbcon_restore_screen(struct vconsole *); void fbcon_screen_on(struct vconsole *); void fbcon_screen_off(unsigned int); void fbcon_buf_scroll(struct vconsole *, int); void fbcon_cursor_blink(unsigned int); void fbcon_init(void); #endif /* _FIWIX_FBCON_H */ ================================================ FILE: include/fiwix/fcntl.h ================================================ /* * 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_DIRECTORY 0200000 /* only open if directory */ #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 */ #define F_GETLK64 12 #define F_SETLK64 13 #define F_SETLKW64 14 #define F_DUPFD_CLOEXEC 1030 /* duplicate file descriptor with close-on-exec*/ /* 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 */ ================================================ FILE: include/fiwix/fd.h ================================================ /* * fiwix/include/fiwix/fd.h * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_FD_H #define _FIWIX_FD_H #include #include #define CHECK_UFD(ufd) \ { \ if((ufd) > (OPEN_MAX - 1) || current->fd[(ufd)] == 0) { \ return -EBADF; \ } \ } \ extern unsigned int fd_table_size; /* size in bytes */ extern struct fd *fd_table; struct fd { struct inode *inode; /* file inode */ unsigned short int flags; /* flags */ unsigned short int count; /* number of opened instances */ #ifdef CONFIG_OFFSET64 __loff_t offset; /* r/w pointer position */ #else __off_t offset; /* r/w pointer position */ #endif /* CONFIG_OFFSET64 */ void *private_data; /* needed for tty driver */ }; #endif /* _FIWIX_FS_H */ ================================================ FILE: include/fiwix/filesystems.h ================================================ /* * fiwix/include/fiwix/filesystems.h * * Copyright 2018-2022, 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 6 /* supported filesystems */ /* special device numbers for nodev filesystems */ enum { FS_NODEV = 0xFFF0, DEVPTS_DEV, PIPE_DEV, PROC_DEV, SOCK_DEV, }; struct filesystems { const char *name; /* filesystem name */ struct fs_operations *fsop; /* filesystem operations */ struct mount *mp; /* mount-table entry (only for nodev) */ }; extern struct filesystems filesystems_table[NR_FILESYSTEMS]; struct mount { __dev_t dev; /* device number */ char *devname; /* device name */ char *dirname; /* mount point directory name */ struct superblock sb; /* superblock */ struct filesystems *fs; /* pointer to filesystem structure */ struct mount *prev; struct mount *next; }; 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); /* 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); __loff_t minix_file_llseek(struct inode *, __loff_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_readdir(struct inode *, struct fd *, struct dirent *, __size_t); 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 *, int, __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 *, int); 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_write(struct inode *, struct fd *, const char *, __size_t); __loff_t ext2_file_llseek(struct inode *, __loff_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_readdir(struct inode *, struct fd *, struct dirent *, __size_t); int ext2_readdir64(struct inode *, struct fd *, struct dirent64 *, __size_t); 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_rmdir(struct inode *, struct inode *); int ext2_link(struct inode *, struct inode *, char *); int ext2_unlink(struct inode *, struct inode *, char *); int ext2_symlink(struct inode *, char *, char *); int ext2_mkdir(struct inode *, char *, __mode_t); int ext2_mknod(struct inode *, char *, __mode_t, __dev_t); int ext2_truncate(struct inode *, __off_t); int ext2_create(struct inode *, char *, int, __mode_t, struct inode **); int ext2_rename(struct inode *, struct inode *, struct inode *, struct inode *, char *, char *); int ext2_read_inode(struct inode *); int ext2_write_inode(struct inode *); int ext2_ialloc(struct inode *, int); void ext2_ifree(struct inode *); void ext2_statfs(struct superblock *, struct statfs *); int ext2_read_superblock(__dev_t, struct superblock *); int ext2_remount_fs(struct superblock *, int); int ext2_write_superblock(struct superblock *); void ext2_release_superblock(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 *, struct fd *, int, unsigned int); __loff_t pipefs_llseek(struct inode *, __loff_t); int pipefs_select(struct inode *, struct fd *, int); int pipefs_ialloc(struct inode *, int); 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 *); __loff_t iso9660_file_llseek(struct inode *, __loff_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_readdir(struct inode *, struct fd *, struct dirent *, __size_t); 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); __loff_t procfs_file_llseek(struct inode *, __loff_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_readdir(struct inode *, struct fd *, struct dirent *, __size_t); 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); #ifdef CONFIG_NET /* sockfs prototypes */ int sockfs_open(struct inode *, struct fd *); int sockfs_close(struct inode *, struct fd *); int sockfs_read(struct inode *, struct fd *, char *, __size_t); int sockfs_write(struct inode *, struct fd *, const char *, __size_t); int sockfs_ioctl(struct inode *, struct fd *, int, unsigned int); __loff_t sockfs_llseek(struct inode *, __loff_t); int sockfs_select(struct inode *, struct fd *, int); int sockfs_ialloc(struct inode *, int); void sockfs_ifree(struct inode *); int sockfs_read_superblock(__dev_t, struct superblock *); int sockfs_init(void); #endif /* CONFIG_NET */ #ifdef CONFIG_UNIX98_PTYS /* devpts prototypes */ int devpts_dir_open(struct inode *, struct fd *); int devpts_dir_close(struct inode *, struct fd *); int devpts_dir_read(struct inode *, struct fd *, char *, __size_t); int devpts_readdir(struct inode *, struct fd *, struct dirent *, __size_t); int devpts_lookup(const char *, struct inode *, struct inode **); int devpts_read_inode(struct inode *); void devpts_statfs(struct superblock *, struct statfs *); int devpts_ialloc(struct inode *, int); void devpts_ifree(struct inode *); int devpts_read_superblock(__dev_t, struct superblock *); int devpts_init(void); #endif /* CONFIG_UNIX98_PTYS */ #endif /* _FIWIX_FILESYSTEMS_H */ ================================================ FILE: include/fiwix/floppy.h ================================================ /* * 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 #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(int, struct sigcontext *); 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 *, struct fd *, int, unsigned int); __loff_t fdc_llseek(struct inode *, __loff_t); void floppy_init(void); #endif /* _FIWIX_FLOPPY_H */ ================================================ FILE: include/fiwix/font.h ================================================ /* * fiwix/include/fiwix/font.h * * Copyright 2021, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_FONT_H #define _FIWIX_FONT_H struct fbcon_font_desc { char *name; int width; int height; void *data; void *cursorshape; }; extern struct fbcon_font_desc font_vga_8x8; extern struct fbcon_font_desc font_vga_8x14; extern struct fbcon_font_desc font_vga_8x16; struct fbcon_font_desc *fbcon_find_font(int); #endif /* _FIWIX_FONT_H */ ================================================ FILE: include/fiwix/fs.h ================================================ /* * fiwix/include/fiwix/fs.h * * Copyright 2018-2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_FS_H #define _FIWIX_FS_H #include #include #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 0x01 /* mount read-only */ #define MS_REMOUNT 0x20 /* 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 #define CLEAR_BIT 0 #define SET_BIT 1 #define INODE_LOCKED 0x01 #define INODE_DIRTY 0x02 struct inode { __mode_t i_mode; /* file mode */ __u32 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 */ __u32 i_gid; /* group id */ __nlink_t i_nlink; /* links count */ __blk_t i_blocks; /* blocks count */ __u32 i_flags; /* file flags */ struct inode *mount_point; __u32 state; __dev_t dev; __ino_t inode; __s16 count; __dev_t rdev; struct fs_operations *fsop; struct superblock *sb; struct inode *prev; struct inode *next; struct inode *prev_hash; struct inode *next_hash; struct inode *prev_free; struct inode *next_free; union { #ifdef CONFIG_FS_MINIX struct minix_i_info minix; #endif /* CONFIG_FS_MINIX */ struct ext2_i_info ext2; struct pipefs_inode pipefs; struct iso9660_inode iso9660; struct procfs_inode procfs; #ifdef CONFIG_NET struct sockfs_inode sockfs; #endif /* CONFIG_NET */ } u; }; extern struct inode *inode_table; extern struct inode **inode_hash_table; /* values to be determined during system startup */ extern unsigned int inode_hash_table_size; /* size in bytes */ #define SUPERBLOCK_LOCKED 0x01 #define SUPERBLOCK_DIRTY 0x02 struct superblock { __dev_t dev; unsigned int flags; unsigned int state; struct inode *root; /* root inode of mounted fs */ struct inode *dir; /* inode on which the fs was mounted */ struct fs_operations *fsop; __u32 s_blocksize; unsigned char s_blocksize_bits; union { #ifdef CONFIG_FS_MINIX struct minix_sb_info minix; #endif /* CONFIG_FS_MINIX */ struct ext2_sb_info 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 *, struct fd *, int, unsigned int); __loff_t (*llseek)(struct inode *, __loff_t); int (*readdir)(struct inode *, struct fd *, struct dirent *, __size_t); int (*readdir64)(struct inode *, struct fd *, struct dirent64 *, __size_t); int (*mmap)(struct inode *, struct vma *); int (*select)(struct inode *, struct fd *, 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 *, int, __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 *, int); 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 int minix_balloc(struct superblock *); extern void minix_bfree(struct superblock *, int); /* 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; extern int ext2_balloc(struct superblock *); extern void ext2_bfree(struct superblock *, int); /* 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 int isodate(const 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 *); /* fs_devpts.h prototypes */ extern struct fs_operations devpts_fsop; extern struct fs_operations devpts_dir_fsop; /* generic VFS function prototypes */ void inode_lock(struct inode *); void inode_unlock(struct inode *); struct inode *ialloc(struct superblock *, int); 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 *add_mount_point(__dev_t, const char *, const char *); void del_mount_point(struct mount *); struct mount *get_mount_point(struct inode *); int get_new_fd(struct inode *); void release_fd(unsigned int); int get_new_user_fd(int); void release_user_fd(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_mknod(char *, __mode_t, __dev_t); int do_select(int, fd_set *, fd_set *, fd_set *, fd_set *, fd_set *, fd_set *); #endif /* _FIWIX_FS_H */ ================================================ FILE: include/fiwix/fs_devpts.h ================================================ /* * fiwix/include/fiwix/fs_devpts.h * * Copyright 2025, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifdef CONFIG_UNIX98_PTYS #ifndef _FIWIX_FS_DEVPTS_H #define _FIWIX_FS_DEVPTS_H #include #define DEVPTS_ROOT_INO 1 /* root inode */ #define DEVPTS_SUPER_MAGIC 0x1CD1 /* same as in Linux */ #define NR_PTYS 64 extern struct fs_operations devpts_fsop; struct devpts_files { int count; struct inode *inode; }; extern struct devpts_files *devpts_list; #endif /* _FIWIX_FS_DEVPTS_H */ #endif /* CONFIG_UNIX98_PTYS */ ================================================ FILE: include/fiwix/fs_ext2.h ================================================ /* * 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_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) #define EXT2_DESC_PER_BLOCK(s) ((s)->u.ext2.desc_per_block) /* * 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 */ }; /* * 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 */ }; /* * EXT2_DIR_PAD defines the directory entries boundaries * * NOTE: It must be a multiple of 4 */ #define EXT2_DIR_PAD 4 #define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) #define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ ~EXT2_DIR_ROUND) /* * 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 */ }; /* * Ext2 directory file types. Only the low 3 bits are used. The * other bits are reserved for now. */ #define EXT2_FT_UNKNOWN 0 #define EXT2_FT_REG_FILE 1 #define EXT2_FT_DIR 2 #define EXT2_FT_CHRDEV 3 #define EXT2_FT_BLKDEV 4 #define EXT2_FT_FIFO 5 #define EXT2_FT_SOCK 6 #define EXT2_FT_SYMLINK 7 /* superblock in memory */ struct ext2_sb_info { unsigned int desc_per_block; unsigned int block_groups; struct ext2_super_block sb; }; /* inode in memory */ struct ext2_i_info { __u32 i_data[EXT2_N_BLOCKS]; /* Pointers to blocks */ __u32 i_dtime; }; #endif /* _FIWIX_FS_EXT2_H */ ================================================ FILE: include/fiwix/fs_iso9660.h ================================================ /* * 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 /* 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 */ ================================================ FILE: include/fiwix/fs_minix.h ================================================ /* * fiwix/include/fiwix/fs_minix.h * * Copyright 2018, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifdef CONFIG_FS_MINIX #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 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 *, int); 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 *, int); 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 */ #endif /* CONFIG_FS_MINIX */ ================================================ FILE: include/fiwix/fs_pipe.h ================================================ /* * 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 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 */ ================================================ FILE: include/fiwix/fs_proc.h ================================================ /* * fiwix/include/fiwix/fs_proc.h * * Copyright 2018-2023, 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_ROOT_INO 1 /* root inode */ #define PROC_KMSG_INO 13 /* /proc/kmsg 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 PIDs */ #define PROC_FD_INO 0x50000000 /* base for FD inodes */ #define PROC_FD_LEV 2 /* array level for FDs */ #define PROC_ARRAY_ENTRIES 25 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_STATM, 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]; extern struct fs_operations procfs_kmsg_fsop; int data_proc_buddyinfo(char *, __pid_t); 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_pci(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_unix(char *, __pid_t); int data_proc_pci_devices(char *, __pid_t); int data_proc_buffernr(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); int data_proc_dirty_background_ratio(char *, __pid_t); /* PID related functions */ int data_proc_pid_fd(char *, __pid_t, __ino_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_statm(char *, __pid_t); int data_proc_pid_status(char *, __pid_t); #endif /* _FIWIX_FS_PROC_H */ ================================================ FILE: include/fiwix/fs_sock.h ================================================ /* * fiwix/include/fiwix/fs_sock.h * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifdef CONFIG_NET #ifndef _FIWIX_FS_SOCK_H #define _FIWIX_FS_SOCK_H #include extern struct fs_operations sockfs_fsop; struct sockfs_inode { struct socket sock; }; #endif /* _FIWIX_FS_SOCK_H */ #endif /* CONFIG_NET */ ================================================ FILE: include/fiwix/i386elf.h ================================================ /* * 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 int check_elf(struct elf32_hdr *); #endif /* _FIWIX_ELF_H */ ================================================ FILE: include/fiwix/ioctl.h ================================================ /* * fiwix/include/fiwix/ioctl.h */ #ifndef _FIWIX_IOCTL_H #define _FIWIX_IOCTL_H #define HDIO_GETGEO 0x0301 /* get device geometry */ #define BLKROSET 0x125D /* set device read-only (0 = read-write) */ #define BLKROGET 0x125E /* get read-only status (0 = read_write) */ #define BLKRRPART 0x125F /* re-read partition table */ #define BLKGETSIZE 0x1260 /* return device size */ #define BLKFLSBUF 0x1261 /* flush buffer cache */ #define BLKSSZGET 0x1268 /* get block device sector size */ #define BLKBSZGET 0x1270 /* get device block size */ #define BLKBSZSET 0x1271 /* set device block size */ /* 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 0x80045430 /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK 0x40045431 /* 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 */ ================================================ FILE: include/fiwix/ipc.h ================================================ /* * fiwix/include/fiwix/ipc.h */ #ifdef CONFIG_SYSVIPC #ifndef _FIWIX_IPC_H #define _FIWIX_IPC_H #include #include #define IPC_CREAT 01000 /* create if key doesn't exist */ #define IPC_EXCL 02000 /* fail if key exists */ #define IPC_NOWAIT 04000 /* return error on wait */ #define IPC_PRIVATE ((key_t)0) /* private key */ #define IPC_RMID 0 /* remove identifier */ #define IPC_SET 1 /* set options */ #define IPC_STAT 2 /* get options */ #define IPC_INFO 3 /* get system-wide limits */ #define IPC_R 0400 /* read or receive permission */ #define IPC_W 0200 /* write or send permission */ #define SEMOP 1 #define SEMGET 2 #define SEMCTL 3 #define MSGSND 11 #define MSGRCV 12 #define MSGGET 13 #define MSGCTL 14 #define SHMAT 21 #define SHMDT 22 #define SHMGET 23 #define SHMCTL 24 #define IPC_UNUSED ((void *) -1) typedef int key_t; struct sysvipc_args { int arg1; int arg2; int arg3; void *ptr; int arg5; }; /* IPC data structure */ struct ipc_perm { key_t key; /* key */ __uid_t uid; /* effective UID of owner */ __gid_t gid; /* effective UID of owner */ __uid_t cuid; /* effective UID of creator */ __gid_t cgid; /* effective UID of creator */ unsigned short int mode; /* access modes */ unsigned short int seq; /* slot sequence number */ }; extern struct resource ipcmsg_resource; void ipc_init(void); int ipc_has_perms(struct ipc_perm *, int); #endif /* _FIWIX_IPC_H */ #endif /* CONFIG_SYSVIPC */ ================================================ FILE: include/fiwix/irq.h ================================================ /* * fiwix/include/fiwix/irq.h * * Copyright 2021-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_IRQ_H #define _FIWIX_IRQ_H #include #define NR_IRQS 16 /* hardware interrupts */ struct interrupt { unsigned int ticks; char *name; void (*handler)(int, struct sigcontext *); struct interrupt *next; }; extern struct interrupt *irq_table[NR_IRQS]; #define BH_ACTIVE 0x01 struct bh { int flags; void (*fn)(struct sigcontext *); struct bh *next; }; void add_bh(struct bh *); int register_irq(int, struct interrupt *); int unregister_irq(int, const struct interrupt *); void irq_handler(int, struct sigcontext); void unknown_irq_handler(void); void do_bh(struct sigcontext); void irq_init(void); #endif /* _FIWIX_IRQ_H */ ================================================ FILE: include/fiwix/kd.h ================================================ /* * 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 */ ================================================ FILE: include/fiwix/kernel.h ================================================ /* * fiwix/include/fiwix/kernel.h * * Copyright 2018-2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_KERNEL_H #define _FIWIX_KERNEL_H #include #include #define QEMU_DEBUG_PORT 0xE9 /* for Bochs-style debug console */ #define BUDDY_MAX_LEVEL 7 #define KERN_EMERG "<0>" /* system is unusable */ #define KERN_ALERT "<1>" /* action must be taken immediately */ #define KERN_CRIT "<2>" /* critical conditions */ #define KERN_ERROR "<3>" /* error conditions */ #define KERN_WARNING "<4>" /* warning conditions */ #define KERN_NOTICE "<5>" /* normal but significant condition */ #define KERN_INFO "<6>" /* informational */ #define KERN_DEBUG "<7>" /* debug-level messages */ #define PANIC(format, args...) \ { \ printk("\nPANIC: in %s()", __FUNCTION__); \ printk("\n"); \ printk(format, ## args); \ kstat.flags |= KF_HAS_PANICKED; \ stop_kernel(); \ } #define CURRENT_TIME (kstat.system_time) #define CURRENT_TICKS (kstat.ticks) #define INIT_PROGRAM "/sbin/init" /* kernel flags */ #define KF_HAS_PANICKED 0x01 /* the kernel has panic'ed */ #define KF_HAS_DEBUGCON 0x02 /* QEMU debug console support */ extern char *init_argv[]; extern char *init_envp[]; extern char *init_args; extern Elf32_Shdr *symtab, *strtab; extern unsigned int _last_data_addr; extern int kexec_proto; extern int kexec_size; extern char kexec_cmdline[NAME_MAX + 1]; 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 kernel_cmdline[NAME_MAX + 1]; struct kernel_stat { int flags; /* kernel flags */ 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 */ int physical_pages; /* physical memory (in pages) */ int kernel_reserved; /* kernel memory reserved (in KB) */ int physical_reserved; /* physical memory reserved (in KB) */ int total_mem_pages; /* total memory (in pages) */ int free_pages; /* pages on free list */ int min_free_pages; /* minimal free pages in system */ int max_inodes; /* max. number of allocated inodes */ int nr_inodes; /* current allocated inodes */ int max_buffers_size; /* max. allocated buffers (in KB) */ int buffers_size; /* current allocated buffers (in KB) */ int nr_buffers; /* number of buffers created */ int cached; /* memory used to cache file pages */ int shared; /* pages with count > 1 */ int max_dirty_buffers; /* max. number of dirty buffers */ int dirty_buffers; /* dirty buffers (in KB) */ int nr_dirty_buffers; /* current dirty buffers */ unsigned int random_seed; /* next random seed */ int pages_reclaimed; /* last pages reclaimed from buffer */ int nr_flocks; /* current allocated file locks */ /* buddy_low algorithm statistics */ int buddy_low_count[BUDDY_MAX_LEVEL + 1]; int buddy_low_num_pages; /* number of pages used */ int buddy_low_mem_requested; /* total memory requested (in bytes) */ int mount_points; /* number of fs currently mounted */ }; extern struct kernel_stat kstat; unsigned int get_last_boot_addr(unsigned int, unsigned int); void multiboot(unsigned int, unsigned int); void start_kernel(unsigned int, unsigned int, unsigned int); void stop_kernel(void); void init_init(void); void cpu_idle(void); #ifdef CUSTOM_KERNEL_H #include #endif #endif /* _FIWIX_KERNEL_H */ ================================================ FILE: include/fiwix/kexec.h ================================================ /* * fiwix/include/fiwix/kexec.h * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifdef CONFIG_KEXEC #ifndef _FIWIX_KEXEC_H #define _FIWIX_KEXEC_H #define KEXEC_MULTIBOOT1 0x01 #define KEXEC_LINUX 0x02 #define KEXEC_BOOT_ADDR 0x9D000 void kexec_multiboot1(void); void kexec_linux(void); #endif /* _FIWIX_KEXEC_H */ #endif /* CONFIG_KEXEC */ ================================================ FILE: include/fiwix/keyboard.h ================================================ /* * fiwix/include/fiwix/keyboard.h * * Copyright 2018-2021, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #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 E0ENTER 96 #define RCTRL 97 #define E0SLASH 98 #define ALTGR 100 #define E0HOME 102 #define E0UP 103 #define E0PGUP 104 #define E0LEFT 105 #define E0RIGHT 106 #define E0END 107 #define E0DOWN 108 #define E0PGDN 109 #define E0INS 110 #define E0DEL 111 #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 SYSRQ (0x06 + SPEC_KEYS) #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 TILDE (0x03 + DEAD_KEYS) #define DIERE (0x04 + DEAD_KEYS) #define SHIFT (0x00 + SHIFT_KEYS) #define CTRL (0x02 + SHIFT_KEYS) #define ALT (0x03 + SHIFT_KEYS) #define LSHIFT (0x04 + SHIFT_KEYS) #define RSHIFT (0x05 + SHIFT_KEYS) #define LCTRL (0x06 + 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) #ifdef __KERNEL__ #include #include struct diacritic { unsigned char letter; unsigned char code; }; extern __key_t keymap[NR_MODIFIERS * NR_SCODES]; void set_leds(unsigned char); void irq_keyboard(int num, struct sigcontext *); void irq_keyboard_bh(struct sigcontext *); void keyboard_init(void); #endif /* __KERNEL__ */ #endif /* _FIWIX_KEYBOARD_H */ ================================================ FILE: include/fiwix/kparms.h ================================================ /* * fiwix/include/fiwix/kparms.h * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_KPARMS_H #define _FIWIX_KPARMS_H #include #define CMDL_ARG_LEN 100 /* max. length of cmdline argument */ #define CMDL_NUM_VALUES 30 /* max. values of cmdline parameter */ #define KPARMS_IDE_NODMA 0x01 /* disable DMA in all ATA drives */ #define KPARMS_PS2_NORESET 0x02 /* disable PS/2 controller reset */ struct kernel_params { int flags; char bgaresolution[15 + 1]; char initrd[DEVNAME_MAX + 1]; int memsize; int extmemsize; int ramdisksize; int ro; int rootdev; char rootfstype[10 + 1]; char rootdevname[DEVNAME_MAX + 1]; int syscondev; }; extern struct kernel_params kparms; struct kernel_params_value { char *name; char *value[CMDL_NUM_VALUES]; unsigned int sysval[CMDL_NUM_VALUES]; }; #endif /* _FIWIX_KPARMS_H */ ================================================ FILE: include/fiwix/limits.h ================================================ /* * 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 50 /* 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 (cannot be bigger than PAGE_SIZE) */ #define UIO_MAXIOV 16 /* maximum number of scatter/gather elements that can be processed in one call */ #ifdef CUSTOM_LIMITS_H #include #endif #endif /* _FIWIX_LIMITS_H */ ================================================ FILE: include/fiwix/linker.h ================================================ /* * fiwix/include/fiwix/linker.h * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_LINKER_H #define _FIWIX_LINKER_H #include #ifdef CONFIG_VM_SPLIT22 #define PAGE_OFFSET 0x80000000 /* VM split: 2GB user / 2GB kernel */ #else #define PAGE_OFFSET 0xC0000000 /* VM split: 3GB user / 1GB kernel */ #endif /* CONFIG_VM_SPLIT22 */ #define KERNEL_ADDR 0x100000 #define KERNEL_STACK 4096 #define GDT_BASE (0xFFFFFFFF - (PAGE_OFFSET - 1)) #endif /* _FIWIX_LINKER_H */ ================================================ FILE: include/fiwix/linuxboot.h ================================================ /* * include/fiwix/linuxboot.h * * Copyright 2024, Richard R. Masters. * Distributed under the terms of the Fiwix License. */ #define IS_VGA_VGA_COLOR 0x22 struct screen_info { __u8 unused1[6]; __u8 mode; __u8 cols; __u8 unused2[2]; __u16 ega_bx; __u8 unused3[2]; __u8 lines; __u8 is_vga; __u16 points; __u8 unused4[46]; } __attribute__((packed)); struct setup_header { __u8 unused1[9]; __u16 video_mode; __u8 unused2[10]; __u16 version; __u8 unused3[8]; __u8 loader_type; __u8 load_flags; __u8 unused4[6]; __u32 initramfs_addr; __u32 initramfs_size; __u8 unused5[8]; __u32 cmdline_addr; __u32 initramfs_addr_max; __u8 unused6[60]; } __attribute__((packed)); struct bios_mem_entry { __u64 addr; __u64 size; __u32 type; } __attribute__((packed)); #define MAX_BIOS_MEM_ENTRIES 128 struct boot_params { struct screen_info screen_info; __u8 unused1[424]; __u8 num_bios_mem_entries; __u8 unused2[8]; struct setup_header hdr; __u8 unused3[100]; struct bios_mem_entry bios_mem_table[MAX_BIOS_MEM_ENTRIES]; __u8 unused4[816]; } __attribute__((packed)); /* End of Linux code */ ================================================ FILE: include/fiwix/locks.h ================================================ /* * 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 *prev; struct flock_file *next; }; extern struct flock_file *flock_file_table; int posix_lock(int, int, struct flock *); void flock_release_inode(struct inode *); int flock_inode(struct inode *, int); #endif /* _FIWIX_LOCKS_H */ ================================================ FILE: include/fiwix/lp.h ================================================ /* * 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 */ ================================================ FILE: include/fiwix/memdev.h ================================================ /* * fiwix/include/fiwix/memdev.h * * Copyright 2018-2022, 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_MEM 1 /* minor for /dev/mem */ #define MEMDEV_KMEM 2 /* minor for /dev/kmem */ #define MEMDEV_NULL 3 /* minor for /dev/null */ #define MEMDEV_PORT 4 /* minor for /dev/port */ #define MEMDEV_ZERO 5 /* minor for /dev/zero */ #define MEMDEV_FULL 7 /* minor for /dev/full */ #define MEMDEV_RANDOM 8 /* minor for /dev/random */ #define MEMDEV_URANDOM 9 /* minor for /dev/urandom */ 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); __loff_t mem_llseek(struct inode *, __loff_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); __loff_t kmem_llseek(struct inode *, __loff_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); __loff_t null_llseek(struct inode *, __loff_t); int port_open(struct inode *, struct fd *); int port_close(struct inode *, struct fd *); int port_read(struct inode *, struct fd *, char *, __size_t); int port_write(struct inode *, struct fd *, const char *, __size_t); __loff_t port_llseek(struct inode *, __loff_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); __loff_t zero_llseek(struct inode *, __loff_t); int full_open(struct inode *, struct fd *); int full_close(struct inode *, struct fd *); int full_read(struct inode *, struct fd *, char *, __size_t); int full_write(struct inode *, struct fd *, const char *, __size_t); __loff_t full_llseek(struct inode *, __loff_t); int urandom_open(struct inode *, struct fd *); int urandom_close(struct inode *, struct fd *); int urandom_read(struct inode *, struct fd *, char *, __size_t); int urandom_write(struct inode *, struct fd *, const char *, __size_t); __loff_t urandom_llseek(struct inode *, __loff_t); int memdev_open(struct inode *, struct fd *); int mem_mmap(struct inode *, struct vma *); void memdev_init(void); #endif /* _FIWIX_MEMDEV_H */ ================================================ FILE: include/fiwix/mm.h ================================================ /* * fiwix/include/fiwix/mm.h * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_MEMORY_H #define _FIWIX_MEMORY_H #include #include #include /* convert from physical to virtual the addresses below PAGE_OFFSET only */ #define P2V(addr) (addr < PAGE_OFFSET ? addr + PAGE_OFFSET : addr) #define V2P(addr) (addr - PAGE_OFFSET) #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_LOCKED 0x001 #define PAGE_BUDDYLOW 0x010 /* page belongs to buddy_low */ #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) struct page { int page; /* page number */ int count; /* usage counter */ int flags; __ino_t inode; /* inode of the file */ __off_t offset; /* file offset */ __dev_t dev; /* device where file resides */ 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; /* buddy_low.c */ static const unsigned int bl_blocksize[] = { 32, 64, 128, 256, 512, 1024, 2048, 4096 }; struct bl_head { unsigned char level; /* size class (exponent of the power of 2) */ struct bl_head *prev; struct bl_head *next; }; unsigned int bl_malloc(__size_t); void bl_free(unsigned int); void buddy_low_init(void); /* alloc.c */ unsigned int kmalloc(__size_t); 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(struct page *); int is_valid_page(int); void invalidate_inode_pages(struct inode *); 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 reserve_pages(unsigned int, unsigned int); void page_init(int); /* memory.c */ unsigned int map_kaddr(unsigned int *,unsigned int, unsigned int, unsigned int, int); void bss_init(void); unsigned int setup_tmp_pgdir(unsigned int, unsigned int); 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); unsigned int map_page_flags(struct proc *, unsigned int, unsigned int, unsigned int, int); int unmap_page(unsigned int); void mem_init(void); void mem_stats(void); /* swapper.c */ int kswapd(void); #endif /* _FIWIX_MEMORY_H */ ================================================ FILE: include/fiwix/mman.h ================================================ /* * fiwix/include/fiwix/mman.h * * Copyright 2018-2022, 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 0x1 /* sync memory asynchronously */ #define MS_INVALIDATE 0x2 /* invalidate the caches */ #define MS_SYNC 0x4 /* synchronous memory sync */ #define MCL_CURRENT 1 /* lock all current mappings */ #define MCL_FUTURE 2 /* lock all future mappings */ #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 */ #define P_SHM 7 /* shared memory section */ /* 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 free_vma_pages(struct vma *, unsigned int, __size_t); void release_binary(void); struct vma *find_vma_region(unsigned int); struct vma *find_vma_intersection(unsigned int, unsigned int); int expand_heap(unsigned int); unsigned int get_unmapped_vma_region(unsigned int); int do_mmap(struct inode *, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, char, char, void *); int do_munmap(unsigned int, __size_t); int do_mprotect(struct vma *, unsigned int, __size_t, int); #endif /* _FIWIX_MMAN_H */ ================================================ FILE: include/fiwix/msg.h ================================================ /* * fiwix/include/fiwix/msg.h */ #ifdef CONFIG_SYSVIPC #ifndef _FIWIX_MSG_H #define _FIWIX_MSG_H #include #include #define MSG_NOERROR 010000 /* no error if message is too big */ #define MSG_EXCEPT 020000 /* recv any msg except of specified type */ /* system-wide limits */ #define MSGMAX 4096 /* max. size of a message */ #define MSGMNI 128 /* max. number of message queues */ #define MSGMNB 16384 /* total size of message queue */ #define MSGTQL 1024 /* max. number of messages */ #define MSG_STAT 11 #define MSG_INFO 12 struct msqid_ds { struct ipc_perm msg_perm; /* access permissions */ struct msg *msg_first; /* ptr to the first message in queue */ struct msg *msg_last; /* ptr to the last message in queue */ __time_t msg_stime; /* time of the last msgsnd() */ __time_t msg_rtime; /* time of the last msgrcv() */ __time_t msg_ctime; /* time of the last change */ unsigned int unused1; unsigned int unused2; unsigned short int msg_cbytes; /* number of bytes in queue */ unsigned short int msg_qnum; /* number of messages in queue */ unsigned short int msg_qbytes; /* max. number of bytes in queue */ unsigned short int msg_lspid; /* PID of last msgsnd() */ unsigned short int msg_lrpid; /* PID of last msgrcv() */ }; /* message buffer for msgsnd() and msgrcv() */ struct msgbuf { int mtype; /* type of message */ char mtext[1]; /* message text */ }; /* buffer for msgctl() with IPC_INFO and MSG_INFO commands */ struct msginfo { int msgpool; int msgmap; int msgmax; /* MSGMAX */ int msgmnb; /* MSGMNB */ int msgmni; /* MSGMNI */ int msgssz; int msgtql; /* MSGTQL */ unsigned short int msgseg; }; /* one msg structure for each message */ struct msg { struct msg *msg_next; /* next message on queue */ int msg_type; char *msg_spot; /* message text address */ __time_t msg_stime; /* msgsnd time */ short int msg_ts; /* message text size */ }; extern struct msqid_ds *msgque[]; extern unsigned int num_queues; extern unsigned int num_msgs; extern unsigned int max_mqid; extern unsigned int msg_seq; void msg_init(void); struct msqid_ds *msg_get_new_mq(void); void msg_release_mq(struct msqid_ds *); struct msg *msg_get_new_md(void); void msg_release_md(struct msg *); int sys_msgsnd(int, const void *, __size_t, int); int sys_msgrcv(int, void *, __size_t, int, int); int sys_msgget(key_t, int); int sys_msgctl(int, int, struct msqid_ds *); #endif /* _FIWIX_MSG_H */ #endif /* CONFIG_SYSVIPC */ ================================================ FILE: include/fiwix/multiboot1.h ================================================ /* multiboot.h - Multiboot header file. */ /* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc. * * 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 ANY * DEVELOPER OR DISTRIBUTOR 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. */ #ifndef MULTIBOOT_HEADER #define MULTIBOOT_HEADER 1 /* How many bytes from the start of the file we search for the header. */ #define MULTIBOOT_SEARCH 8192 #define MULTIBOOT_HEADER_ALIGN 4 /* The magic field should contain this. */ #define MULTIBOOT_HEADER_MAGIC 0x1BADB002 /* This should be in %eax. */ #define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 /* Alignment of multiboot modules. */ #define MULTIBOOT_MOD_ALIGN 0x00001000 /* Alignment of the multiboot info structure. */ #define MULTIBOOT_INFO_ALIGN 0x00000004 /* Flags set in the ’flags’ member of the multiboot header. */ /* Align all boot modules on i386 page (4KB) boundaries. */ #define MULTIBOOT_PAGE_ALIGN 0x00000001 /* Must pass memory information to OS. */ #define MULTIBOOT_MEMORY_INFO 0x00000002 /* Must pass video information to OS. */ #define MULTIBOOT_VIDEO_MODE 0x00000004 /* This flag indicates the use of the address fields in the header. */ #define MULTIBOOT_AOUT_KLUDGE 0x00010000 /* Flags to be set in the ’flags’ member of the multiboot info structure. */ /* is there basic lower/upper memory information? */ #define MULTIBOOT_INFO_MEMORY 0x00000001 /* is there a boot device set? */ #define MULTIBOOT_INFO_BOOTDEV 0x00000002 /* is the command-line defined? */ #define MULTIBOOT_INFO_CMDLINE 0x00000004 /* are there modules to do something with? */ #define MULTIBOOT_INFO_MODS 0x00000008 /* These next two are mutually exclusive */ /* is there a symbol table loaded? */ #define MULTIBOOT_INFO_AOUT_SYMS 0x00000010 /* is there an ELF section header table? */ #define MULTIBOOT_INFO_ELF_SHDR 0X00000020 /* is there a full memory map? */ #define MULTIBOOT_INFO_MEM_MAP 0x00000040 /* Is there drive info? */ #define MULTIBOOT_INFO_DRIVE_INFO 0x00000080 /* Is there a config table? */ #define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100 /* Is there a boot loader name? */ #define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200 /* Is there a APM table? */ #define MULTIBOOT_INFO_APM_TABLE 0x00000400 /* Is there video information? */ #define MULTIBOOT_INFO_VBE_INFO 0x00000800 #define MULTIBOOT_INFO_FRAMEBUFFER_INFO 0x00001000 #ifndef ASM_FILE typedef unsigned char multiboot_uint8_t; typedef unsigned short multiboot_uint16_t; typedef unsigned int multiboot_uint32_t; typedef unsigned long long multiboot_uint64_t; struct multiboot_header { /* Must be MULTIBOOT_MAGIC - see above. */ multiboot_uint32_t magic; /* Feature flags. */ multiboot_uint32_t flags; /* The above fields plus this one must equal 0 mod 2^32. */ multiboot_uint32_t checksum; /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */ multiboot_uint32_t header_addr; multiboot_uint32_t load_addr; multiboot_uint32_t load_end_addr; multiboot_uint32_t bss_end_addr; multiboot_uint32_t entry_addr; /* These are only valid if MULTIBOOT_VIDEO_MODE is set. */ multiboot_uint32_t mode_type; multiboot_uint32_t width; multiboot_uint32_t height; multiboot_uint32_t depth; }; /* The symbol table for a.out. */ struct multiboot_aout_symbol_table { multiboot_uint32_t tabsize; multiboot_uint32_t strsize; multiboot_uint32_t addr; multiboot_uint32_t reserved; }; typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t; /* The section header table for ELF. */ struct multiboot_elf_section_header_table { multiboot_uint32_t num; multiboot_uint32_t size; multiboot_uint32_t addr; multiboot_uint32_t shndx; }; typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t; struct multiboot_info { /* Multiboot info version number */ multiboot_uint32_t flags; /* Available memory from BIOS */ multiboot_uint32_t mem_lower; multiboot_uint32_t mem_upper; /* "root" partition */ multiboot_uint32_t boot_device; /* Kernel command line */ multiboot_uint32_t cmdline; /* Boot-Module list */ multiboot_uint32_t mods_count; multiboot_uint32_t mods_addr; union { multiboot_aout_symbol_table_t aout_sym; multiboot_elf_section_header_table_t elf_sec; } u; /* Memory Mapping buffer */ multiboot_uint32_t mmap_length; multiboot_uint32_t mmap_addr; /* Drive Info buffer */ multiboot_uint32_t drives_length; multiboot_uint32_t drives_addr; /* ROM configuration table */ multiboot_uint32_t config_table; /* Boot Loader Name */ multiboot_uint32_t boot_loader_name; /* APM table */ multiboot_uint32_t apm_table; /* Video */ multiboot_uint32_t vbe_control_info; multiboot_uint32_t vbe_mode_info; multiboot_uint16_t vbe_mode; multiboot_uint16_t vbe_interface_seg; multiboot_uint16_t vbe_interface_off; multiboot_uint16_t vbe_interface_len; multiboot_uint64_t framebuffer_addr; multiboot_uint32_t framebuffer_pitch; multiboot_uint32_t framebuffer_width; multiboot_uint32_t framebuffer_height; multiboot_uint8_t framebuffer_bpp; #define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 #define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 #define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 multiboot_uint8_t framebuffer_type; union { struct { multiboot_uint32_t framebuffer_palette_addr; multiboot_uint16_t framebuffer_palette_num_colors; }; struct { multiboot_uint8_t framebuffer_red_field_position; multiboot_uint8_t framebuffer_red_mask_size; multiboot_uint8_t framebuffer_green_field_position; multiboot_uint8_t framebuffer_green_mask_size; multiboot_uint8_t framebuffer_blue_field_position; multiboot_uint8_t framebuffer_blue_mask_size; }; }; }; typedef struct multiboot_info multiboot_info_t; struct multiboot_color { multiboot_uint8_t red; multiboot_uint8_t green; multiboot_uint8_t blue; }; struct multiboot_mmap_entry { multiboot_uint32_t size; multiboot_uint64_t addr; multiboot_uint64_t len; #define MULTIBOOT_MEMORY_AVAILABLE 1 #define MULTIBOOT_MEMORY_RESERVED 2 #define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 #define MULTIBOOT_MEMORY_NVS 4 #define MULTIBOOT_MEMORY_BADRAM 5 multiboot_uint32_t type; } __attribute__((packed)); typedef struct multiboot_mmap_entry multiboot_memory_map_t; struct multiboot_mod_list { /* the memory used goes from bytes ’mod_start’ to ’mod_end-1’ inclusive */ multiboot_uint32_t mod_start; multiboot_uint32_t mod_end; /* Module command line */ multiboot_uint32_t cmdline; /* padding to take it to 16 bytes (must be zero) */ multiboot_uint32_t pad; }; typedef struct multiboot_mod_list multiboot_module_t; /* APM BIOS info. */ struct multiboot_apm_info { multiboot_uint16_t version; multiboot_uint16_t cseg; multiboot_uint32_t offset; multiboot_uint16_t cseg_16; multiboot_uint16_t dseg; multiboot_uint16_t flags; multiboot_uint16_t cseg_len; multiboot_uint16_t cseg_16_len; multiboot_uint16_t dseg_len; }; /* VBE controller information. */ struct vbe_controller { unsigned char signature[4]; unsigned short version; unsigned long oem_string; unsigned long capabilities; unsigned long video_mode; unsigned short total_memory; unsigned short oem_software_rev; unsigned long oem_vendor_name; unsigned long oem_product_name; unsigned long oem_product_rev; unsigned char reserved[222]; unsigned char oem_data[256]; } __attribute__ ((packed)); /* VBE mode information. */ struct vbe_mode { unsigned short mode_attributes; unsigned char win_a_attributes; unsigned char win_b_attributes; unsigned short win_granularity; unsigned short win_size; unsigned short win_a_segment; unsigned short win_b_segment; unsigned long win_func; unsigned short bytes_per_scanline; /* >=1.2 */ unsigned short x_resolution; unsigned short y_resolution; unsigned char x_char_size; unsigned char y_char_size; unsigned char number_of_planes; unsigned char bits_per_pixel; unsigned char number_of_banks; unsigned char memory_model; unsigned char bank_size; unsigned char number_of_image_pages; unsigned char reserved0; /* direct color */ unsigned char red_mask_size; unsigned char red_field_position; unsigned char green_mask_size; unsigned char green_field_position; unsigned char blue_mask_size; unsigned char blue_field_position; unsigned char reserved_mask_size; unsigned char reserved_field_position; unsigned char direct_color_mode_info; /* >=2.0 */ unsigned long phys_base; unsigned long reserved1; unsigned short reversed2; /* >=3.0 */ unsigned short linear_bytes_per_scanline; unsigned char banked_number_of_image_pages; unsigned char linear_number_of_image_pages; unsigned char linear_red_mask_size; unsigned char linear_red_field_position; unsigned char linear_green_mask_size; unsigned char linear_green_field_position; unsigned char linear_blue_mask_size; unsigned char linear_blue_field_position; unsigned char linear_reserved_mask_size; unsigned char linear_reserved_field_position; unsigned long max_pixel_clock; unsigned char reserved3[190]; } __attribute__ ((packed)); #endif /* ! ASM_FILE */ #endif /* ! MULTIBOOT_HEADER */ ================================================ FILE: include/fiwix/net/ipv4.h ================================================ /* * fiwix/include/fiwix/net/ipv4.h * * Copyright 2025, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifdef CONFIG_NET #ifndef _FIWIX_NET_IPV4_H #define _FIWIX_NET_IPV4_H #include /* AF_INET */ struct ipv4_info { int count; struct socket *socket; struct ipv4_info *next; }; extern struct ipv4_info *ipv4_socket_head; extern struct proto_ops ipv4_ops; int ipv4_create(struct socket *, int, int, int); void ipv4_free(struct socket *); int ipv4_bind(struct socket *, const struct sockaddr *, int); int ipv4_listen(struct socket *, int); int ipv4_connect(struct socket *, const struct sockaddr *, int); int ipv4_accept(struct socket *, struct sockaddr *, unsigned int *); int ipv4_getname(struct socket *, struct sockaddr *, unsigned int *, int); int ipv4_socketpair(struct socket *, struct socket *); int ipv4_send(struct socket *, struct fd *, const char *, __size_t, int); int ipv4_recv(struct socket *, struct fd *, char *, __size_t, int); int ipv4_sendto(struct socket *, struct fd *, const char *, __size_t, int, const struct sockaddr *, int); int ipv4_recvfrom(struct socket *, struct fd *, char *, __size_t, int, struct sockaddr *, int *); int ipv4_read(struct socket *, struct fd *, char *, __size_t); int ipv4_write(struct socket *, struct fd *, const char *, __size_t); int ipv4_ioctl(struct socket *, struct fd *, int, unsigned int); int ipv4_select(struct socket *, int); int ipv4_shutdown(struct socket *, int); int ipv4_setsockopt(struct socket *, int, int, const void *, unsigned int); int ipv4_getsockopt(struct socket *, int, int, void *, unsigned int *); int ipv4_init(void); #endif /* _FIWIX_NET_IPV4_H */ #endif /* CONFIG_NET */ ================================================ FILE: include/fiwix/net/packet.h ================================================ /* * fiwix/include/fiwix/net/packet.h * * Copyright 2024, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifdef CONFIG_NET #ifndef _FIWIX_NET_PACKET_H #define _FIWIX_NET_PACKET_H #include struct packet { char *data; int len; __off_t offset; struct socket *socket; struct packet *prev; struct packet *next; }; struct packet *peek_packet(struct packet *); struct packet *remove_packet_from_queue(struct packet **); void append_packet_to_queue(struct packet *, struct packet **); #endif /* _FIWIX_NET_PACKET_H */ #endif /* CONFIG_NET */ ================================================ FILE: include/fiwix/net/unix.h ================================================ /* * fiwix/include/fiwix/net/unix.h * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifdef CONFIG_NET #ifndef _FIWIX_NET_UNIX_H #define _FIWIX_NET_UNIX_H #include #include /* AF_UNIX */ struct unix_info { int count; char *data; int readoff; int writeoff; int size; struct sockaddr_un *sun; short int sun_len; struct inode *inode; struct socket *socket; struct packet *packet_queue; struct unix_info *peer; struct unix_info *next; }; extern struct unix_info *unix_socket_head; extern struct proto_ops unix_ops; int unix_create(struct socket *, int, int, int); void unix_free(struct socket *); int unix_bind(struct socket *, const struct sockaddr *, int); int unix_listen(struct socket *, int); int unix_connect(struct socket *, const struct sockaddr *, int); int unix_accept(struct socket *, struct sockaddr *, unsigned int *); int unix_getname(struct socket *, struct sockaddr *, unsigned int *, int); int unix_socketpair(struct socket *, struct socket *); int unix_send(struct socket *, struct fd *, const char *, __size_t, int); int unix_recv(struct socket *, struct fd *, char *, __size_t, int); int unix_sendto(struct socket *, struct fd *, const char *, __size_t, int, const struct sockaddr *, int); int unix_recvfrom(struct socket *, struct fd *, char *, __size_t, int, struct sockaddr *, int *); int unix_read(struct socket *, struct fd *, char *, __size_t); int unix_write(struct socket *, struct fd *, const char *, __size_t); int unix_ioctl(struct socket *, struct fd *, int, unsigned int); int unix_select(struct socket *, int); int unix_shutdown(struct socket *, int); int unix_setsockopt(struct socket *, int, int, const void *, unsigned int); int unix_getsockopt(struct socket *, int, int, void *, unsigned int *); int unix_init(void); #endif /* _FIWIX_NET_UNIX_H */ #endif /* CONFIG_NET */ ================================================ FILE: include/fiwix/net.h ================================================ /* * fiwix/include/fiwix/net.h * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifdef CONFIG_NET #ifndef _FIWIX_NET_H #define _FIWIX_NET_H #include #include #include #include #include #define SYS_SOCKET 1 #define SYS_BIND 2 #define SYS_CONNECT 3 #define SYS_LISTEN 4 #define SYS_ACCEPT 5 #define SYS_GETSOCKNAME 6 #define SYS_GETPEERNAME 7 #define SYS_SOCKETPAIR 8 #define SYS_SEND 9 #define SYS_RECV 10 #define SYS_SENDTO 11 #define SYS_RECVFROM 12 #define SYS_SHUTDOWN 13 #define SYS_SETSOCKOPT 14 #define SYS_GETSOCKOPT 15 typedef unsigned int socklen_t; struct socket { short int state; int flags; struct fd *fd; int fd_ext; /* fd of external TCP/IP API */ short int type; struct proto_ops *ops; int queue_len; /* number of connections in queue */ int queue_limit; /* max. number of pending connections */ struct socket *queue_head; /* first connection in queue */ struct socket *next_queue; /* next connection in queue */ union { struct unix_info unix_info; struct ipv4_info ipv4_info; } u; }; struct domain_table { int family; char *name; struct proto_ops *ops; }; struct proto_ops { int (*create)(struct socket *, int, int, int); void (*free)(struct socket *); int (*bind)(struct socket *, const struct sockaddr *, int); int (*listen)(struct socket *, int); int (*connect)(struct socket *, const struct sockaddr *, int); int (*accept)(struct socket *, struct sockaddr *, unsigned int *); int (*getname)(struct socket *, struct sockaddr *, unsigned int *, int); int (*socketpair)(struct socket *, struct socket *); int (*send)(struct socket *, struct fd *, const char *, __size_t, int); int (*recv)(struct socket *, struct fd *, char *, __size_t, int); int (*sendto)(struct socket *, struct fd *, const char *, __size_t, int, const struct sockaddr *, int); int (*recvfrom)(struct socket *, struct fd *, char *, __size_t, int, struct sockaddr *, int *); int (*read)(struct socket *, struct fd *, char *, __size_t); int (*write)(struct socket *, struct fd *, const char *, __size_t); int (*ioctl)(struct socket *, struct fd *, int, unsigned int); int (*select)(struct socket *, int); int (*shutdown)(struct socket *, int); int (*setsockopt)(struct socket *, int, int, const void *, unsigned int); int (*getsockopt)(struct socket *, int, int, void *, unsigned int *); int (*init)(void); }; int assign_proto(struct socket *, int); struct socket *get_socket_from_queue(struct socket *); int insert_socket_to_queue(struct socket *, struct socket *); int sock_alloc(struct socket **); void sock_free(struct socket *); int socket(int, int, int); int bind(int, struct sockaddr *, int); int listen(int, int); int connect(int, struct sockaddr *, int); int accept(int, struct sockaddr *, unsigned int *); int getname(int, struct sockaddr *, unsigned int *, int); int socketpair(int, int, int, int [2]); int send(int, const void *, __size_t, int); int recv(int, void *, __size_t, int); int sendto(int, const void *, __size_t, int, const struct sockaddr *, int); int recvfrom(int, void *, __size_t, int, struct sockaddr *, int *); int shutdown(int, int); int setsockopt(int, int, int, const void *, socklen_t); int getsockopt(int, int, int, void *, socklen_t *); void net_init(void); #endif /* _FIWIX_NET_H */ #endif /* CONFIG_NET */ ================================================ FILE: include/fiwix/netdevice.h ================================================ /* * fiwix/include/fiwix/netdevice.h * * Copyright 2026, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifdef CONFIG_NET #ifndef _FIWIX_NETDEVICE_H #define _FIWIX_NETDEVICE_H int dev_ioctl(int, void *); #endif /* _FIWIX_NETDEVICE_H */ #endif /* CONFIG_NET */ ================================================ FILE: include/fiwix/part.h ================================================ /* * 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 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 */ ================================================ FILE: include/fiwix/pci.h ================================================ /* * fiwix/include/fiwix/pci.h * * Copyright 2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifdef CONFIG_PCI #ifndef _FIWIX_PCI_H #define _FIWIX_PCI_H #include #define PCI_ADDRESS 0x0CF8 #define PCI_DATA 0x0CFC #define PCI_MAX_BUS 16 /* 256 buses maximum */ #define PCI_MAX_DEV 32 #define PCI_MAX_FUNC 8 #define PCI_VENDOR_ID 0x00 /* 16 bits */ #define PCI_DEVICE_ID 0x02 /* 16 bits */ #define PCI_COMMAND 0x04 /* 16 bits */ #define PCI_COMMAND_IO 0x1 /* enable response in I/O space */ #define PCI_COMMAND_MEMORY 0x2 /* enable response in memory space */ #define PCI_COMMAND_MASTER 0x4 /* enable bus mastering */ #define PCI_STATUS 0x06 /* 16 bits */ #define PCI_REVISION_ID 0x08 /* 8 bits */ #define PCI_PROG_IF 0x09 /* 8 bits */ #define PCI_CLASS_DEVICE 0x0A /* 16 bits (class + subclass) */ #define PCI_CACHE_LINE_SIZE 0x0C /* 8 bits */ #define PCI_LATENCY_TIMER 0x0D /* 8 bits */ #define PCI_HEADER_TYPE 0x0E /* 8 bits */ #define PCI_HEADER_TYPE_NORMAL 0x00 /* standard header */ #define PCI_BIST 0x0F /* 8 bits */ #define PCI_BASE_ADDR_0 0x10 /* 32 bits */ #define PCI_BASE_ADDR_1 0x14 /* 32 bits (header 0 and 1 only) */ #define PCI_BASE_ADDR_2 0x18 /* 32 bits (header 0 only) */ #define PCI_BASE_ADDR_3 0x1C /* 32 bits */ #define PCI_BASE_ADDR_4 0x20 /* 32 bits */ #define PCI_BASE_ADDR_5 0x24 /* 32 bits */ #define PCI_BASE_ADDR_SPACE 0x01 /* 0 = memory, 1 = I/O */ #define PCI_BASE_ADDR_SPACE_MEM 0x00 #define PCI_BASE_ADDR_SPACE_IO 0x01 #define PCI_BASE_ADDR_TYPE_MASK 0x06 /* base register size (32bit/64bit) */ #define PCI_BASE_ADDR_TYPE_32 0x00 /* 32 bit address */ #define PCI_BASE_ADDR_TYPE_64 0x04 /* 64 bit address */ #define PCI_BASE_ADDR_MEM_PREF 0x08 /* prefetchable memory */ #define PCI_BASE_ADDR_MEM_MASK 0xFFFFFFF0 #define PCI_BASE_ADDR_IO_MASK 0xFFFFFFFC #define PCI_INTERRUPT_LINE 0x3C /* 8 bits */ #define PCI_INTERRUPT_PIN 0x3D /* 8 bits */ #define PCI_MIN_GRANT 0x3E /* 8 bits */ #define PCI_MAX_LATENCY 0x3F /* 8 bits */ /* bus mastering */ #define BM_COMMAND 0x00 /* command register primary */ #define BM_STATUS 0x02 /* status register primary */ #define BM_PRD_ADDRESS 0x04 /* PRD table address primary */ #define BM_COMMAND_START 0x01 /* start */ #define BM_COMMAND_READ 0x08 /* read from disk and write to memory */ #define BM_COMMAND_WRITE 0x00 /* read from memory and write to disk */ #define BM_STATUS_ACTIVE 0x01 /* active */ #define BM_STATUS_ERROR 0x02 /* error */ #define BM_STATUS_INTR 0x04 /* IDE interrupt */ #define BM_STATUS_DRVDMA 0x20 /* drive 0 is DMA capable */ /* 0x40: drive 1 is DMA capable */ #define BM_STATUS_SIMPLEX 0x80 /* simplex only */ /* flags */ #define PCI_F_ADDR_SPACE_IO 0x01 #define PCI_F_ADDR_SPACE_MEM 0x02 #define PCI_F_ADDR_MEM_32 0x04 /* 32 bit address */ #define PCI_F_ADDR_MEM_64 0x08 /* 64 bit address */ #define PCI_F_ADDR_SPACE_PREFET 0x10 /* prefetchable memory */ #define PCI_DEVFN(dev,func) ((((dev) & 0x1f) << 3) | ((func) & 0x07)) struct pci_supported_devices { unsigned short int vendor_id; unsigned short int device_id; }; struct pci_device { unsigned char bus; unsigned char dev; unsigned char func; unsigned short int vendor_id; unsigned short int device_id; unsigned short int command; unsigned short int status; unsigned char rev; unsigned char prog_if; unsigned short int class; unsigned char cline_size; unsigned char latency; unsigned char hdr_type; unsigned char bist; unsigned int obar[6]; /* original address */ unsigned int bar[6]; unsigned char irq; unsigned char pin; unsigned char min_gnt; unsigned char max_lat; char size[6]; char flags[6]; const char *name; struct pci_device *prev; struct pci_device *next; }; extern struct pci_device *pci_device_table; unsigned char pci_read_char(struct pci_device *, int); unsigned short int pci_read_short(struct pci_device *, int); unsigned int pci_read_long(struct pci_device *, int); void pci_write_char(struct pci_device *, int, unsigned char); void pci_write_short(struct pci_device *, int, unsigned short int); void pci_write_long(struct pci_device *, int, unsigned int); void pci_show_desc(struct pci_device *); void pci_init(void); #endif /* _FIWIX_PCI_H */ #endif /* CONFIG_PCI */ ================================================ FILE: include/fiwix/pci_ids.h ================================================ /* * fiwix/include/fiwix/pci_ids.h * * Copyright 2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_PCI_IDS_H #define _FIWIX_PCI_IDS_H /* device classes and subclasses */ #define PCI_CLASS_NOT_DEFINED 0x0000 #define PCI_CLASS_NOT_DEFINED_VGA 0x0001 #define PCI_CLASS_STORAGE_IDE 0x0101 #define PCI_CLASS_NETWORK_ETHERNET 0x0200 #define PCI_CLASS_DISPLAY_VGA 0x0300 #define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400 #define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401 #define PCI_CLASS_MULTIMEDIA_OTHER 0x0480 #define PCI_CLASS_BRIDGE_HOST 0x0600 #define PCI_CLASS_BRIDGE_ISA 0x0601 #define PCI_CLASS_BRIDGE_PCI 0x0604 #define PCI_CLASS_BRIDGE_OTHER 0x0680 #define PCI_CLASS_COMMUNICATION_SERIAL 0x0700 #define PCI_CLASS_SYSTEM_PIC 0x0800 #define PCI_CLASS_SERIAL_FIREWIRE 0x0c00 #define PCI_CLASS_SERIAL_USB 0x0c03 /* supported vendor and device ids */ #define PCI_VENDOR_ID_BOCHS 0x1234 #define PCI_DEVICE_ID_BGA 0x1111 #endif /* _FIWIX_PCI_IDS_H */ ================================================ FILE: include/fiwix/pic.h ================================================ /* * fiwix/include/fiwix/pic.h * * Copyright 2018-2021, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_PIC_H #define _FIWIX_PIC_H #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 */ void enable_irq(int); void disable_irq(int); void spurious_interrupt(int); void ack_pic_irq(int); void pic_init(void); #endif /* _FIWIX_PIC_H */ ================================================ FILE: include/fiwix/pit.h ================================================ /* * 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 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 bits * --------------------------------------------------------------------- * #7 R:system board RAM parity W:IRQ=1, 0=reset * #6 R:channel check W:reserved * #5 R:timer 2 (speaker time) output W:reserved * #4 R:refresh request (toggle) W:reserved * #3 R:channel check status W:channel check enable * #2 R:parity check status W:parity check enable * #1 R:speaker data status W:speaker data enable * #0 R:timer 2 gate to speaker status W:timer 2 gate to speaker enable */ #define PS2_SYSCTRL_B 0x61 /* PS/2 system control port B (R/W) */ #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); int pit_getcounter0(void); void pit_init(unsigned short int); #endif /* _FIWIX_PIT_H */ ================================================ FILE: include/fiwix/process.h ================================================ /* * fiwix/include/fiwix/process.h * * Copyright 2018-2022, 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; /* segment type (P_TEXT, P_DATA, ...) */ struct inode *inode; /* file inode */ char o_mode; /* open mode (O_RDONLY, O_RDWR, ...) */ void *object; /* generic pointer (currently only for shm) */ struct vma *prev; struct vma *next; }; #include #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 PF_NOTINTERRUPT 0x00000008 /* non-interruptible sleeping */ #define MMAP_START 0x40000000 /* mmap()s start at 1GB */ #define IS_SUPERUSER (current->euid == 0) #define IO_BITMAP_SIZE 8192 /* 8192*8bit = all I/O address space */ #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) p = proc_table_head->next ; while(p) #define FOR_EACH_PROCESS_RUNNING(p) p = proc_run_head ; while(p) /* 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; extern struct proc *proc_table_head; 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; unsigned char io_bitmap[IO_BITMAP_SIZE + 1]; }; struct proc { struct i386tss tss; struct proc *ppid; /* pointer to parent process */ __pid_t pid; /* 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; unsigned int end_code; char argv0[NAME_MAX + 1]; int argc; char **argv; int envc; char **envp; char pidstr[5]; /* PID number converted to string */ struct vma *vma_table; /* 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 int it_real_interval, it_real_value; unsigned int it_virt_interval, it_virt_value; unsigned int it_prof_interval, it_prof_value; unsigned int timeout; struct rlimit rlim[RLIM_NLIMITS]; unsigned int rss; __mode_t umask; unsigned char loopcnt; /* nested symlinks counter */ #ifdef CONFIG_SYSVIPC struct sem_undo *semundo; #endif /* CONFIG_SYSVIPC */ struct proc *prev; struct proc *next; struct proc *prev_sleep; struct proc *next_sleep; struct proc *prev_run; struct proc *next_run; }; extern struct proc *current; extern struct proc *proc_table; int can_signal(struct proc *); int send_sig(struct proc *, __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); struct proc *kernel_process(const char *, int (*fn)(void)); void proc_slot_init(struct proc *); void proc_init(void); int elf_load(struct inode *, struct binargs *, struct sigcontext *, char *); int script_load(char *, char *, char *); #endif /* _FIWIX_PROCESS_H */ ================================================ FILE: include/fiwix/ps2.h ================================================ /* * fiwix/include/fiwix/ps2.h * * Copyright 2024, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_PS2_H #define _FIWIX_PS2_H #define PS2_DATA 0x60 /* I/O data port */ #define PS2_STATUS 0x64 /* status register port */ #define PS2_COMMAND 0x64 /* command/control port */ /* status register */ #define PS2_STAT_OUTBUSY 0x01 /* output buffer full, don't read yet */ #define PS2_STAT_INBUSY 0x02 /* input buffer full, don't write yet */ #define PS2_STAT_TXTMOUT 0x20 /* transmit time-out error */ #define PS2_STAT_RXTMOUT 0x40 /* receive time-out error */ #define PS2_STAT_PARERR 0x80 /* parity error */ #define PS2_STAT_COMMERR (PS2_STAT_TXTMOUT | PS2_STAT_RXTMOUT) /* controller commands */ #define PS2_CMD_RECV_CONFIG 0x20 /* read controller's config byte */ #define PS2_CMD_SEND_CONFIG 0x60 /* write controller's config byte */ #define PS2_CMD_DISABLE_CH2 0xA7 /* disable second channel (if any) */ #define PS2_CMD_ENABLE_CH2 0xA8 /* enable second channel (if any) */ #define PS2_CMD_TEST_CH2 0xA9 /* test interface second channel */ #define PS2_CMD_SELF_TEST 0xAA /* self-test command */ #define PS2_CMD_TEST_CH1 0xAB /* test interface first channel */ #define PS2_CMD_DISABLE_CH1 0xAD /* disable first channel */ #define PS2_CMD_ENABLE_CH1 0xAE /* enable first channel */ #define PS2_CMD_CH2_PREFIX 0xD4 /* second channel (mouse) prefix */ #define PS2_CMD_HOTRESET 0xFE /* Hot Reset */ /* device commands */ #define PS2_DEV_GETINFO 0xE9 /* get current status information */ #define PS2_KB_SETLED 0xED /* set/reset status indicators (LEDs) */ #define PS2_KB_ECHO 0xEE /* echo (for diagnostics only) */ #define PS2_KB_GETSETSCAN 0xF0 /* keyboard get/set scan code */ #define PS2_DEV_IDENTIFY 0xF2 /* device identify (for PS/2 only) */ #define PS2_DEV_RATE 0xF3 /* set typematic rate/delay */ #define PS2_DEV_ENABLE 0xF4 /* keyboard enable scanning */ #define PS2_KB_DISABLE 0xF5 /* keyboard disable scanning */ #define PS2_AUX_DISABLE 0xF6 /* mouse disable scanning */ #define PS2_DEV_RESET 0xFF /* device reset */ #define DEV_RESET_OK 0xAA /* self-test passed */ #define DEV_ACK 0xFA /* acknowledge */ #define PS2_TIMEOUT 600000 extern volatile unsigned char ack; int ps2_wait_ack(void); void ps2_write(const unsigned char, const unsigned char); unsigned char ps2_read(const unsigned char); void ps2_clear_buffer(void); void ps2_init(void); #endif /* _FIWIX_PS2_H */ ================================================ FILE: include/fiwix/psaux.h ================================================ /* * fiwix/include/fiwix/psaux.h * * Copyright 2024, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifdef CONFIG_PSAUX #ifndef _FIWIX_PSAUX_H #define _FIWIX_PSAUX_H #include #include #include #define PSAUX_IRQ 12 #define PSAUX_MAJOR 10 /* major number for /dev/psaux */ #define PSAUX_MINOR 1 /* minor number for /dev/psaux */ struct psaux { int count; struct clist read_q; struct clist write_q; }; extern struct psaux psaux_table; int psaux_open(struct inode *, struct fd *); int psaux_close(struct inode *, struct fd *); int psaux_read(struct inode *, struct fd *, char *, __size_t); int psaux_write(struct inode *, struct fd *, const char *, __size_t); int psaux_select(struct inode *, struct fd *, int); void irq_psaux(int num, struct sigcontext *); void psaux_init(void); #endif /* _FIWIX_PSAUX_H */ #endif /* CONFIG_PSAUX */ ================================================ FILE: include/fiwix/pty.h ================================================ /* * fiwix/include/fiwix/pty.h * * Copyright 2025, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifdef CONFIG_UNIX98_PTYS #ifndef _FIWIX_PTY_H #define _FIWIX_PTY_H #define PTY_MASTER_MAJOR 5 /* major number for /dev/ptmx */ #define PTY_MASTER_MINOR 2 /* minor number for /dev/ptmx */ #define PTY_SLAVE_MAJOR 136 /* major number for PTY slaves */ #define PTMX_DEV MKDEV(PTY_MASTER_MAJOR, PTY_MASTER_MINOR) void pty_wakeup_read(struct tty *); int pty_open(struct tty *); int pty_close(struct tty *); int pty_read(struct inode *, struct fd *, char *, __size_t); int pty_write(struct inode *, struct fd *, const char *, __size_t); int pty_ioctl(struct tty *, struct fd *, int, unsigned int); int pty_select(struct inode *, struct fd *, int); void pty_init(void); #endif /* _FIWIX_PTY_H */ #endif /* CONFIG_UNIX98_PTYS */ ================================================ FILE: include/fiwix/ramdisk.h ================================================ /* * 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_TOTAL 10 /* total number of ramdisk drives */ struct ramdisk { char *addr; /* ramdisk memory address */ int size; /* in KB */ }; extern int ramdisk_minors; /* initrd + RAMDISK_DRIVES + kexec */ extern struct ramdisk ramdisk_table[RAMDISK_TOTAL]; 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 *, struct fd *, int, unsigned int); __loff_t ramdisk_llseek(struct inode *, __loff_t); void ramdisk_init(void); #endif /* _FIWIX_RAMDISK_H */ ================================================ FILE: include/fiwix/reboot.h ================================================ /* * 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 */ ================================================ FILE: include/fiwix/resource.h ================================================ /* * 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 */ ================================================ FILE: include/fiwix/sched.h ================================================ /* * 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_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 */ ================================================ FILE: include/fiwix/segments.h ================================================ /* * fiwix/include/fiwix/segments.h * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_SEGMENTS_H #define _FIWIX_SEGMENTS_H #include #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 */ #define USER_PL 0x03 /* User Privilege Level 3 */ /* flags for memory pages */ #define PAGE_PRESENT 0x001 /* Present */ #define PAGE_RW 0x002 /* Read/Write */ #define PAGE_USER 0x004 /* User */ #define PAGE_NOALLOC 0x200 /* No Page Allocated (OS managed) */ #ifndef ASM_FILE #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_CODE 0x0A /* CODE Exec/Read */ #define SD_DATA 0x02 /* DATA Read/Write */ #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 (kernel) */ #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 */ /* 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 /* ! ASM_FILE */ #endif /* _FIWIX_SEGMENTS_H */ ================================================ FILE: include/fiwix/sem.h ================================================ /* * fiwix/include/fiwix/sem.h */ #ifdef CONFIG_SYSVIPC #ifndef _FIWIX_SEM_H #define _FIWIX_SEM_H #include #include #define SEM_UNDO 0x1000 /* undo the operation on exit */ /* semctl() command definitions */ #define GETPID 11 /* get sempid */ #define GETVAL 12 /* get semval */ #define GETALL 13 /* get all semval's */ #define GETNCNT 14 /* get semncnt */ #define GETZCNT 15 /* get semzcnt */ #define SETVAL 16 /* set semval */ #define SETALL 17 /* set all semval's */ /* system-wide limits */ #define SEMMNI 128 /* number of semaphores sets */ #define SEMMSL 32 /* max. number of semaphores per id */ #define SEMMNS (SEMMNI*SEMMSL) /* max. number of messages in system */ #define SEMOPM 32 /* max. number of ops. per semop() */ #define SEMVMX 32767 /* semaphore maximum value */ #define SEM_STAT 18 #define SEM_INFO 19 struct semid_ds { struct ipc_perm sem_perm; /* access permissions */ __time_t sem_otime; /* time of the last semop() */ __time_t sem_ctime; /* time of the last change */ struct sem *sem_base; /* ptr to the first semaphore in set */ unsigned int unused1; unsigned int unused2; struct sem_undo *undo; /* list of undo requests */ unsigned short int sem_nsems; /* number of semaphores in set */ }; /* semaphore buffer for semop() */ struct sembuf { unsigned short int sem_num; /* semaphore number */ short int sem_op; /* semaphore operation */ short int sem_flg; /* operation flags */ }; /* arg for semctl() */ union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */ unsigned short int *array; /* array for GETALL & SETALL */ struct seminfo *__buf; /* buffer for IPC_INFO */ }; /* semaphore information structure */ struct seminfo { int semmap; int semmni; int semmns; int semmnu; int semmsl; int semopm; int semume; int semusz; int semvmx; int semaem; }; /* one semaphore structure for each semaphore in the system */ struct sem { short int semval; /* current value */ short int sempid; /* pid of last operation */ short int semncnt; /* nprocs awaiting increase in semval */ short int semzcnt; /* nprocs awaiting semval = 0 */ }; /* list of undo requests executed automatically when the process exits */ struct sem_undo { struct sem_undo *proc_next; /* next entry on this process */ struct sem_undo *id_next; /* next entry on this semaphore set */ int semid; /* semaphore set identifier */ short int semadj; /* adjustment during exit() */ unsigned short int sem_num; /* semaphore number */ }; extern struct semid_ds *semset[]; extern unsigned int num_semsets; extern unsigned int num_sems; extern unsigned int max_semid; extern unsigned int sem_seq; void sem_init(void); struct semid_ds *sem_get_new_ss(void); void sem_release_ss(struct semid_ds *); struct sem *sem_get_new_sma(void); void sem_release_sma(struct sem *); struct sem_undo *sem_get_new_su(void); void sem_release_su(struct sem_undo *); void semexit(void); int sys_semop(int, struct sembuf *, int); int sys_semget(key_t, int, int); int sys_semctl(int, int, int, void *); #endif /* _FIWIX_SEM_H */ #endif /* CONFIG_SYSVIPC */ ================================================ FILE: include/fiwix/serial.h ================================================ /* * fiwix/include/fiwix/serial.h * * Copyright 2020-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_SERIAL_H #define _FIWIX_SERIAL_H #define SERIAL4_IRQ 4 /* IRQ for serial ports 1 and 3 */ #define SERIAL3_IRQ 3 /* IRQ for serial ports 2 and 4 */ #define NR_SERIAL 5 /* maximum number of serial ttys */ #define SERIAL_MAJOR 4 /* major number for /dev/ttyS[n] */ #define SERIAL_MINORS NR_SERIAL #define SERIAL_MSF 6 /* serial minor shift factor */ /* UART registers */ #define UART_TD 0 /* W: Transmitter Holding Buffer */ #define UART_RD 0 /* R: Receiver Buffer */ #define UART_DLL 0 /* RW: Divisor Latch Low Byte */ #define UART_DLH 1 /* RW: Divisor Latch High Byte */ #define UART_IER 1 /* RW: Interrupt Enable Register */ #define UART_IIR 2 /* R: Interrupt Identification Register */ #define UART_FCR 2 /* W: FIFO Control Register */ #define UART_LCR 3 /* RW: Line Control Register */ #define UART_MCR 4 /* RW: Modem Control Register */ #define UART_LSR 5 /* R: Line Status Register */ #define UART_MSR 6 /* R: Modem Status Register */ #define UART_SR 7 /* RW: Scratch Register */ /* Interrupt Enable Register */ #define UART_IER_RDAI 0x1 /* enable Received Data Available Interrupt */ #define UART_IER_THREI 0x2 /* enable Transmitter Holding Register Empty Interrupt */ #define UART_IER_RLSI 0x4 /* enable Receiver Line Status Interrupt */ #define UART_IER_MSI 0x8 /* enable Modem Status Interrupt */ /* Interrupt Identification Register */ #define UART_IIR_NOINT 0x01 /* no interrupts pending */ #define UART_IIR_MKINT 0x06 /* mask all interrupt flags */ #define UART_IIR_MSI 0x00 /* Modem Status Interrupt */ #define UART_IIR_THREI 0x02 /* Transmitter Holding Register Empty Interrupt */ #define UART_IIR_RDAI 0x04 /* Received Data Available Interrupt */ #define UART_IIR_RLSI 0x06 /* Receiver Line Status Interrupt */ #define UART_IIR_FIFOTO 0xC0 /* FIFO TimeOut interrupt */ #define UART_IIR_FIFO64 0x20 /* 64 byte FIFO enabled (16750 only) */ #define UART_IIR_FIFO 0x40 /* FIFO is enabled (still needs bit #7 on) */ #define UART_IIR_FIFOKO 0x80 /* FIFO is enabled, but unusable */ /* FIFO Control Register */ #define UART_FCR_FIFO 0x07 /* enable FIFO (clear receive and transmit) */ #define UART_FCR_CRCVR 0x02 /* clear receiver */ #define UART_FCR_CXMTR 0x04 /* clear transmitter */ #define UART_FCR_DMA 0x08 /* DMA mode select */ #define UART_FCR_FIFO64 0x20 /* enable 64 byte FIFO (16750 only) */ #define UART_FCR_FIFO14 0xC0 /* set to 14 bytes 'trigger level' FIFO */ /* Line Control Register */ #define UART_LCR_WL5 0x00 /* word length 5 bits */ #define UART_LCR_WL6 0x01 /* word length 6 bits */ #define UART_LCR_WL7 0x02 /* word length 7 bits */ #define UART_LCR_WL8 0x03 /* word length 8 bits */ #define UART_LCR_2STB 0x04 /* 2 stop bits */ #define UART_LCR_1STB 0x00 /* 1 stop bit */ #define UART_LCR_NP 0x00 /* no parity */ #define UART_LCR_OP 0x08 /* odd parity */ #define UART_LCR_EP 0x18 /* even parity */ #define UART_LCR_SBRK 0x40 /* Set Break enable */ #define UART_LCR_DLAB 0x80 /* Divisor Latch Access Bit */ /* Modem Control Register */ #define UART_MCR_DTR 0x1 /* Data Terminal Ready */ #define UART_MCR_RTS 0x2 /* Request To Send */ #define UART_MCR_OUT2 0x8 /* Auxiliary Output 2 */ /* Line Status Register */ #define UART_LSR_RDA 0x01 /* Received Data Available */ #define UART_LSR_OE 0x02 /* Overrun Error */ #define UART_LSR_PE 0x04 /* Parity Error */ #define UART_LSR_FE 0x08 /* Framing Error */ #define UART_LSR_BI 0x10 /* Break Interrupt */ #define UART_LSR_THRE 0x20 /* Transmitter Holding Register Empty */ #define UART_LSR_EDHR 0x40 /* Empty Data Holding Registers TD and SH */ #define UART_LSR_EFIFO 0x80 /* Error in Received FIFO */ #define UART_FIFO_SIZE 16 /* 16 bytes */ #define UART_HAS_FIFO 0x02 /* has FIFO working */ #define UART_IS_8250 0x04 /* is a 8250 chip */ #define UART_IS_16450 0x08 /* is a 16450 chip */ #define UART_IS_16550 0x10 /* is a 16550 chip */ #define UART_IS_16550A 0x20 /* is a 16550A chip */ #define UART_ACTIVE 0x80 struct serial { unsigned short int ioaddr; /* port I/O address */ int iosize; char irq; int baud; char *name; short int lctrl; /* line control flags (8N1, 7E2, ...) */ int flags; struct tty *tty; struct serial *next; }; int serial_open(struct tty *); int serial_close(struct tty *); int serial_ioctl(struct tty *, int, unsigned int); void serial_write(struct tty *); void irq_serial(int, struct sigcontext *); void irq_serial_bh(struct sigcontext *); void serial_init(void); #endif /* _FIWIX_SERIAL_H */ ================================================ FILE: include/fiwix/shm.h ================================================ /* * fiwix/include/fiwix/shm.h */ #ifdef CONFIG_SYSVIPC #ifndef _FIWIX_SHM_H #define _FIWIX_SHM_H #include #include #define SHM_DEST 01000 /* destroy segment on last detach */ #define SHM_RDONLY 010000 /* attach a read-only segment */ #define SHM_RND 020000 /* round attach address to SHMLBA */ #define SHM_REMAP 040000 /* take-over region on attach */ /* super user shmctl commands */ #define SHM_LOCK 11 #define SHM_UNLOCK 12 /* system-wide limits */ /* * Since the current kernel memory allocator has a granularity of page size, * it's not possible to go beyond 4096 pages in the array of pointers to page * frames (*shm_pages). Hence SHMMAX must stay to 0x1000000 (4096 * 4096). * * It will be 0x2000000 (or more) when the new kernel memory allocator be * implemented. */ #define SHMMAX 0x1000000 /* max. segment size (in bytes) */ #define SHMMIN 1 /* min. segment size (in bytes) */ #define SHMMNI 128 /* max. number of shared segments */ #define SHMSEG SHMMNI /* max. segments per process */ #define SHMLBA PAGE_SIZE /* low boundary address (in bytes) */ #define SHMALL 524288 /* max. total segments (in pages) */ #define SHM_STAT 13 #define SHM_INFO 14 #define NUM_ATTACHES_PER_SEG (PAGE_SIZE / sizeof(struct vma)) struct shmid_ds { struct ipc_perm shm_perm; /* access permissions */ __size_t shm_segsz; /* size of segment (in bytes) */ __time_t shm_atime; /* time of the last shmat() */ __time_t shm_dtime; /* time of the last shmdt() */ __time_t shm_ctime; /* time of the last change */ unsigned short shm_cpid; /* pid of creator */ unsigned short shm_lpid; /* pid of last shm operation */ unsigned short shm_nattch; /* num. of current attaches */ /* the following are for kernel only */ unsigned short shm_npages; /* size of segment (in pages) */ unsigned int *shm_pages; /* array of ptrs to frames -> SHMMAX */ struct vma *shm_attaches; /* ptr to array of attached regions */ }; struct shminfo { int shmmax; int shmmin; int shmmni; int shmseg; int shmall; }; struct shm_info { int used_ids; unsigned int shm_tot; /* total allocated shm */ unsigned int shm_rss; /* total resident shm */ unsigned int shm_swp; /* total swapped shm */ unsigned int swap_attempts; unsigned int swap_successes; }; extern struct shmid_ds *shmseg[]; extern unsigned int num_segs; extern unsigned int max_segid; extern unsigned int shm_seq; extern unsigned int shm_tot; extern unsigned int shm_rss; void shm_init(void); struct shmid_ds *shm_get_new_seg(void); void shm_release_seg(struct shmid_ds *); void free_seg(int); struct vma *shm_get_new_attach(struct shmid_ds *); void shm_release_attach(struct vma *); int shm_map_page(struct vma *, unsigned int); int sys_shmat(int, char *, int, unsigned int *); int sys_shmdt(char *); int sys_shmget(key_t, __size_t, int); int sys_shmctl(int, int, struct shmid_ds *); #endif /* _FIWIX_SHM_H */ #endif /* CONFIG_SYSVIPC */ ================================================ FILE: include/fiwix/sigcontext.h ================================================ /* * 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 */ ================================================ FILE: include/fiwix/signal.h ================================================ /* * fiwix/include/fiwix/signal.h * * Copyright 2018-2022, 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 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))) #define KERNEL 1 /* kernel is who has sent the signal */ #define USER 2 /* user is who has sent the signal */ int issig(void); void psig(unsigned int); int kill_pid(__pid_t, __sigset_t, int); int kill_pgrp(__pid_t, __sigset_t, int); #endif /* _FIWIX_SIGNAL_H */ ================================================ FILE: include/fiwix/sleep.h ================================================ /* * fiwix/include/fiwix/sleep.h * * Copyright 2018-2021, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_SLEEP_H #define _FIWIX_SLEEP_H #include #define AREA_BH 0x00000001 #define AREA_CALLOUT 0x00000002 #define AREA_TTY_READ 0x00000004 #define AREA_SERIAL_READ 0x00000008 extern struct proc *proc_run_head; struct resource { char locked; char wanted; }; void runnable(struct proc *); void not_runnable(struct proc *, int); int sleep(void *, int); void wakeup(void *); void wakeup_proc(struct proc *); void lock_resource(struct resource *); void unlock_resource(struct resource *); int can_lock_area(unsigned int); int unlock_area(unsigned int); void sleep_init(void); #endif /* _FIWIX_SLEEP_H */ ================================================ FILE: include/fiwix/socket.h ================================================ /* * fiwix/include/fiwix/socket.h * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifdef CONFIG_NET #ifndef _FIWIX_SOCKET_H #define _FIWIX_SOCKET_H #include /* supported address families (domains) */ #define AF_UNIX 1 /* UNIX domain socket */ #define AF_LOCAL AF_UNIX /* POSIX name for AF_UNIX */ #define AF_INET 2 /* IPv4 Internet domain socket */ /* protocol families */ #define PF_UNIX AF_UNIX #define PF_LOCAL AF_LOCAL #define PF_INET AF_INET /* types */ #define SOCK_STREAM 1 #define SOCK_DGRAM 2 /* maximum queue length specifiable by listen() */ #define SOMAXCONN 128 /* states */ #define SS_UNCONNECTED 1 #define SS_CONNECTING 2 #define SS_CONNECTED 3 #define SS_DISCONNECTING 4 /* flags */ #define SO_ACCEPTCONN 0x10000 /* flags for send() and recv() */ #define MSG_PEEK 0x02 #define MSG_DONTWAIT 0x40 typedef unsigned short int sa_family_t; /* generic socket address structure */ struct sockaddr { sa_family_t sa_family; /* address family: AF_xxx */ char sa_data[14]; /* protocol specific address */ }; /* UNIX domain socket address structure */ struct sockaddr_un { sa_family_t sun_family; /* AF_UNIX */ char sun_path[108]; /* socket filename */ }; #endif /* _FIWIX_SOCKET_H */ #endif /* CONFIG_NET */ ================================================ FILE: include/fiwix/stat.h ================================================ #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 */ ================================================ FILE: include/fiwix/statbuf.h ================================================ /* * 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; }; struct stat64 { unsigned long long int st_dev; int __st_dev_padding; int __st_ino_truncated; unsigned int st_mode; unsigned int st_nlink; unsigned int st_uid; unsigned int st_gid; unsigned long long int st_rdev; int __st_rdev_padding; long long int st_size; int st_blksize; long long int st_blocks; int st_atime; int st_atime_nsec; int st_mtime; int st_mtime_nsec; int st_ctime; int st_ctime_nsec; unsigned long long int st_ino; }; #endif /* _FIWIX_STATBUF_H */ ================================================ FILE: include/fiwix/statfs.h ================================================ /* * 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 { int val[2]; } fsid_t; struct statfs { int f_type; int f_bsize; int f_blocks; int f_bfree; int f_bavail; int f_files; int f_ffree; fsid_t f_fsid; int f_namelen; int f_spare[6]; }; #endif /* _FIWIX_STATFS_H */ ================================================ FILE: include/fiwix/stdarg.h ================================================ /* 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 */ ================================================ FILE: include/fiwix/stddef.h ================================================ /* * fiwix/include/fiwix/stddef.h * * Copyright 2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _INCLUDE_STDDEF_H #define _INCLUDE_STDDEF_H #define offsetof(st, m) ((__size_t)&(((st *)0)->m)) #endif /* _INCLUDE_STDDEF_H */ ================================================ FILE: include/fiwix/stdio.h ================================================ /* * 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 #include void flush_log_buf(struct tty *); void printk(const char *, ...); int sprintk(char *, const char *, ...); int snprintk(char *, unsigned int, const char *, ...); #endif /* _INCLUDE_STDIO_H */ ================================================ FILE: include/fiwix/string.h ================================================ /* * fiwix/include/fiwix/string.h * * Copyright 2018-2022, 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 ((void *)0) #endif #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) 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 *strchr(const char *, int); char *strrchr(const char *, int); int strtol(const char *, char **, int); 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); int memcmp(const void *, const void *, unsigned int); void *memmove(void *, void const *, int); #endif /* _INCLUDE_STRING_H */ ================================================ FILE: include/fiwix/syscalls.h ================================================ /* * fiwix/include/fiwix/syscalls.h * * Copyright 2018-2022, 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 #include #define NR_SYSCALLS (sizeof(syscall_table) / sizeof(unsigned int)) #ifdef CONFIG_SYSCALL_6TH_ARG int do_syscall(unsigned int, int, int, int, int, int, int, struct sigcontext); #else int do_syscall(unsigned int, int, int, int, int, int, struct sigcontext); #endif /* CONFIG_SYSCALL_6TH_ARG */ int sys_exit(int); void do_exit(int); #ifdef CONFIG_SYSCALL_6TH_ARG int sys_fork(int, int, int, int, int, int, struct sigcontext *); #else int sys_fork(int, int, int, int, int, struct sigcontext *); #endif /* CONFIG_SYSCALL_6TH_ARG */ 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 *); #ifdef CONFIG_SYSCALL_6TH_ARG int sys_execve(const char *, char **, char **, int, int, int, struct sigcontext *); #else int sys_execve(const char *, char **, char **, int, int, struct sigcontext *); #endif /* CONFIG_SYSCALL_6TH_ARG */ 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_lchown(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 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 int); int sys_fcntl(unsigned int, int, unsigned 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(unsigned int, unsigned 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 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(unsigned int, __off_t); int sys_fchmod(unsigned int, __mode_t); int sys_fchown(unsigned int, __uid_t, __gid_t); int sys_statfs(const char *, struct statfs *); int sys_fstatfs(unsigned int, struct statfs *); int sys_ioperm(unsigned int, unsigned int, int); int sys_socketcall(int, unsigned int *); int sys_syslog(int, char *, 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 *); #ifdef CONFIG_SYSCALL_6TH_ARG int sys_iopl(int, int, int, int, int, int, struct sigcontext *); #else int sys_iopl(int, int, int, int, int, struct sigcontext *); #endif /* CONFIG_SYSCALL_6TH_ARG */ int sys_wait4(__pid_t, int *, int, struct rusage *); int sys_sysinfo(struct sysinfo *); #ifdef CONFIG_SYSVIPC int sys_ipc(unsigned int, struct sysvipc_args *); #endif /* CONFIG_SYSVIPC */ int sys_fsync(unsigned int); #ifdef CONFIG_SYSCALL_6TH_ARG int sys_sigreturn(unsigned int, int, int, int, int, int, struct sigcontext *); #else int sys_sigreturn(unsigned int, int, int, int, int, struct sigcontext *); #endif /* CONFIG_SYSCALL_6TH_ARG */ 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 int); int sys_setfsuid(__uid_t); int sys_setfsgid(__gid_t); int sys_llseek(unsigned int, unsigned int, unsigned 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(unsigned int, int); int sys_readv(int, struct iovec *, int); int sys_writev(int, struct iovec *, int); int sys_getsid(__pid_t); int sys_fdatasync(int); int sys_nanosleep(const struct timespec *, struct timespec *); int sys_chown(const char *, __uid_t, __gid_t); int sys_getcwd(char *, __size_t); #ifdef CONFIG_MMAP2 int sys_mmap2(unsigned int, unsigned int, unsigned int, unsigned int, int, unsigned int); #endif /* CONFIG_MMAP2 */ int sys_truncate64(const char *, __loff_t); int sys_ftruncate64(unsigned int, __loff_t); int sys_stat64(const char *, struct stat64 *); int sys_lstat64(const char *, struct stat64 *); int sys_fstat64(unsigned int, struct stat64 *); int sys_chown32(const char *, unsigned int, unsigned int); int sys_getdents64(unsigned int, struct dirent64 *, unsigned int); int sys_fcntl64(unsigned int, int, unsigned int); int sys_utimes(const char *, struct timeval times[2]); #endif /* _FIWIX_SYSCALLS_H */ ================================================ FILE: include/fiwix/sysconsole.h ================================================ /* * fiwix/include/fiwix/sysconsole.h * * Copyright 2024, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_SYSCONSOLE_H #define _FIWIX_SYSCONSOLE_H #include #include #include struct sysconsole { __dev_t dev; struct tty *tty; }; extern struct sysconsole sysconsole_table[NR_SYSCONSOLES]; int add_sysconsoledev(__dev_t); void register_console(struct tty *); void sysconsole_init(void); #endif /* _FIWIX_SYSCONSOLE_H */ ================================================ FILE: include/fiwix/syslog.h ================================================ /* * fiwix/include/fiwix/syslog.h * * Copyright 2025, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_SYSLOG_H #define _FIWIX_SYSLOG_H #define SYSLOG_CLOSE 0 /* close the log */ #define SYSLOG_OPEN 1 /* open the log */ #define SYSLOG_READ 2 /* read the log */ #define SYSLOG_READ_ALL 3 /* read all messages */ #define SYSLOG_READ_CLEAR 4 /* read and clear all messages */ #define SYSLOG_CLEAR 5 /* clear the buffer */ #define SYSLOG_CONSOLE_OFF 6 /* disable printk to console */ #define SYSLOG_CONSOLE_ON 7 /* enable printk to console */ #define SYSLOG_CONSOLE_LEVEL 8 /* set printk level to console */ #define SYSLOG_SIZE_UNREAD 9 /* get the number of unread chars */ #define SYSLOG_SIZE_BUFFER 10 /* get the size of the buffer */ #define DEFAULT_MESSAGE_LOGLEVEL 6 /* KERN_INFO */ #define DEFAULT_CONSOLE_LOGLEVEL 7 /* KERN_DEBUG */ #define LOG_BUF_LEN 4096 extern char log_buf[LOG_BUF_LEN]; /* circular buffer */ extern unsigned int log_read, log_write, log_size, log_new_chars; extern int console_loglevel; #endif /* _FIWIX_SYSLOG_H */ ================================================ FILE: include/fiwix/sysrq.h ================================================ /* * fiwix/include/fiwix/sysrq.h * * Copyright 2021, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_SYSRQ_H #define _FIWIX_SYSRQ_H /* the key combination consists of Alt+SysRq and another key, defined below */ #define SYSRQ_STACK 0x00000001 /* 'l' -> stack backtrace */ #define SYSRQ_MEMORY 0x00000002 /* 'm' -> memory information */ #define SYSRQ_TASKS 0x00000004 /* 't' -> task list */ #define SYSRQ_UNDEF 0x80000000 /* Undefined operation */ void sysrq(int); #endif /* _FIWIX_SYSRQ_H */ ================================================ FILE: include/fiwix/system.h ================================================ /* * fiwix/include/fiwix/system.h * * Copyright 2018-2022, 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.7.0" #define UTS_DOMAINNAME "(none)" struct sysinfo { int uptime; /* seconds since boot */ unsigned int loads[3]; /* load average (1, 5 and 15 minutes) */ unsigned int totalram; /* total usable main memory size */ unsigned int freeram; /* available memory size */ unsigned int sharedram; /* amount of shared memory */ unsigned int bufferram; /* amount of memory used by buffers */ unsigned int totalswap; /* total swap space size */ unsigned int freeswap; /* available swap space */ unsigned short int procs; /* number of current processes */ char _f[22]; /* pads structure to 64 bytes */ }; #ifdef CUSTOM_SYSTEM_H #include #endif #endif /* _FIWIX_SYSTEM_H */ ================================================ FILE: include/fiwix/termbits.h ================================================ /* * 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 int tcflag_t; /* Type of control characters. */ typedef unsigned char cc_t; /* Type of baud rate specifiers. */ typedef int speed_t; /* 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 */ ================================================ FILE: include/fiwix/termios.h ================================================ /* * 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 /* old terminal control structure */ 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 */ }; #define NCCS 19 /* new 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 */ }; #endif /* _FIWIX_TERMIOS_H */ ================================================ FILE: include/fiwix/time.h ================================================ /* * 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 { int tv_sec; /* seconds since 00:00:00, 1 Jan 1970 UTC */ int tv_nsec; /* nanoseconds (1000000000ns = 1sec) */ }; struct timeval { int tv_sec; /* seconds since 00:00:00, 1 Jan 1970 UTC */ 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 tm { int tm_sec; /* seconds */ int tm_min; /* minutes */ int tm_hour; /* hour */ int tm_mday; /* day of the month */ int tm_month; /* month */ int tm_year; /* year */ int tm_wday; /* day of the week */ int tm_yday; /* day of the year */ int tm_isdst; /* daylight savings flag */ }; unsigned int tv2ticks(const struct timeval *); void ticks2tv(int, struct timeval *); int setitimer(int, const struct itimerval *, struct itimerval *); unsigned int mktime(struct tm *); #endif /* _FIWIX_TIME_H */ ================================================ FILE: include/fiwix/timeb.h ================================================ /* * 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 */ ================================================ FILE: include/fiwix/timer.h ================================================ /* * 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(int, struct sigcontext *); void irq_timer_bh(struct sigcontext *); void do_callouts_bh(struct sigcontext *); void get_system_time(void); void set_system_time(__time_t); int gettimeoffset(void); void timer_init(void); #endif /* _FIWIX_TIMER_H */ ================================================ FILE: include/fiwix/times.h ================================================ /* * 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 */ ================================================ FILE: include/fiwix/traps.h ================================================ /* * fiwix/include/fiwix/traps.h * * Copyright 2018-2021, 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); const char * elf_lookup_symbol(unsigned int addr); void stack_backtrace(void); int dump_registers(unsigned int, struct sigcontext *); #endif /* _FIWIX_TRAPS_H */ ================================================ FILE: include/fiwix/tty.h ================================================ /* * fiwix/include/fiwix/tty.h * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_TTY_H #define _FIWIX_TTY_H #include #include #include #include #include #include #define TAB_SIZE 8 #define MAX_TAB_COLS 132 /* maximum number of tab stops */ /* tty flags */ #define TTY_HAS_LNEXT 0x01 #define TTY_OTHER_CLOSED 0x02 #define TTY_PTY_LOCK 0x04 struct kbd_state { char mode; }; struct tty { __dev_t dev; struct clist read_q; struct clist cooked_q; struct clist write_q; short int count; struct termios termios; struct winsize winsize; struct kbd_state kbd; __pid_t pid, pgid, sid; void *driver_data; int canon_data; char tab_stop[132]; int column; int flags; struct tty *link; struct tty *next; /* tty driver 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 *); int (*open)(struct tty *); int (*close)(struct tty *); int (*ioctl)(struct tty *, struct fd *, int cmd, unsigned int); void (*set_termios)(struct tty *); }; extern struct tty *tty_table; void tty_reset(struct tty *); struct tty *register_tty(__dev_t); void unregister_tty(struct tty *); struct tty *get_tty(__dev_t); void disassociate_ctty(struct tty *); void termios_reset(struct tty *); void tty_deltab(struct tty *); void do_cook(struct tty *); 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 *, struct fd *, int cmd, unsigned int); __loff_t tty_llseek(struct inode *, __loff_t); int tty_select(struct inode *, struct fd *, int); void tty_init(void); int vt_ioctl(struct tty *, int, unsigned int); #endif /* _FIWIX_TTY_H */ ================================================ FILE: include/fiwix/types.h ================================================ /* * 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 __signed__ long long int __s64; typedef unsigned long long int __u64; typedef __u16 __uid_t; typedef __u16 __gid_t; typedef __u32 __ino_t; typedef __u64 __ino64_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 __u32 __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 error */ typedef __s32 __daddr_t; typedef __s64 __loff_t; /* must be signed in order to return error */ /* 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 int) * 8) #define __FDELT(d) ((d) / __NFDBITS) #define __FDMASK(d) (1 << ((d) % __NFDBITS)) /* define the fd_set structure for select() */ typedef struct { unsigned int fds_bits[__FD_SETSIZE / __NFDBITS]; } fd_set; /* define the iovec structure for readv/writev */ struct iovec { void *iov_base; __size_t iov_len; }; #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)) #define __bswap32(x) \ ((unsigned int)( \ ((x & 0xFF) << 24) | \ (((x >> 8) & 0xFF) << 16) | \ (((x >> 16) & 0xFF) << 8) | \ ((x >> 24) & 0xFF) \ )) #endif /* _FIWIX_TYPES_H */ ================================================ FILE: include/fiwix/unistd.h ================================================ /* * fiwix/include/fiwix/unistd.h * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_UNISTD_H #define _FIWIX_UNISTD_H /* * This is intended to be pure Linux 2.0 i386 ABI, plus some system calls from * Linux 2.2 and Linux 2.4. */ /* #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_lchown 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 Linux 2.2) 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 103 #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 117 #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 145 #define SYS_writev 146 #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 */ /* #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 182 #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 /* extra system calls from Linux 2.4 */ /* #define SYS_ugetrlimit */ #define SYS_mmap2 192 #define SYS_truncate64 193 #define SYS_ftruncate64 194 #define SYS_stat64 195 #define SYS_lstat64 196 #define SYS_fstat64 197 #define SYS_chown32 212 #define SYS_getdents64 220 #define SYS_fcntl64 221 #define SYS_utimes 271 #endif /* _FIWIX_UNISTD_H */ ================================================ FILE: include/fiwix/ustat.h ================================================ /* * 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 */ ================================================ FILE: include/fiwix/utime.h ================================================ /* * 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 */ ================================================ FILE: include/fiwix/utsname.h ================================================ /* 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. */ /* * fiwix/include/fiwix/utsname.h * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_UTSNAME_H #define _FIWIX_UTSNAME_H #define _OLD_UTSNAME_LENGTH 8 #define _UTSNAME_LENGTH 64 #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 + 1]; char nodename[_OLD_UTSNAME_LENGTH + 1]; char release[_OLD_UTSNAME_LENGTH + 1]; char version[_OLD_UTSNAME_LENGTH + 1]; char machine[_OLD_UTSNAME_LENGTH + 1]; }; /* old structure describing the system and machine */ struct old_utsname { char sysname[_UTSNAME_LENGTH + 1]; char nodename[_UTSNAME_NODENAME_LENGTH + 1]; char release[_UTSNAME_LENGTH + 1]; char version[_UTSNAME_LENGTH + 1]; char machine[_UTSNAME_LENGTH + 1]; }; /* new structure describing the system and machine */ struct new_utsname { /* name of this implementation of the operating system */ char sysname[_UTSNAME_LENGTH + 1]; /* name of this node on the network */ char nodename[_UTSNAME_NODENAME_LENGTH + 1]; /* current release level of this implementation */ char release[_UTSNAME_LENGTH + 1]; /* current version level of this release */ char version[_UTSNAME_LENGTH + 1]; /* name of the hardware type on which the system is running */ char machine[_UTSNAME_LENGTH + 1]; char domainname[_UTSNAME_LENGTH + 1]; }; extern struct new_utsname sys_utsname; extern char UTS_MACHINE[_UTSNAME_LENGTH + 1]; #endif /* _FIWIX_UTSNAME_H */ ================================================ FILE: include/fiwix/version.h ================================================ #define UTS_VERSION "Sat Nov 15 08:10:29 UTC 2025" ================================================ FILE: include/fiwix/vgacon.h ================================================ /* * fiwix/include/fiwix/vgacon.h * * Copyright 2021, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_VGACON_H #define _FIWIX_VGACON_H #include #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 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 void vgacon_put_char(struct vconsole *, unsigned char); void vgacon_insert_char(struct vconsole *); void vgacon_delete_char(struct vconsole *); void vgacon_update_curpos(struct vconsole *); void vgacon_show_cursor(struct vconsole *, int); void vgacon_get_curpos(struct vconsole *); void vgacon_write_screen(struct vconsole *, int, int, short int); void vgacon_blank_screen(struct vconsole *); void vgacon_scroll_screen(struct vconsole *, int, int); void vgacon_restore_screen(struct vconsole *); void vgacon_screen_on(struct vconsole *); void vgacon_screen_off(unsigned int); void vgacon_buf_scroll(struct vconsole *, int); void vgacon_cursor_blink(unsigned int); void vgacon_init(void); #endif /* _FIWIX_VGACON_H */ ================================================ FILE: include/fiwix/video.h ================================================ /* * fiwix/include/fiwix/video.h * * Copyright 2021, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #ifndef _FIWIX_VIDEO_H #define _FIWIX_VIDEO_H void video_init(void); #endif /* _FIWIX_VIDEO_H */ ================================================ FILE: include/fiwix/vt.h ================================================ #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 */ ================================================ FILE: kernel/Makefile ================================================ # fiwix/kernel/Makefile # # Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. # Distributed under the terms of the Fiwix License. # .S.o: $(CC) $(CONFFLAGS) -traditional -I$(INCLUDE) -c -o $@ $< .c.o: $(CC) $(CFLAGS) -c -o $@ $< OBJS = boot.o core386.o main.o init.o gdt.o idt.o kexec.o syscalls.o pic.o \ pit.o irq.o traps.o cpu.o cmos.o timer.o sched.o sleep.o signal.o \ process.o multiboot.o all: $(OBJS) clean: rm -f *.o ================================================ FILE: kernel/boot.S ================================================ /* * fiwix/kernel/boot.S * * Copyright 2018-2021, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #define ASM_FILE 1 #include #include #define MULTIBOOT_HEADER_FLAGS MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO /* flags for CR0 (control register) */ #define CR0_MP 0x00000002 /* bit 01 -> enable monitor coprocessor */ #define CR0_NE 0x00000020 /* bit 05 -> enable native x87 FPU mode */ #define CR0_WP 0x00010000 /* bit 16 -> enable write protect (for CoW) */ #define CR0_AM 0x00040000 /* bit 18 -> enable alignment checking */ #define CR0_PG 0x80000000 /* bit 31 -> enable paging */ #ifdef __TINYC__ .data #else .section .setup, "a" /* "a" attribute means Allocatable section */ #endif .align 4 tmp_gdtr: .word ((3 * 8) - 1) tmp_gdta: .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 GDT_BASE >> 24 /* 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 GDT_BASE >> 24 /* base address 31-24 */ #ifdef __TINYC__ .text #endif .align 4 multiboot_header: /* multiboot header */ .long MULTIBOOT_HEADER_MAGIC /* magic */ .long MULTIBOOT_HEADER_FLAGS /* flags */ /* checksum */ .long -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) /* not used */ .long 0 /* header_addr */ .long 0 /* load_addr */ .long 0 /* load_end_addr */ .long 0 /* bss_end_addr */ .long 0 /* entry_addr */ /* valid only with GRUB2 */ .long 0 /* mode_type */ .long 0 /* width */ .long 0 /* height */ .long 0 /* depth */ /* * 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. */ .align 4 .globl _start; _start: cli #ifdef __TINYC__ movl $tmp_gdt, %esi subl $PAGE_OFFSET, %esi movl $tmp_gdta, %edi subl $PAGE_OFFSET, %edi movl %esi, (%edi) movl $tmp_gdtr, %esi subl $PAGE_OFFSET, %esi lgdt (%esi) /* load GDTR with the temporary GDT */ #else lgdt tmp_gdtr /* load GDTR with the temporary GDT */ #endif movw $KERNEL_DS, %cx movw %cx, %ds movw %cx, %es movw %cx, %fs movw %cx, %gs movw %cx, %ss ljmp $KERNEL_CS, $setup_kernel .text .align 4 .globl setup_kernel; setup_kernel: movl $PAGE_OFFSET + 0x10000, %esp /* default stack address */ pushl $0 /* reset EFLAGS */ popf pushl %ebx /* save Multiboot info structure */ pushl %eax /* save Multiboot magic value */ call setup_tmp_pgdir /* setup a temporary page directory */ movl %eax, %cr3 movl %cr0, %eax andl $0x00000011, %eax /* disable all, preserve ET & PE (GRUB) */ orl $CR0_PG, %eax /* enable PG */ orl $CR0_AM, %eax /* enable AM */ orl $CR0_WP, %eax /* enable WP */ orl $CR0_NE, %eax /* enable NE */ orl $CR0_MP, %eax /* enable MP */ movl %eax, %cr0 call bss_init /* initialize BSS segment */ call gdt_init /* setup and load the definitive GDT */ call get_last_boot_addr popl %ecx /* restore Multiboot magic value */ popl %ebx /* restore Multiboot info structure */ pushl %eax /* save the last boot address */ pushl %ebx /* save Multiboot info structure */ pushl %ecx /* save Multiboot magic value */ call start_kernel /* not reached */ jmp cpu_idle ================================================ FILE: kernel/cmos.c ================================================ /* * 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 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 int flags; SAVE_FLAGS(flags); CLI(); outport_b(CMOS_INDEX, addr); outport_b(CMOS_DATA, value); RESTORE_FLAGS(flags); } ================================================ FILE: kernel/core386.S ================================================ /* * fiwix/kernel/core386.S * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #define ASM_FILE 1 #include #include #include #define CR0_MP ~(0x00000002) /* CR0 bit-01 MP (Monitor Coprocessor) */ #define CR0_EM 0x00000004 /* CR0 bit-02 EM (Emulation) */ #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 \ pusha ;\ 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 #define BOTTOM_HALVES \ sti ;\ call do_bh #define CHECK_IF_NESTED_INTERRUPT \ cmpw $(KERNEL_CS), CS(%esp) ;\ je 2f #define CHECK_IF_SIGNALS \ call issig ;\ testl $0xFFFFFFFF, %eax ;\ jz 1f ;\ movl %esp, %eax ;\ pushl %eax ;\ call psig ;\ addl $4, %esp ;\ 1: #define CHECK_IF_NEED_SCHEDULE \ movl need_resched, %eax ;\ testl $0xFFFFFFFF, %eax ;\ jz 2f ;\ call do_sched ;\ 2: #define RESTORE_ALL \ popl %gs ;\ popl %fs ;\ popl %es ;\ popl %ds ;\ popa ;\ addl $4, %esp /* suppress error code from stack */ .text #define BUILD_EXCEPTION_SIMUL_ERR(num, name) \ .align 4 ;\ .globl name; name: ;\ pushl $0 /* save simulated error code to stack */;\ SAVE_ALL ;\ EXCEPTION(num) ;\ BOTTOM_HALVES ;\ CHECK_IF_NESTED_INTERRUPT ;\ CHECK_IF_SIGNALS ;\ CHECK_IF_NEED_SCHEDULE ;\ RESTORE_ALL ;\ iret #define BUILD_EXCEPTION(num, name) \ .align 4 ;\ .globl name; name: ;\ SAVE_ALL ;\ EXCEPTION(num) ;\ BOTTOM_HALVES ;\ CHECK_IF_NESTED_INTERRUPT ;\ CHECK_IF_SIGNALS ;\ CHECK_IF_NEED_SCHEDULE ;\ RESTORE_ALL ;\ iret BUILD_EXCEPTION_SIMUL_ERR(0, except0) /* DIVIDE ERROR */ BUILD_EXCEPTION_SIMUL_ERR(1, except1) /* DEBUG */ BUILD_EXCEPTION_SIMUL_ERR(2, except2) /* NMI INTERRUPT */ BUILD_EXCEPTION_SIMUL_ERR(3, except3) /* BREAKPOINT INT3 */ BUILD_EXCEPTION_SIMUL_ERR(4, except4) /* OVERFLOW */ BUILD_EXCEPTION_SIMUL_ERR(5, except5) /* BOUND */ BUILD_EXCEPTION_SIMUL_ERR(6, except6) /* INVALID OPCODE */ .align 4 .globl except7; except7: # NO MATH COPROCESSOR pushl $0 # save simulated error code to stack SAVE_ALL EXCEPTION(0x7) clts # floating-opcode cached! BOTTOM_HALVES CHECK_IF_NESTED_INTERRUPT CHECK_IF_SIGNALS CHECK_IF_NEED_SCHEDULE RESTORE_ALL iret BUILD_EXCEPTION(8, except8) /* DOUBLE FAULT */ BUILD_EXCEPTION_SIMUL_ERR(9, except9) /* COPROCESSOR SEGMENT OVERRUN */ BUILD_EXCEPTION(10, except10) /* INVALID TSS */ BUILD_EXCEPTION(11, except11) /* SEGMENT NOT PRESENT */ BUILD_EXCEPTION(12, except12) /* STACK SEGMENT FAULT */ BUILD_EXCEPTION(13, except13) /* GENERAL PROTECTION FAULT */ BUILD_EXCEPTION(14, except14) /* PAGE FAULT */ BUILD_EXCEPTION_SIMUL_ERR(15, except15) /* INTEL RESERVED */ BUILD_EXCEPTION_SIMUL_ERR(16, except16) /* FLOATING POINT ERROR */ BUILD_EXCEPTION(17, except17) /* ALIGNMENT CHECK */ BUILD_EXCEPTION_SIMUL_ERR(18, except18) /* MACHINE CHECK */ BUILD_EXCEPTION_SIMUL_ERR(19, except19) /* SIMD FLOATING POINT */ BUILD_EXCEPTION_SIMUL_ERR(20, except20) /* INTEL RESERVED */ BUILD_EXCEPTION_SIMUL_ERR(21, except21) /* INTEL RESERVED */ BUILD_EXCEPTION_SIMUL_ERR(22, except22) /* INTEL RESERVED */ BUILD_EXCEPTION_SIMUL_ERR(23, except23) /* INTEL RESERVED */ BUILD_EXCEPTION_SIMUL_ERR(24, except24) /* INTEL RESERVED */ BUILD_EXCEPTION_SIMUL_ERR(25, except25) /* INTEL RESERVED */ BUILD_EXCEPTION_SIMUL_ERR(26, except26) /* INTEL RESERVED */ BUILD_EXCEPTION_SIMUL_ERR(27, except27) /* INTEL RESERVED */ BUILD_EXCEPTION_SIMUL_ERR(28, except28) /* INTEL RESERVED */ BUILD_EXCEPTION_SIMUL_ERR(29, except29) /* INTEL RESERVED */ BUILD_EXCEPTION_SIMUL_ERR(30, except30) /* INTEL RESERVED */ BUILD_EXCEPTION_SIMUL_ERR(31, except31) /* INTEL RESERVED */ #define BUILD_IRQ(num, name) \ .align 4 ;\ .globl name; name: ;\ pushl $0 /* save simulated error code to stack */;\ SAVE_ALL ;\ IRQ(num) ;\ BOTTOM_HALVES ;\ CHECK_IF_NESTED_INTERRUPT ;\ CHECK_IF_SIGNALS ;\ CHECK_IF_NEED_SCHEDULE ;\ RESTORE_ALL ;\ iret BUILD_IRQ(0, irq0) BUILD_IRQ(1, irq1) BUILD_IRQ(2, irq2) BUILD_IRQ(3, irq3) BUILD_IRQ(4, irq4) BUILD_IRQ(5, irq5) BUILD_IRQ(6, irq6) BUILD_IRQ(7, irq7) BUILD_IRQ(8, irq8) BUILD_IRQ(9, irq9) BUILD_IRQ(10, irq10) BUILD_IRQ(11, irq11) BUILD_IRQ(12, irq12) BUILD_IRQ(13, irq13) BUILD_IRQ(14, irq14) BUILD_IRQ(15, irq15) .align 4 .globl unknown_irq; unknown_irq: pushl $0 # save simulated error code to stack SAVE_ALL call unknown_irq_handler 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 | USER_PL), %eax movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs pushl %eax pushl $PAGE_OFFSET - 4 # user stack address pushl $0x202 # initialize eflags (Linux 2.2 = 0x292) popfl pushfl movl $(USER_CS | USER_PL), %eax pushl %eax pushl $PAGE_OFFSET - 0x1000 # go to init_trampoline() in user mode iret .align 4 .globl sighandler_trampoline; sighandler_trampoline: /* * This pushes twice the %eax register in order to save the 'signum' * value from being clobbered by functions like printf(), during the * signal handler calling. * FIXME: is this a bug in Newlib or in my side? */ pushl %eax # save 'signum' pushl %eax # 'signum' is the argument of the handler call *%ecx addl $4, %esp # discard the first push popl %ebx # restore 'signum' 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 #ifdef CONFIG_SYSCALL_6TH_ARG pushl %ebp # + 6th argument #endif /* CONFIG_SYSCALL_6TH_ARG */ pushl %edi # \ 5th argument pushl %esi # | 4th argument pushl %edx # | 3rd argument pushl %ecx # | 2nd argument pushl %ebx # / 1st argument pushl %eax # system call number call do_syscall #ifdef CONFIG_SYSCALL_6TH_ARG addl $28, %esp # suppress all 7 pushl from the stack #else addl $24, %esp # suppress all 6 pushl from the stack #endif /* CONFIG_SYSCALL_6TH_ARG */ movl %eax, EAX(%esp) # save the return value BOTTOM_HALVES CHECK_IF_SIGNALS CHECK_IF_NEED_SCHEDULE .globl return_from_syscall; return_from_syscall: RESTORE_ALL iret .align 4 .globl do_switch; do_switch: pusha pushfl movl %esp, %ebx addl $0x24, %ebx 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 popa ret .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 get_cpu_vendor_id; get_cpu_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 pushl %ebx mov $2, %eax cpuid movl %eax, _tlbinfo_eax # store cache information movl %ebx, _tlbinfo_ebx movl %edx, _tlbinfo_ecx movl %ecx, _tlbinfo_edx popl %ebx popl %ecx popl %edx ret .align 4 .globl inport_b; inport_b: pushl %ebp movl %esp, %ebp movw 0x08(%ebp), %dx # port addr xorl %eax, %eax # initialize %eax 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_l; inport_l: pushl %ebp movl %esp, %ebp movl 0x08(%ebp), %edx # port addr inl %dx, %eax 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 inport_sl; inport_sl: pushl %ebp movl %esp, %ebp pushl %edx pushl %edi pushl %ecx cld movl 0x8(%ebp), %edx # port addr movl 0xC(%ebp), %edi # dest movl 0x10(%ebp), %ecx # count rep insl 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_l; outport_l: pushl %ebp movl %esp, %ebp movl 0x8(%ebp), %edx # port addr movl 0xC(%ebp), %eax # data outl %eax, %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 outport_sl; outport_sl: pushl %ebp movl %esp, %ebp pushl %edx pushl %esi pushl %ecx cld movl 0x8(%ebp), %edx # port addr movl 0xC(%ebp), %esi # src movl 0x10(%ebp), %ecx # count rep outsl 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 ================================================ FILE: kernel/cpu.c ================================================ /* * fiwix/kernel/cpu.c * * Copyright 2018-2022, 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 + 1]; struct cpu cpu_table; 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 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 n, size; unsigned int mask; size = sprintk(buffer, "flags :"); for(n = 0, mask = 1; n < 32; n++, mask <<= 1) { if(_cpuflags & mask) { size += sprintk(buffer + size, " %s", cpu_flags[n]); } } size += sprintk(buffer + size, "\n"); return size; } void cpu_init(void) { unsigned int n; int maxcpuid; memset_b(&cpu_table, 0, 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 = get_cpu_vendor_id(); cpu_table.vendor_id = _vendorid; if(maxcpuid >= 1) { signature_flags(); cpu_table.family = _cputype; cpu_table.flags = _cpuflags; 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; } strcpy(UTS_MACHINE, "i386"); strncpy(sys_utsname.machine, UTS_MACHINE, _UTSNAME_LENGTH); cpu_table.has_fpu = getfpu(); } ================================================ FILE: kernel/gdt.c ================================================ /* * fiwix/kernel/gdt.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #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 i386tss), loflags, SD_OPSIZE32); load_gdt((unsigned int)&gdtr); } ================================================ FILE: kernel/idt.c ================================================ /* * fiwix/kernel/idt.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include struct gate_desc idt[NR_IDT_ENTRIES]; struct desc_r idtr = { sizeof(idt) - 1, (unsigned int)idt }; static void *except_handlers[] = { &except0, &except1, &except2, &except3, &except4, &except5, &except6, &except7, &except8, &except9, &except10, &except11, &except12, &except13, &except14, &except15, &except16, &except17, &except18, &except19, &except20, &except21, &except22, &except23, &except24, &except25, &except26, &except27, &except28, &except29, &except30, &except31 }; static void *irq_handlers[] = { &irq0, &irq1, &irq2, &irq3, &irq4, &irq5, &irq6, &irq7, &irq8, &irq9, &irq10, &irq11, &irq12, &irq13, &irq14, &irq15 }; 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, 0, sizeof(idt)); for(n = 0; n < NR_IDT_ENTRIES; n++) { if(n < 0x20) { set_idt_entry(n, (__off_t)except_handlers[n], SD_32TRAPGATE | SD_PRESENT); continue; } if(n < 0x30) { set_idt_entry(n, (__off_t)irq_handlers[n - 0x20], SD_32INTRGATE | SD_PRESENT); continue; } set_idt_entry(n, (__off_t)&unknown_irq, SD_32INTRGATE | SD_PRESENT); } set_idt_entry(0x80, (__off_t)&syscall, SD_32TRAPGATE | SD_DPL3 | SD_PRESENT); load_idt((unsigned int)&idtr); } ================================================ FILE: kernel/init.c ================================================ /* * fiwix/kernel/init.c * * Copyright 2018-2022, 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 INIT_TRAMPOLINE_SIZE 256 /* max. size of init_trampoline() */ char *init_args; char *init_argv[] = { INIT_PROGRAM, NULL, 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(PAGE_SIZE))) { goto init_init__die; } init->rss++; memcpy_b(pgdir, kpage_dir, PAGE_SIZE); init->tss.cr3 = V2P((unsigned int)pgdir); init->ppid = &proc_table[IDLE]; init->pgid = 0; init->sid = 0; init->flags = 0; init->children = 0; init->priority = DEF_PRIORITY; init->start_time = CURRENT_TICKS; init->sleep_address = NULL; init->uid = init->gid = 0; init->euid = init->egid = 0; init->suid = init->sgid = 0; memset_b(init->fd, 0, sizeof(init->fd)); memset_b(init->fd_flags, 0, sizeof(init->fd_flags)); init->root = current->root; init->pwd = current->pwd; strcpy(init->argv0, init_argv[0]); init_argv[1] = init_args; sprintk(init->pidstr, "%d", init->pid); init->sigpending = 0; init->sigblocked = 0; init->sigexecuting = 0; memset_b(init->sigaction, 0, sizeof(init->sigaction)); memset_b(&init->usage, 0, sizeof(struct rusage)); memset_b(&init->cusage, 0, 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_max = NR_PROCS; init->umask = 0022; /* setup the stack */ if(!(init->tss.esp0 = kmalloc(PAGE_SIZE))) { 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, PAGE_OFFSET - 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; runnable(init); nr_processes++; return; init_init__die: PANIC("unable to run init process.\n"); } ================================================ FILE: kernel/irq.c ================================================ /* * fiwix/kernel/irq.c * * Copyright 2021-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include struct interrupt *irq_table[NR_IRQS]; static struct bh *bh_table = NULL; int register_irq(int num, struct interrupt *new_irq) { struct interrupt **irq; if(num < 0 || num >= NR_IRQS) { printk("WARNING: %s(): interrupt %d is greater than NR_IRQS (%d)!\n", __FUNCTION__, num, NR_IRQS); return -EINVAL; } irq = &irq_table[num]; while(*irq) { if(*irq == new_irq) { printk("WARNING: %s(): interrupt %d already registered!\n", __FUNCTION__, num); return -EINVAL; } irq = &(*irq)->next; } *irq = new_irq; new_irq->ticks = 0; return 0; } int unregister_irq(int num, const struct interrupt *old_irq) { struct interrupt **irq, *prev_irq; if(num < 0 || num >= NR_IRQS) { return -EINVAL; } irq = &irq_table[num]; prev_irq = NULL; while(*irq) { if(*irq == old_irq) { if((*irq)->next) { printk("WARNING: %s(): cannot unregister interrupt %d.\n", __FUNCTION__, num); return -EINVAL; } *irq = NULL; if(prev_irq) { prev_irq->next = NULL; } break; } prev_irq = *irq; irq = &(*irq)->next; } return 0; } void add_bh(struct bh *new) { unsigned int flags; struct bh **b; SAVE_FLAGS(flags); CLI(); b = &bh_table; while(*b) { b = &(*b)->next; } *b = new; RESTORE_FLAGS(flags); } /* each ISR points to this function (interrupts are disabled) */ void irq_handler(int num, struct sigcontext sc) { struct interrupt *irq; disable_irq(num); irq = irq_table[num]; /* spurious interrupt treatment */ if(!irq) { spurious_interrupt(num); goto end; } ack_pic_irq(num); kstat.irqs++; irq->ticks++; do { irq->handler(num, &sc); irq = irq->next; } while(irq); end: enable_irq(num); } void unknown_irq_handler(void) { printk("Unknown IRQ received!\n"); return; } /* execute bottom halves (interrupts are enabled) */ void do_bh(struct sigcontext sc) { struct bh *b; void (*fn)(struct sigcontext *); b = bh_table; while(b) { if(b->flags & BH_ACTIVE) { b->flags &= ~BH_ACTIVE; fn = b->fn; (*fn)(&sc); } b = b->next; } } void irq_init(void) { memset_b(irq_table, 0, sizeof(irq_table)); } ================================================ FILE: kernel/kexec.c ================================================ /* * fiwix/kernel/kexec.c * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Copyright (C) 2019-2024 mintsuki and contributors, Limine project. * Copyright 2023-2024, Richard R. Masters. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_KEXEC int kexec_proto; int kexec_size; char kexec_cmdline[NAME_MAX + 1]; static void _memcpy_b(void *dest, const void *src, unsigned int count) { unsigned char *d, *s; s = (unsigned char *)src; d = (unsigned char *)dest; while(count--) { *d = *s; d++; s++; } } static void _memset_b(void *dest, unsigned char value, unsigned int count) { unsigned char *d; d = (unsigned char *)dest; while(count--) { *d = 0; d++; } } static void multiboot1_trampoline(unsigned int ramdisk_addr, unsigned int kernel_addr, int size, struct multiboot_info *info) { struct elf32_phdr *elf32_ph; struct elf32_hdr *elf32_h; unsigned int start, length, offset; unsigned int entry_addr; struct gate_desc idt[64]; struct desc_r idtr = { sizeof(idt) - 1, (unsigned int)&idt }; struct seg_desc gdt[3]; struct desc_r gdtr = { sizeof(gdt) - 1, (unsigned int)&gdt }; int n, cr0; CLI(); /* invalidate IDT */ _memset_b(&idt, 0, sizeof(idt)); __asm__ __volatile__ ( "lidt %0\n\t" : /* no output */ : "m"(idtr) ); /* configure a new flat GDT */ gdt[0].sd_lolimit = 0; gdt[0].sd_lobase = 0; gdt[0].sd_loflags = 0; gdt[0].sd_hilimit = 0; gdt[0].sd_hiflags = 0; gdt[0].sd_hibase = 0; gdt[1].sd_lolimit = 0xFFFF; gdt[1].sd_lobase = 0x00000000 & 0xFFFFFF; gdt[1].sd_loflags = SD_CODE | SD_CD | SD_DPL0 | SD_PRESENT; gdt[1].sd_hilimit = (0xFFFFFFFF >> 16) & 0x0F; gdt[1].sd_hiflags = SD_OPSIZE32 | SD_PAGE4KB; gdt[1].sd_hibase = (0x00000000 >> 24) & 0xFF; gdt[2].sd_lolimit = 0xFFFF; gdt[2].sd_lobase = 0x00000000 & 0xFFFFFF; gdt[2].sd_loflags = SD_DATA | SD_CD | SD_DPL0 | SD_PRESENT; gdt[2].sd_hilimit = (0xFFFFFFFF >> 16) & 0x0F; gdt[2].sd_hiflags = SD_OPSIZE32 | SD_PAGE4KB; gdt[2].sd_hibase = (0x00000000 >> 24) & 0xFF; __asm__ __volatile__ ( "lgdt %0\n\t" : /* no output */ : "m"(gdtr) ); /* disable paging and others */ cr0 = 0x11; /* preserve ET & PE */ __asm__ __volatile__( "mov %0, %%cr0" : /* no output */ : "r"(cr0) ); /* flush TLB (needed?) */ __asm__ __volatile__( "xorl %%eax, %%eax\n\t" "movl %%eax, %%cr3\n\t" : /* no output */ : /* no input */ : "eax" /* clobbered registers */ ); /* * Clear memory. This is intended to avoid unexpected results if the * new kernel guesses its uninitialized variables are zeroed. */ _memset_b(0x0, 0, KEXEC_BOOT_ADDR); _memset_b((void *)0x100000, 0, ramdisk_addr - 0x100000); /* install the kernel previously stored in RAMdisk by the user */ elf32_h = (struct elf32_hdr *)ramdisk_addr; entry_addr = elf32_h->e_entry; for(n = 0; n < elf32_h->e_phnum; n++) { elf32_ph = (struct elf32_phdr *)(ramdisk_addr + elf32_h->e_phoff + (sizeof(struct elf32_phdr) * n)); if(elf32_ph->p_type == PT_LOAD) { start = elf32_ph->p_paddr; length = elf32_ph->p_filesz; offset = elf32_ph->p_offset; _memcpy_b((void *)start, (unsigned char *)(ramdisk_addr + offset), length); } } /* flush TLB (needed?) */ __asm__ __volatile__( "xorl %%eax, %%eax\n\t" "movl %%eax, %%cr3\n\t" : /* no output */ : /* no input */ : "eax" /* clobbered registers */ ); /* load all the segment registers with the kernel data segment value */ __asm__ __volatile__( "movw $0x10, %%ax\n\t" "movw %%ax, %%ds\n\t" "movw %%ax, %%es\n\t" "movw %%ax, %%fs\n\t" "movw %%ax, %%gs\n\t" "movw %%ax, %%ss\n\t" : /* no output */ : /* no input */ : "eax" /* clobbered registers */ ); /* Multiboot 1 */ #ifdef __TINYC__ unsigned int multiboot_magic = MULTIBOOT_BOOTLOADER_MAGIC; __asm__ __volatile__( "movl %0, %%eax\n\t" "movl %1, %%ebx\n\t" : /* no output */ : "r"(multiboot_magic), "r"((unsigned int)info) ); #else __asm__ __volatile__( "movl %0, %%eax\n\t" "movl %1, %%ebx\n\t" : /* no output */ : "eax"(MULTIBOOT_BOOTLOADER_MAGIC), "ebx"((unsigned int)info) ); #endif /* * This jumps to the kernel entry address. * * i.e.: ljmp $0x08, $entry_addr */ __asm__ __volatile__( "pushw $0x08\n\t" "pushl %0\n\t" "ljmp *(%%esp)\n\t" : /* no output */ : "c"(entry_addr) ); /* not reached */ } void kexec_multiboot1(void) { unsigned int *esp, ramdisk_addr; struct proc *idle, *prev; struct multiboot_info *info; struct multiboot_mmap_entry *map, *map_orig; struct elf32_hdr *elf32_h; char *cmdline; char *boot_loader_name; int n, nmaps; CLI(); ramdisk_addr = (unsigned int)ramdisk_table[ramdisk_minors - 1].addr; elf32_h = (struct elf32_hdr *)ramdisk_addr; if(check_elf(elf32_h)) { printk("WARNING: %s(): unrecognized i386 binary ELF.\n", __FUNCTION__); return; } /* the IDLE process will do the job */ idle = &proc_table[IDLE]; idle->tss.eip = (unsigned int)KEXEC_BOOT_ADDR; map_kaddr((unsigned int *)P2V(current->tss.cr3), KEXEC_BOOT_ADDR, KEXEC_BOOT_ADDR + PAGE_SIZE, 0, PAGE_PRESENT | PAGE_RW); map_kaddr((unsigned int *)P2V(idle->tss.cr3), KEXEC_BOOT_ADDR, KEXEC_BOOT_ADDR + PAGE_SIZE, 0, PAGE_PRESENT | PAGE_RW); invalidate_tlb(); memcpy_b((void *)KEXEC_BOOT_ADDR, multiboot1_trampoline, PAGE_SIZE); /* stack starts at the end of the page */ esp = (unsigned int *)(KEXEC_BOOT_ADDR + PAGE_SIZE - 4); /* space reserved for the cmdline string (256 bytes) */ esp -= 256 / sizeof(unsigned int); cmdline = (char *)esp; sprintk(cmdline, "%s %s", UTS_SYSNAME, kexec_cmdline); /* space reserved for the boot_loader_name string (16 bytes) */ esp -= 16 / sizeof(unsigned int); boot_loader_name = (char *)esp; sprintk(boot_loader_name, "Fiwix kexec"); /* space reserved for the memory map structure */ nmaps = 0; while(bios_mem_map[nmaps].type) { nmaps++; } esp -= sizeof(struct multiboot_mmap_entry) * nmaps; map_orig = map = (struct multiboot_mmap_entry *)esp; /* setup the memory map */ for(n = 0; n < nmaps; n++) { map->size = sizeof(struct multiboot_mmap_entry) - sizeof(map->size); map->addr = bios_mem_map[n].from_hi; map->addr = map->addr << 32 | bios_mem_map[n].from; map->len = bios_mem_map[n].to_hi; map->len = map->len << 32 | bios_mem_map[n].to; map->len -= map->addr; map->type = bios_mem_map[n].type; map++; } /* space reserved for the multiboot_info structure */ esp -= sizeof(struct multiboot_info) / sizeof(unsigned int); memset_b(esp, 0, sizeof(struct multiboot_info)); info = (struct multiboot_info *)esp; /* setup Multiboot structure */ info->flags = MULTIBOOT_INFO_MEMORY; info->mem_lower = kparm_memsize; info->mem_upper = kparm_extmemsize; info->flags |= MULTIBOOT_INFO_CMDLINE; info->cmdline = (unsigned int)cmdline; info->flags |= MULTIBOOT_INFO_BOOT_LOADER_NAME; info->boot_loader_name = (unsigned int)boot_loader_name; info->flags |= MULTIBOOT_INFO_MEM_MAP; info->mmap_length = sizeof(struct multiboot_mmap_entry) * nmaps; info->mmap_addr = (unsigned int)map_orig; esp--; /* now put the four parameters into the stack */ *esp = (unsigned int)info; esp--; *esp = kexec_size * 1024; esp--; *esp = KERNEL_ADDR; esp--; *esp = V2P(ramdisk_addr); esp--; idle->tss.esp = (unsigned int)esp; printk("%s(): jumping to multiboot1_trampoline() ...\n", __FUNCTION__); prev = current; set_tss(idle); do_switch(&prev->tss.esp, &prev->tss.eip, idle->tss.esp, idle->tss.eip, idle->tss.cr3, TSS); /* not reached */ return; } static void linux_trampoline(char *kernel_src_addr, unsigned int kernel_size, char *initrd_src_addr, unsigned int initrd_size, char *initrd_dst_addr, struct boot_params *boot_params) { struct gate_desc idt[64]; struct desc_r idtr = { sizeof(idt) - 1, (unsigned int)&idt }; struct seg_desc gdt[4]; struct desc_r gdtr = { sizeof(gdt) - 1, (unsigned int)&gdt }; int cr0; CLI(); /* invalidate IDT */ char *dst, *src; unsigned int len; for (dst = (char *)&idt, len = sizeof(idt); len; len--) { *dst++ = 0; } __asm__ __volatile__ ( "lidt %0\n\t" : /* no output */ : "m"(idtr) ); /* configure a new flat GDT */ gdt[0].sd_lolimit = 0; gdt[0].sd_lobase = 0; gdt[0].sd_loflags = 0; gdt[0].sd_hilimit = 0; gdt[0].sd_hiflags = 0; gdt[0].sd_hibase = 0; gdt[1].sd_lolimit = 0; gdt[1].sd_lobase = 0; gdt[1].sd_loflags = 0; gdt[1].sd_hilimit = 0; gdt[1].sd_hiflags = 0; gdt[1].sd_hibase = 0; gdt[2].sd_lolimit = 0xFFFF; gdt[2].sd_lobase = 0x00000000 & 0xFFFFFF; gdt[2].sd_loflags = SD_CODE | SD_CD | SD_DPL0 | SD_PRESENT; gdt[2].sd_hilimit = (0xFFFFFFFF >> 16) & 0x0F; gdt[2].sd_hiflags = SD_OPSIZE32 | SD_PAGE4KB; gdt[2].sd_hibase = (0x00000000 >> 24) & 0xFF; gdt[3].sd_lolimit = 0xFFFF; gdt[3].sd_lobase = 0x00000000 & 0xFFFFFF; gdt[3].sd_loflags = SD_DATA | SD_CD | SD_DPL0 | SD_PRESENT; gdt[3].sd_hilimit = (0xFFFFFFFF >> 16) & 0x0F; gdt[3].sd_hiflags = SD_OPSIZE32 | SD_PAGE4KB; gdt[3].sd_hibase = (0x00000000 >> 24) & 0xFF; __asm__ __volatile__ ( "lgdt %0\n\t" : /* no output */ : "m"(gdtr) ); /* disable paging and others */ cr0 = 0x11; /* preserve ET & PE */ __asm__ __volatile__( "mov %0, %%cr0" : /* no output */ : "r"(cr0) ); /* flush TLB (needed?) */ __asm__ __volatile__( "xorl %%eax, %%eax\n\t" "movl %%eax, %%cr3\n\t" : /* no output */ : /* no input */ : "eax" /* clobbered registers */ ); /* Note that below we avoid using _memset_b and _memcpy_b because we cannot * make function calls because this trampoline has been relocated. */ /* * Clear memory. This is intended to avoid unexpected results if the * new kernel guesses its uninitialized variables are zeroed. */ for (dst = (char *)0, len = KEXEC_BOOT_ADDR; len; len--) { *dst++ = 0; } for (dst = (char *)0x100000, len = (unsigned int)kernel_src_addr - 0x100000; len; len--) { *dst++ = 0; } /* install the kernel previously stored in RAMdisk by the user */ for (dst = (char *)0x100000, src = kernel_src_addr, len = kernel_size; len; len--) { *dst++ = *src++; } /* install the ramdisk previously stored in RAMdisk by the user */ for (dst = (char *)initrd_dst_addr, src = initrd_src_addr, len = initrd_size; len; len--) { *dst++ = *src++; } /* flush TLB (needed?) */ __asm__ __volatile__( "xorl %%eax, %%eax\n\t" "movl %%eax, %%cr3\n\t" : /* no output */ : /* no input */ : "eax" /* clobbered registers */ ); /* load all the segment registers with the kernel data segment value */ __asm__ __volatile__( "movw $0x18, %%ax\n\t" "movw %%ax, %%ds\n\t" "movw %%ax, %%es\n\t" "movw %%ax, %%fs\n\t" "movw %%ax, %%gs\n\t" "movw %%ax, %%ss\n\t" : /* no output */ : /* no input */ : "eax" /* clobbered registers */ ); /* Linux boot */ #ifdef __TINYC__ __asm__ __volatile__( "movl %0, %%eax\n\t" "movl %%eax, %%esi\n\t" : /* no output */ : "r"((unsigned int)boot_params) ); #else __asm__ __volatile__( "movl %0, %%eax\n\t" "movl %%eax, %%esi\n\t" : /* no output */ : "eax"((unsigned int)boot_params) ); #endif /* * This jumps to the kernel entry address. * */ __asm__ __volatile__( "pushw $0x10\n\t" "pushl %0\n\t" "ljmp *(%%esp)\n\t" : /* no output */ : "c"(KERNEL_ADDR) ); /* not reached */ } void kexec_linux(void) { struct proc *idle, *prev; unsigned int *esp; char *kernel_src_addr, *initrd_src_addr; __u32 kernel_size, initrd_size; struct boot_params *boot_params; char *cmdline; kernel_size = *((__u32 *)ramdisk_table[ramdisk_minors - 1].addr); printk("kexec_linux: kernel file size: %u\n", kernel_size); initrd_size = *((__u32 *) (ramdisk_table[ramdisk_minors - 1].addr + sizeof(__u32))); /* space reserved for the memory map structure */ kernel_src_addr = ramdisk_table[ramdisk_minors - 1].addr + (sizeof(__u32) * 2); __u32 signature; memcpy_b(&signature, kernel_src_addr + 0x202, sizeof(__u32)); /* validate signature */ if (signature != 0x53726448) { printk("kexec_linux: Invalid kernel signature\n"); return; } else { printk("kexec_linux: Valid kernel signature\n"); } __size_t setup_code_size = 0; memcpy_b(&setup_code_size, kernel_src_addr + 0x1f1, 1); if (setup_code_size == 0) { setup_code_size = 4; } setup_code_size *= 512; __size_t real_mode_code_size = 512 + setup_code_size; /* the IDLE process will do the job */ idle = &proc_table[IDLE]; idle->tss.eip = (unsigned int)KEXEC_BOOT_ADDR; map_kaddr((unsigned int *)P2V(current->tss.cr3), KEXEC_BOOT_ADDR, KEXEC_BOOT_ADDR + (PAGE_SIZE * 2), 0, PAGE_PRESENT | PAGE_RW); map_kaddr((unsigned int *)P2V(idle->tss.cr3), KEXEC_BOOT_ADDR, KEXEC_BOOT_ADDR + (PAGE_SIZE * 2), 0, PAGE_PRESENT | PAGE_RW); invalidate_tlb(); memcpy_b((void *)KEXEC_BOOT_ADDR, linux_trampoline, PAGE_SIZE); /* stack starts at the end of the page */ esp = (unsigned int *)(KEXEC_BOOT_ADDR + (PAGE_SIZE * 2) - 4); /* space reserved for the cmdline string (256 bytes) */ esp -= 256 / sizeof(unsigned int); cmdline = (char *)esp; strcpy(cmdline, kexec_cmdline); /* space reserved for the boot_params structure */ esp -= sizeof(struct boot_params) / sizeof(unsigned int); memset_b(esp, 0, sizeof(struct boot_params)); boot_params = (struct boot_params *)esp; struct setup_header *setup_header = &boot_params->hdr; __size_t setup_header_end = ({ __u8 x; memcpy_b(&x, kernel_src_addr + 0x201, 1); 0x202 + x; }); memcpy_b(setup_header, kernel_src_addr + 0x1f1, setup_header_end - 0x1f1); printk("kexec_linux: Boot protocol: %u.%u\n", setup_header->version >> 8, setup_header->version & 0xff); if (setup_header->version < 0x203) { printk("kexec_linux: Protocols < 2.03 are not supported"); return; } setup_header->cmdline_addr = (__u32)(unsigned int *)cmdline; /* video_mode. 0xffff means "normal" */ setup_header->video_mode = 0xffff; /* 0xff means this loader does not have an assigned id */ setup_header->loader_type = 0xff; if (!(setup_header->load_flags & (1 << 0))) { printk("kexec_linux: Kernels that load at 0x10000 are not supported"); return; } setup_header->load_flags &= ~(1 << 5); /* print early messages */ /* Modules */ __u32 modules_mem_base = setup_header->initramfs_addr_max; if (modules_mem_base == 0) { modules_mem_base = 0x38000000; } initrd_src_addr = kernel_src_addr + kernel_size; modules_mem_base -= initrd_size; if (initrd_size) { setup_header->initramfs_addr = (__u32)modules_mem_base; } else { setup_header->initramfs_addr = 0; } setup_header->initramfs_size = (__u32)initrd_size; struct screen_info *screen_info = &boot_params->screen_info; screen_info->mode = 3; screen_info->ega_bx = 3; screen_info->lines = 25; screen_info->cols = 80; screen_info->points = 16; screen_info->is_vga = IS_VGA_VGA_COLOR; /* e820 bios memory entries */ struct bios_mem_entry *bios_mem_table = boot_params->bios_mem_table; __size_t i, j; for (i = 0, j = 0; i < NR_BIOS_MM_ENT; i++) { if(!bios_mem_map[i].type || bios_mem_map[i].type > 0x1000) { continue; } bios_mem_table[j].addr = bios_mem_map[i].from_hi; bios_mem_table[j].addr = (bios_mem_table[j].addr << 32) | bios_mem_map[i].from; bios_mem_table[j].size = bios_mem_map[i].to_hi; bios_mem_table[j].size = (bios_mem_table[j].size << 32) | bios_mem_map[i].to; bios_mem_table[j].size -= bios_mem_table[j].addr; bios_mem_table[j].type = bios_mem_map[i].type; j++; boot_params->num_bios_mem_entries = j; } /* now put the six parameters into the stack */ esp--; *esp = (unsigned int)boot_params; esp--; *esp = modules_mem_base; esp--; *esp = initrd_size; esp--; *esp = (unsigned int)V2P(initrd_src_addr); esp--; *esp = (kernel_size - real_mode_code_size); esp--; *esp = (unsigned int)V2P(kernel_src_addr + real_mode_code_size); esp--; idle->tss.esp = (unsigned int)esp; printk("kexec_linux: boot_params: %x\n", boot_params); printk("kexec_linux: modules_mem_base: %x\n", modules_mem_base); printk("kexec_linux: initrd_size: %u\n", initrd_size); printk("kexec_linux: initrd_src_addr: %x\n", initrd_src_addr); printk("kexec_linux: initrd_src_addr: %x\n", V2P(initrd_src_addr)); printk("kexec_linux: kernel_size: %u\n", kernel_size - real_mode_code_size); printk("kexec_linux: kernel_src_addr: %x\n", kernel_src_addr + real_mode_code_size); printk("kexec_linux: kernel_src_addr: %x\n", V2P(kernel_src_addr + real_mode_code_size)); printk("kexec_linux: jumping to linux_trampoline() ...\n"); prev = current; set_tss(idle); do_switch(&prev->tss.esp, &prev->tss.eip, idle->tss.esp, idle->tss.eip, idle->tss.cr3, TSS); /* not reached */ return; } #endif /* CONFIG_KEXEC */ ================================================ FILE: kernel/main.c ================================================ /* * fiwix/kernel/main.c * * Copyright 2018-2023, 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 #include #include #include #include #include struct kernel_params kparms; struct kernel_stat kstat; unsigned int _last_data_addr; struct new_utsname sys_utsname = { UTS_SYSNAME, UTS_NODENAME, UTS_RELEASE, UTS_VERSION, "", UTS_DOMAINNAME, }; static void set_default_values(void) { /* filesystem is ext2 */ if(!kparms.rootfstype[0]) { strcpy(kparms.rootfstype, "ext2"); } /* console is /dev/tty0 */ if(!kparms.syscondev) { kparms.syscondev = MKDEV(VCONSOLES_MAJOR, 0); add_sysconsoledev(kparms.syscondev); } } void start_kernel(unsigned int magic, unsigned int info, unsigned int last_boot_addr) { struct proc *init; _last_data_addr = last_boot_addr - PAGE_OFFSET; memset_b(&kstat, 0, sizeof(kstat)); sysconsole_init(); #ifdef CONFIG_QEMU_DEBUGCON if(inport_b(QEMU_DEBUG_PORT) == QEMU_DEBUG_PORT) { kstat.flags |= KF_HAS_DEBUGCON; } #endif /* CONFIG_QEMU_DEBUGCON */ printk(" Fiwix v%s for i386 architecture\n", UTS_RELEASE); printk(" Copyright (c) 2018-2025, Jordi Sanfeliu\n"); printk("\n"); #ifdef __TINYC__ printk(" (built on %s with tcc)\n", UTS_VERSION); #else printk(" (built on %s with GCC %s)\n", UTS_VERSION, __VERSION__); #endif printk("\n"); printk("DEVICE ADDRESS IRQ COMMENT\n"); printk("--------------------------------------------------------------------------------\n"); cpu_init(); multiboot(magic, info); set_default_values(); pic_init(); irq_init(); idt_init(); dev_init(); tty_init(); mem_init(); #ifdef CONFIG_PCI pci_init(); #endif /* CONFIG_PCI */ video_init(); console_init(); timer_init(); ps2_init(); proc_init(); sleep_init(); buffer_init(); sched_init(); inode_init(); fd_init(); /* * IDLE is now the current process (created manually as PID 0), * it won't be placed in the running queue. */ current = get_proc_free(); proc_slot_init(current); set_tss(current); load_tr(TSS); current->tss.cr3 = V2P((unsigned int)kpage_dir); current->flags |= PF_KPROC; sprintk(current->argv0, "%s", "idle"); /* PID 1 is for the INIT process */ init = get_proc_free(); proc_slot_init(init); init->pid = get_unused_pid(); kernel_process("kswapd", kswapd); /* PID 2 */ kernel_process("kbdflushd", kbdflushd); /* PID 3 */ /* kswapd will take over the rest of the kernel initialization */ need_resched = 1; STI(); /* let's rock! */ cpu_idle(); } void stop_kernel(void) { struct proc *p, *next; int n; /* put all processes to sleep and reset all pending signals */ FOR_EACH_PROCESS_RUNNING(p) { next = p->next_run; not_runnable(p, PROC_SLEEPING); p->sigpending = 0; p = next; } #ifdef CONFIG_KEXEC if(!(kstat.flags & KF_HAS_PANICKED)) { if(kexec_size > 0) { switch(kexec_proto) { case KEXEC_MULTIBOOT1: kexec_multiboot1(); break; case KEXEC_LINUX: kexec_linux(); break; } } } #endif /* CONFIG_KEXEC */ printk("\n"); printk("** Safe to Power Off **\n"); printk(" -or-\n"); printk("** Press Any Key to Reboot **\n"); any_key_to_reboot = 1; /* stop and disable all interrupts! */ CLI(); for(n = 0; n < NR_IRQS; n++) { disable_irq(n); } /* enable keyboard only */ enable_irq(KEYBOARD_IRQ); STI(); cpu_idle(); } void cpu_idle(void) { for(;;) { if(need_resched) { do_sched(); } HLT(); } } ================================================ FILE: kernel/multiboot.c ================================================ /* * fiwix/kernel/multiboot.c * * Copyright 2021-2022, 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 char bios_data[256]; static struct kernel_params_value kparamval_table[] = { #ifdef CONFIG_BGA { "bga=", { "640x480x32", "800x600x32", "1024x768x32" }, { 0 } }, #endif /* CONFIG_BGA */ { "console=", { "/dev/tty0", "/dev/tty1", "/dev/tty2", "/dev/tty3", "/dev/tty4", "/dev/tty5", "/dev/tty6", "/dev/tty7", "/dev/tty8", "/dev/tty9", "/dev/tty10", "/dev/tty11", "/dev/tty12", "/dev/ttyS0", "/dev/ttyS1", "/dev/ttyS2", "/dev/ttyS3" }, { 0x400, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, 0x409, 0x40A, 0x40B, 0x40C, 0x440, 0x441, 0x442, 0x443 } }, { "ide_nodma", { 0 }, { 0 } }, { "initrd=", { 0 }, { 0 } }, #ifdef CONFIG_KEXEC { "kexec_proto=", { "multiboot1", "linux" }, { 1, 2 } }, { "kexec_size=", { 0 }, { 0 } }, { "kexec_cmdline=", { 0 }, { 0 } }, #endif /* CONFIG_KEXEC */ { "ps2_noreset", { 0 }, { 0 } }, { "ramdisksize=", { 0 }, { 0 } }, { "ro", { 0 }, { 0 } }, { "root=", { "/dev/ram0", "/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", }, { 0x100, 0x200, 0x201, 0x300, 0x301, 0x302, 0x303, 0x304, 0x340, 0x341, 0x342, 0x343, 0x344, 0x1600, 0x1601, 0x1602, 0x1603, 0x1604, 0x1640, 0x1641, 0x1642, 0x1643, 0x1644, } }, { "rootfstype=", { "minix", "ext2", "iso9660" }, { 0 } }, { NULL } }; char kernel_cmdline[NAME_MAX + 1]; Elf32_Shdr *symtab, *strtab; /* check the validity of a command line parameter */ static int check_param(struct kernel_params_value *kpv, const char *value) { int n; #ifdef CONFIG_PCI #ifdef CONFIG_BGA if(!strcmp(kpv->name, "bga=")) { for(n = 0; kpv->value[n]; n++) { if(!strcmp(kpv->value[n], value)) { strncpy(kparms.bgaresolution, value, 14); kparms.bgaresolution[14] = '\0'; return 0; } } return 1; } #endif /* CONFIG_BGA */ #endif /* CONFIG_PCI */ if(!strcmp(kpv->name, "console=")) { for(n = 0; kpv->value[n]; n++) { if(!strcmp(kpv->value[n], value)) { if(kpv->sysval[n]) { if(add_sysconsoledev(kpv->sysval[n])) { kparms.syscondev = kpv->sysval[n]; } else { printk("WARNING: console device '%s' exceeds NR_SYSCONSOLES.\n", value); } return 0; } printk("WARNING: device name for '%s' is not defined!\n", kpv->name); } } return 1; } if(!strcmp(kpv->name, "ide_nodma")) { kparms.flags |= KPARMS_IDE_NODMA; return 0; } if(!strcmp(kpv->name, "initrd=")) { if(value[0]) { strncpy(kparms.initrd, value, DEVNAME_MAX); return 0; } } #ifdef CONFIG_KEXEC if(!strcmp(kpv->name, "kexec_proto=")) { for(n = 0; kpv->value[n]; n++) { if(!strcmp(kpv->value[n], value)) { if(kpv->sysval[n]) { kexec_proto = kpv->sysval[n]; return 0; } printk("WARNING: kexec protocol '%s' is not defined!\n", kpv->name); } } return 1; } if(!strcmp(kpv->name, "kexec_size=")) { if(value[0]) { kexec_size = atoi(value); return 0; } return 1; } if(!strcmp(kpv->name, "kexec_cmdline=")) { if(value[0]) { /* copy the provided cmdline and also remove quotes */ strncpy(kexec_cmdline, value + 1, MIN(strlen(value) - 2, NAME_MAX)); return 0; } return 1; } #endif /* CONFIG_KEXEC */ if(!strcmp(kpv->name, "ps2_noreset")) { kparms.flags |= KPARMS_PS2_NORESET; return 0; } if(!strcmp(kpv->name, "ramdisksize=")) { kparms.ramdisksize = atoi(value); ramdisk_minors = RAMDISK_DRIVES; return 0; } if(!strcmp(kpv->name, "ro")) { kparms.ro = 1; return 0; } if(!strcmp(kpv->name, "root=")) { for(n = 0; kpv->value[n]; n++) { if(!strcmp(kpv->value[n], value)) { kparms.rootdev = kpv->sysval[n]; strncpy(kparms.rootdevname, value, DEVNAME_MAX); return 0; } } return 1; } if(!strcmp(kpv->name, "rootfstype=")) { for(n = 0; kpv->value[n]; n++) { if(!strcmp(kpv->value[n], value)) { strncpy(kparms.rootfstype, value, sizeof(kparms.rootfstype)); return 0; } } return 1; } printk("WARNING: the parameter '%s' looks valid but it's not defined!\n", kpv->name); return 0; } static int parse_arg(const char *arg) { int n; const char *value; /* '--' marks the beginning of the init arguments */ if(!strcmp(arg, "--")) { return 1; } /* find out where the value starts (if it exists) */ value = arg; while(*value && *value != '=') { value++; } if(*value) { value++; } for(n = 0; kparamval_table[n].name; n++) { if(!strncmp(arg, kparamval_table[n].name, value ? value - arg : strlen(arg))) { if(check_param(&kparamval_table[n], value)) { printk("WARNING: invalid value '%s' in the '%s' parameter.\n", value, kparamval_table[n].name); } return 0; } } printk("WARNING: invalid cmdline parameter: '%s'.\n", arg); return 0; } static char *parse_cmdline(const char *str) { char *from, *to; char arg[CMDL_ARG_LEN]; char c; int open, close, incomplete; from = to = (char *)str; open = close = 0; for(;;) { c = *(str++); if(c == '"') { if(open) { close = 1; } open = 1; } incomplete = open - close; if((c == ' ' || !c) && !incomplete) { if(to - from < CMDL_ARG_LEN) { memcpy_b(arg, from, to - from); arg[to - from] = 0; if(arg[0] != 0) { if(parse_arg(arg)) { while(*(from++)) { if(*from != '-' && *from != ' ') { break; } } return from; } } } else { memcpy_b(arg, from, CMDL_ARG_LEN); arg[CMDL_ARG_LEN - 1] = 0; printk("WARNING: invalid length of the cmdline parameter '%s'.\n", arg); } from = ++to; if(!c) { break; } continue; } to++; } return NULL; } #ifdef CONFIG_BGA static void parse_bgaresolution(void) { char str[5], *p; int n; n = 0; for(;;) { p = &str[0]; while(kparms.bgaresolution[n] && kparms.bgaresolution[n] != 'x') { *p = kparms.bgaresolution[n]; p++; n++; } *p = '\0'; if(!video.fb_width) { video.fb_width = atoi(str); } else if(!video.fb_height) { video.fb_height = atoi(str); } else if(!video.fb_bpp) { video.fb_bpp = atoi(str); } else { break; } n++; } } #endif /* CONFIG_BGA */ /* * This function returns the last address used by kernel symbols or the value * of 'mod_end' (in the module structure) of the last module loaded by GRUB. * * In the case where there are no ELF header tables, then it returns the last * .bss address plus one page. * * This is intended to place the kernel stack beyond all these addresses. */ unsigned int get_last_boot_addr(unsigned int magic, unsigned int info) { struct multiboot_info *mbi; Elf32_Shdr *shdr; struct multiboot_elf_section_header_table *hdr; struct multiboot_mod_list *mod; unsigned short int n; unsigned int addr; if(magic != MULTIBOOT_BOOTLOADER_MAGIC) { addr = ((unsigned int)_end & PAGE_MASK) + PAGE_SIZE; return P2V(addr); } mbi = (struct multiboot_info *)info; /* ELF header tables */ if(mbi->flags & MULTIBOOT_INFO_ELF_SHDR) { symtab = strtab = NULL; hdr = &(mbi->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; } } addr = strtab->sh_addr + strtab->sh_size; } else { addr = ((unsigned int)_end & PAGE_MASK) + PAGE_SIZE; } /* * https://www.gnu.org/software/grub/manual/multiboot/multiboot.html * * Check if GRUB has loaded some modules and, if so, get the last * address used by the last one. */ if(mbi->flags & MULTIBOOT_INFO_MODS) { mod = (struct multiboot_mod_list *)mbi->mods_addr; for(n = 0; n < mbi->mods_count; n++, mod++) { addr = mod->mod_end; } } return P2V(addr); } void multiboot(unsigned int magic, unsigned int info) { struct multiboot_info mbi; memset_b(&video, 0, sizeof(struct video_parms)); memset_b(&ramdisk_table, 0, sizeof(ramdisk_table)); ramdisk_minors = 0; /* save the BIOS Data Area (BDA) */ memcpy_b(bios_data, (unsigned char *)(PAGE_OFFSET + 0x400), 256); if(magic != MULTIBOOT_BOOTLOADER_MAGIC) { printk("WARNING: invalid multiboot magic number: 0x%x. Assuming 4MB of RAM.\n", magic); memset_b(&mbi, 0, sizeof(struct multiboot_info)); kparms.memsize = 640; kparms.extmemsize = 3072; bios_map_init(NULL, 0); video.columns = 80; video.lines = 25; video.flags = VPF_VGA; video.memsize = 384 * 1024; return; } memcpy_b(&mbi, (void *)info, sizeof(struct multiboot_info)); if(mbi.flags & MULTIBOOT_INFO_BOOT_LOADER_NAME) { printk("bootloader -\t%s\n", mbi.boot_loader_name); } if(!(mbi.flags & MULTIBOOT_INFO_MEMORY)) { printk("WARNING: values in mem_lower and mem_upper are not valid!\n"); } kparms.memsize = (unsigned int)mbi.mem_lower; kparms.extmemsize = (unsigned int)mbi.mem_upper; if(mbi.flags & MULTIBOOT_INFO_CMDLINE) { 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(kernel_cmdline, p); init_args = parse_cmdline(kernel_cmdline); } else { printk("WARNING: no cmdline detected!\n"); } printk("kernel 0x%08x -\tcmdline='%s'\n", KERNEL_ADDR, kernel_cmdline); if(mbi.flags & MULTIBOOT_INFO_MODS) { unsigned int n, size; struct multiboot_mod_list *mod; mod = (struct multiboot_mod_list *)mbi.mods_addr; for(n = 0; n < mbi.mods_count; n++, mod++) { if(!strcmp((char *)mod->cmdline, kparms.initrd)) { printk("initrd 0x%08x-0x%08x file='%s' size=%dKB\n", mod->mod_start, mod->mod_end, mod->cmdline, (mod->mod_end - mod->mod_start) / 1024); size = mod->mod_end - mod->mod_start; ramdisk_table[0].addr = (char *)mod->mod_start; ramdisk_table[0].size = size / 1024; ramdisk_minors++; } } } if(!(mbi.flags & MULTIBOOT_INFO_ELF_SHDR)) { printk("WARNING: ELF section header table is not valid!\n"); } if(mbi.flags & MULTIBOOT_INFO_MEM_MAP) { bios_map_init((struct multiboot_mmap_entry *)mbi.mmap_addr, mbi.mmap_length); } else { bios_map_init(NULL, 0); } if(mbi.flags & MULTIBOOT_INFO_VBE_INFO) { struct vbe_controller *vbec; struct vbe_mode *vbem; vbec = (struct vbe_controller *)mbi.vbe_control_info; vbem = (struct vbe_mode *)mbi.vbe_mode_info; video.flags = VPF_VESAFB; video.address = (unsigned int *)vbem->phys_base; video.port = 0; video.memsize = vbec->total_memory * vbem->win_size * 1024; strcpy((char *)video.signature, (char *)vbec->signature); video.columns = vbem->x_resolution / vbem->x_char_size; video.lines = vbem->y_resolution / vbem->y_char_size; video.fb_version = vbec->version; video.fb_width = vbem->x_resolution; video.fb_height = vbem->y_resolution; video.fb_char_width = vbem->x_char_size; video.fb_char_height = vbem->y_char_size; video.fb_bpp = vbem->bits_per_pixel; video.fb_pixelwidth = vbem->bits_per_pixel / 8; video.fb_pitch = vbem->bytes_per_scanline; video.fb_linesize = video.fb_pitch * video.fb_char_height; video.fb_size = vbem->x_resolution * vbem->y_resolution * video.fb_pixelwidth; video.fb_vsize = video.lines * video.fb_pitch * video.fb_char_height; } #ifdef CONFIG_BGA if(*kparms.bgaresolution) { video.flags = VPF_VESAFB; parse_bgaresolution(); video.fb_char_width = 8; video.fb_char_height = 16; video.columns = video.fb_width / video.fb_char_width; video.lines = video.fb_height / video.fb_char_height; } #endif /* CONFIG_BGA */ if(!video.flags) { /* fallback to standard VGA */ video.flags = VPF_VGA; video.columns = 80; video.lines = 25; video.memsize = 384 * 1024; } } ================================================ FILE: kernel/pic.c ================================================ /* * fiwix/kernel/pic.c * * Copyright 2018-2021, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include /* interrupt vector base addresses */ #define IRQ0_ADDR 0x20 #define IRQ8_ADDR 0x28 /* * 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 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)); } void spurious_interrupt(int irq) { int real; /* spurious interrupt treatment */ 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); } if(kstat.sirqs < MAX_SPU_NOTICES) { printk("WARNING: spurious interrupt detected (unregistered IRQ %d).\n", irq); } else if(kstat.sirqs == MAX_SPU_NOTICES) { printk("WARNING: too many spurious interrupts; not logging any more.\n"); } kstat.sirqs++; return; } ack_pic_irq(irq); } void ack_pic_irq(int irq) { if(irq > 7) { outport_b(PIC_SLAVE, EOI); } outport_b(PIC_MASTER, EOI); } void pic_init(void) { /* 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); } ================================================ FILE: kernel/pit.c ================================================ /* * 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)); } int pit_getcounter0(void) { int count; /* latch counter 0 and read its value */ outport_b(MODEREG, SEL_CHAN0); count = inport_b(CHANNEL0); count |= inport_b(CHANNEL0) << 8; return count; } 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 */ } ================================================ FILE: kernel/process.c ================================================ /* * fiwix/kernel/process.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #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 = { 0, 0 }; static struct resource pid_resource = { 0, 0 }; int nr_processes = 0; __pid_t lastpid = 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; FOR_EACH_PROCESS(p) { if(p->ppid == parent && p->state == PROC_ZOMBIE) { return p; } p = p->next; } return NULL; } __pid_t remove_zombie(struct proc *p) { struct proc *pp; __pid_t pid; pid = p->pid; kfree(p->tss.esp0); p->rss--; kfree(P2V(p->tss.cr3)); p->rss--; pp = p->ppid; release_proc(p); if(pp) { pp->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; int retval; retval = 0; lock_resource(&slot_resource); FOR_EACH_PROCESS(p) { if(p->pgid == pgid) { if(p->state != PROC_ZOMBIE) { pp = p->ppid; /* return if only one is found that breaks the rule */ if(pp->pgid != pgid || pp->sid == p->sid) { break; } } } p = p->next; } unlock_resource(&slot_resource); return retval; } 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, 0, 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) { /* * Make sure the kernel never reuses active pid, pgid * or sid values. */ if(lastpid == p->pid || lastpid == p->pgid || lastpid == p->sid) { goto loop; } p = p->next; } unlock_resource(&pid_resource); return lastpid; } struct proc *get_proc_by_pid(__pid_t pid) { struct proc *p; FOR_EACH_PROCESS(p) { if(p->pid == pid) { return p; } p = p->next; } return NULL; } struct proc *kernel_process(const char *name, int (*fn)(void)) { struct proc *p; p = get_proc_free(); proc_slot_init(p); p->pid = get_unused_pid(); p->ppid = &proc_table[IDLE]; p->flags |= PF_KPROC; p->priority = DEF_PRIORITY; if(!(p->tss.esp0 = kmalloc(PAGE_SIZE))) { release_proc(p); return NULL; } p->entry_address = PAGE_OFFSET; p->end_code = (int)_end; p->tss.esp0 += PAGE_SIZE - 4; p->rss++; p->tss.cr3 = V2P((unsigned int)kpage_dir); p->tss.eip = (unsigned int)fn; p->tss.esp = p->tss.esp0; sprintk(p->pidstr, "%d", p->pid); sprintk(p->argv0, "%s", name); runnable(p); return p; } void proc_slot_init(struct proc *p) { /* 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->prev_sleep = p->next_sleep = NULL; p->prev_run = p->next_run = NULL; unlock_resource(&slot_resource); memset_b(&p->tss, 0, sizeof(struct i386tss) - IO_BITMAP_SIZE); p->tss.io_bitmap_addr = offsetof(struct i386tss, io_bitmap); /* I/O permissions are not inherited by the child */ memset_l(&p->tss.io_bitmap, ~0, IO_BITMAP_SIZE / sizeof(unsigned int)); p->tss.io_bitmap[IO_BITMAP_SIZE] = ~0; /* extra byte must be all 1's */ p->state = PROC_IDLE; } void proc_init(void) { int n; struct proc *p; memset_b(proc_table, 0, 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; } ================================================ FILE: kernel/sched.c ================================================ /* * fiwix/kernel/sched.c * * Copyright 2018-2022, 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; CLI(); 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); STI(); } void set_tss(struct proc *p) { struct seg_desc *g; g = &gdt[TSS / sizeof(struct seg_desc)]; g->sd_lobase = (unsigned int)&p->tss; g->sd_loflags = SD_TSSPRESENT; g->sd_hibase = (char)(((unsigned int)&p->tss) >> 24); } /* Round Robin algorithm */ void do_sched(void) { int count; struct proc *p, *selected; /* let the current running process consume its time slice */ if(current->state == PROC_RUNNING && current->cpu_count > 0) { return; } need_resched = 0; for(;;) { count = -1; selected = &proc_table[IDLE]; FOR_EACH_PROCESS_RUNNING(p) { if(p->cpu_count > count) { count = p->cpu_count; selected = p; } p = p->next_run; } if(count) { break; } /* reassigns new quantum to all running processes */ FOR_EACH_PROCESS_RUNNING(p) { p->cpu_count = p->priority; p = p->next_run; } } if(current != selected) { context_switch(selected); } } void sched_init(void) { get_system_time(); /* this should be more unpredictable */ kstat.random_seed = CURRENT_TIME; } ================================================ FILE: kernel/signal.c ================================================ /* * fiwix/kernel/signal.c * * Copyright 2018-2022, 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 /* can process 'current' send a signal to process 'p'? */ int can_signal(struct proc *p) { if(!IS_SUPERUSER && current->euid != p->euid && current->sid != p->sid) { return 0; } return 1; } int send_sig(struct proc *p, __sigset_t signum) { if(signum > NSIG || !p) { return -EINVAL; } /* 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) { runnable(p); need_resched = 1; } /* 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; } /* * Force some signals, that a process cannot ignore, by changing its * signal disposition to SIG_DFL. */ switch(signum) { case SIGFPE: case SIGSEGV: if(p->sigaction[signum - 1].sa_handler == SIG_IGN) { p->sigaction[signum - 1].sa_handler = SIG_DFL; } break; } if(p->sigaction[signum - 1].sa_handler == SIG_DFL) { /* * INIT process is special, it only gets signals that have the * signal handler installed. This avoids to bring down the * system accidentally. */ if(p->pid == INIT) { return 0; } /* these signals with SIG_DFL are ignored */ switch(signum) { case SIGCONT: case SIGWINCH: case SIGCHLD: case SIGURG: return 0; } } if(p->sigaction[signum - 1].sa_handler == SIG_IGN) { /* if SIGCHLD is ignored reap its children (prevent zombies) */ if(signum == SIGCHLD) { while(sys_waitpid(-1, NULL, WNOHANG) > 0) { continue; } } return 0; } p->sigpending |= 1 << (signum - 1); p->usage.ru_nsignals++; /* wake up the process only if the 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; struct proc *p; 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) { /* * page_not_present() may have raised a SIGSEGV if it * detected that this process doesn't have an stack * region in vma_table. * * So, this check is needed to make sure to terminate the * process now, otherwise the kernel would panic when using * 'sc->oldesp' during the 'memcpy_b' below. */ if(!find_vma_region(sc->oldesp)) { printk("WARNING: %s(): no stack region in vma table for process %d. Terminated.\n", __FUNCTION__, current->pid); do_exit(signum); } 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: runnable(current); need_resched = 1; break; case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU: current->exit_code = signum; not_runnable(current, PROC_STOPPED); if(!(current->sigaction[signum - 1].sa_flags & SA_NOCLDSTOP)) { p = current->ppid; send_sig(p, SIGCHLD); /* needed for job control */ wakeup(&sys_wait4); } 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' */ } } } int kill_pid(__pid_t pid, __sigset_t signum, int sender) { struct proc *p; FOR_EACH_PROCESS(p) { if(p->pid == pid && p->state != PROC_ZOMBIE) { if(sender == USER) { if(!can_signal(p)) { return -EPERM; } } return send_sig(p, signum); } p = p->next; } return -ESRCH; } int kill_pgrp(__pid_t pgid, __sigset_t signum, int sender) { struct proc *p; int found; found = 0; FOR_EACH_PROCESS(p) { if(p->pgid == pgid && p->state != PROC_ZOMBIE) { if(sender == USER) { if(!can_signal(p)) { p = p->next; continue; } } send_sig(p, signum); found = 1; } p = p->next; } if(!found) { return -ESRCH; } return 0; } ================================================ FILE: kernel/sleep.c ================================================ /* * fiwix/kernel/sleep.c * * Copyright 2018-2022, 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 ? (NR_PROCS * 10) / 100 : 1) #define SLEEP_HASH(addr) ((addr) % (NR_BUCKETS)) struct proc *sleep_hash_table[NR_BUCKETS]; struct proc *proc_run_head; static unsigned int area = 0; void runnable(struct proc *p) { unsigned int flags; if(p->state == PROC_RUNNING) { printk("WARNING: %s(): process with pid '%d' is already running!\n", __FUNCTION__, p->pid); return; } SAVE_FLAGS(flags); CLI(); if(proc_run_head) { p->next_run = proc_run_head; proc_run_head->prev_run = p; } proc_run_head = p; p->state = PROC_RUNNING; RESTORE_FLAGS(flags); } void not_runnable(struct proc *p, int state) { unsigned int flags; SAVE_FLAGS(flags); CLI(); if(p->next_run) { p->next_run->prev_run = p->prev_run; } if(p->prev_run) { p->prev_run->next_run = p->next_run; } if(p == proc_run_head) { proc_run_head = p->next_run; } p->prev_run = p->next_run = NULL; p->state = state; RESTORE_FLAGS(flags); } int sleep(void *address, int state) { unsigned int flags; struct proc **h; int signum, i; SAVE_FLAGS(flags); CLI(); /* return if it has signals */ if(state == PROC_INTERRUPTIBLE) { if((signum = issig())) { RESTORE_FLAGS(flags); return signum; } } if(current->state == PROC_SLEEPING) { printk("WARNING: %s(): process with pid '%d' is already sleeping!\n", __FUNCTION__, current->pid); RESTORE_FLAGS(flags); return 0; } i = SLEEP_HASH((unsigned int)address); h = &sleep_hash_table[i]; /* insert process in the head */ if(!*h) { *h = current; (*h)->prev_sleep = (*h)->next_sleep = NULL; } else { current->prev_sleep = NULL; current->next_sleep = *h; (*h)->prev_sleep = current; *h = current; } current->sleep_address = address; if(state == PROC_UNINTERRUPTIBLE) { current->flags |= PF_NOTINTERRUPT; } not_runnable(current, PROC_SLEEPING); do_sched(); signum = 0; if(state == PROC_INTERRUPTIBLE) { signum = issig(); } RESTORE_FLAGS(flags); return signum; } void wakeup(void *address) { unsigned int flags; struct proc **h; int i, found; SAVE_FLAGS(flags); CLI(); i = SLEEP_HASH((unsigned int)address); h = &sleep_hash_table[i]; found = 0; while(*h) { if((*h)->sleep_address == address) { (*h)->sleep_address = NULL; (*h)->flags &= ~PF_NOTINTERRUPT; (*h)->cpu_count = (*h)->priority; runnable(*h); found = 1; if((*h)->next_sleep) { (*h)->next_sleep->prev_sleep = (*h)->prev_sleep; } if((*h)->prev_sleep) { (*h)->prev_sleep->next_sleep = (*h)->next_sleep; } if(h == &sleep_hash_table[i]) { /* if it's the head */ *h = (*h)->next_sleep; } continue; } h = &(*h)->next_sleep; } RESTORE_FLAGS(flags); if(found) { need_resched = 1; } } void wakeup_proc(struct proc *p) { unsigned int flags; struct proc **h; int i; if(p->state != PROC_SLEEPING && p->state != PROC_STOPPED) { return; } /* return if the process is not interruptible */ if(p->flags & PF_NOTINTERRUPT) { return; } SAVE_FLAGS(flags); CLI(); /* stopped processes don't have sleep address */ if(p->sleep_address) { if(p->next_sleep) { p->next_sleep->prev_sleep = p->prev_sleep; } if(p->prev_sleep) { p->prev_sleep->next_sleep = p->next_sleep; } i = SLEEP_HASH((unsigned int)p->sleep_address); h = &sleep_hash_table[i]; if(*h == p) { /* if it's the head */ *h = (*h)->next_sleep; } } p->sleep_address = NULL; p->cpu_count = p->priority; runnable(p); need_resched = 1; RESTORE_FLAGS(flags); } void lock_resource(struct resource *resource) { unsigned 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 int flags; SAVE_FLAGS(flags); CLI(); resource->locked = 0; if(resource->wanted) { resource->wanted = 0; wakeup(resource); } RESTORE_FLAGS(flags); } int can_lock_area(unsigned int type) { unsigned int flags; int retval; SAVE_FLAGS(flags); CLI(); retval = area & type; area |= type; RESTORE_FLAGS(flags); return !retval; } int unlock_area(unsigned int type) { unsigned int flags; int retval; SAVE_FLAGS(flags); CLI(); retval = area & type; area &= ~type; RESTORE_FLAGS(flags); return retval; } void sleep_init(void) { proc_run_head = NULL; memset_b(sleep_hash_table, 0, sizeof(sleep_hash_table)); } ================================================ FILE: kernel/syscalls/Makefile ================================================ # fiwix/kernel/syscalls/Makefile # # Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. # Distributed under the terms of the Fiwix License. # .c.o: $(CC) $(CFLAGS) -c -o $@ $< SRC = $(wildcard *.c) OBJS = $(patsubst %.c,%.o,$(SRC)) all: $(OBJS) clean: rm -f *.o ================================================ FILE: kernel/syscalls/access.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/alarm.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/brk.c ================================================ /* * fiwix/kernel/syscalls/brk.c * * Copyright 2018-2022, 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; #ifdef __DEBUG__ printk("0x%08x\n", current->brk); #endif /*__DEBUG__ */ return current->brk; } if(!expand_heap(newbrk)) { current->brk = brk; } else { return -ENOMEM; } #ifdef __DEBUG__ printk("0x%08x\n", current->brk); #endif /*__DEBUG__ */ return current->brk; } ================================================ FILE: kernel/syscalls/chdir.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/chmod.c ================================================ /* * 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->state |= INODE_DIRTY; iput(i); free_name(tmp_name); return 0; } ================================================ FILE: kernel/syscalls/chown.c ================================================ /* * fiwix/kernel/syscalls/chown.c * * Copyright 2018-2021, 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->state |= INODE_DIRTY; iput(i); free_name(tmp_name); return 0; } ================================================ FILE: kernel/syscalls/chown32.c ================================================ /* * fiwix/kernel/syscalls/chown32.c * * Copyright 2018-2023, Jordi Sanfeliu. All rights reserved. * Copyright 2018-2023, Richard R. Masters. 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_chown32(const char *filename, unsigned int owner, unsigned int group) { struct inode *i; char *tmp_name; int errno; #ifdef __DEBUG__ printk("(pid %d) sys_chown32('%s', %u, %u)\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 == (unsigned int)-1) { owner = i->i_uid; } else { i->i_mode &= ~(S_ISUID); i->i_ctime = CURRENT_TIME; } if(group == (unsigned int)-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->state |= INODE_DIRTY; iput(i); free_name(tmp_name); return 0; } ================================================ FILE: kernel/syscalls/chroot.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/close.c ================================================ /* * fiwix/kernel/syscalls/close.c * * Copyright 2018, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #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; } ================================================ FILE: kernel/syscalls/creat.c ================================================ /* * 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); } ================================================ FILE: kernel/syscalls/dup.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/dup2.c ================================================ /* * fiwix/kernel/syscalls/dup2.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #ifdef __DEBUG__ #include #endif /*__DEBUG__ */ int sys_dup2(unsigned int old_ufd, unsigned int new_ufd) { int errno; #ifdef __DEBUG__ printk("(pid %d) sys_dup2(%d, %d)", current->pid, old_ufd, new_ufd); #endif /*__DEBUG__ */ CHECK_UFD(old_ufd); if(new_ufd > OPEN_MAX) { return -EINVAL; } if(old_ufd == new_ufd) { return new_ufd; } if(current->fd[new_ufd]) { sys_close(new_ufd); } if((errno = get_new_user_fd(new_ufd)) < 0) { return errno; } new_ufd = errno; 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; } ================================================ FILE: kernel/syscalls/execve.c ================================================ /* * fiwix/kernel/syscalls/execve.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #ifdef __DEBUG__ #include #endif /*__DEBUG__ */ static int initialize_barg(struct binargs *barg, char *argv[], char *envp[]) { int n, errno; 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 *)))) { 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 *)))) { return errno; } barg->envp_len += strlen(envp[n]) + 1; } barg->envc = n; 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 add_strings(struct binargs *barg, char *filename, char *interpreter, char *args) { int n, p, offset; unsigned int ae_str_len; char *page; /* * For a script we need to substitute the saved argv[0] by the original * 'filename' supplied in execve(), otherwise the interpreter won't be * able to find the script file. */ 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 - 1)); /* mod PAGE_SIZE */ if(offset == PAGE_SIZE) { offset = 0; p++; } page = (char *)barg->page[p]; while(*(page + offset)) { offset++; barg->argv_len--; if(offset == PAGE_SIZE) { p++; offset = 0; page = (char *)barg->page[p]; } } barg->argv_len--; p = ARG_MAX - 1; barg->argv_len += strlen(interpreter) + 1; barg->argv_len += strlen(args) ? strlen(args) + 1 : 0; barg->argv_len += strlen(filename) + 1; barg->argc++; if(*args) { barg->argc++; } ae_str_len = barg->argv_len + barg->envp_len + 4; p -= ae_str_len / PAGE_SIZE; offset = PAGE_SIZE - (ae_str_len & (PAGE_SIZE - 1)); /* mod PAGE_SIZE */ if(offset == PAGE_SIZE) { offset = 0; p++; } barg->offset = offset; for(n = p; n < ARG_MAX; n++) { if(!barg->page[n]) { if(!(barg->page[n] = kmalloc(PAGE_SIZE))) { free_barg_pages(barg); return -ENOMEM; } } } /* interpreter */ page = (char *)barg->page[p]; while(*interpreter) { *(page + offset) = *interpreter; offset++; interpreter++; if(offset == PAGE_SIZE) { p++; offset = 0; page = (char *)barg->page[p]; } } *(page + offset++) = 0; if(offset == PAGE_SIZE) { p++; offset = 0; } /* args */ page = (char *)barg->page[p]; if(*args) { while(*args) { *(page + offset) = *args; offset++; args++; if(offset == PAGE_SIZE) { p++; offset = 0; page = (char *)barg->page[p]; } } *(page + offset++) = 0; if(offset == PAGE_SIZE) { p++; offset = 0; } } /* original script ('filename' with path) at argv[0] */ page = (char *)barg->page[p]; while(*filename) { *(page + offset) = *filename; offset++; filename++; if(offset == PAGE_SIZE) { p++; offset = 0; page = (char *)barg->page[p]; } } *(page + offset) = 0; return 0; } static int copy_strings(struct binargs *barg, char *argv[], char *envp[]) { int n, p, offset; unsigned int ae_ptr_len, ae_str_len; char *page, *str; p = ARG_MAX - 1; ae_ptr_len = (1 + (barg->argc + 1) + (barg->envc + 1)) * sizeof(unsigned int); /* the last 4 bytes of the stack pages are not used */ ae_str_len = barg->argv_len + barg->envp_len + 4; if (ae_ptr_len + ae_str_len > (ARG_MAX * PAGE_SIZE)) { return -E2BIG; } p -= ae_str_len / PAGE_SIZE; offset = PAGE_SIZE - (ae_str_len & (PAGE_SIZE - 1)); /* mod PAGE_SIZE */ if(offset == PAGE_SIZE) { offset = 0; p++; } barg->offset = offset; for(n = p; n < ARG_MAX; n++) { if(!(barg->page[n] = kmalloc(PAGE_SIZE))) { 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++) = 0; 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++) = 0; if(offset == PAGE_SIZE) { p++; offset = 0; } } return 0; } static int do_execve(const char *filename, char *argv[], char *envp[], struct sigcontext *sc) { char interpreter[NAME_MAX + 1], args[NAME_MAX + 1], name[NAME_MAX + 1]; __blk_t block; struct buffer *buf; struct inode *i; struct binargs barg; char *data; int errno; if((errno = initialize_barg(&barg, &(*argv), &(*envp))) < 0) { return errno; } /* save 'argv' and 'envp' into the kernel address space */ if((errno = copy_strings(&barg, &(*argv), &(*envp)))) { return errno; } if(!(data = (void *)kmalloc(PAGE_SIZE))) { return -ENOMEM; } strcpy(name, filename); loop: if((errno = namei(name, &i, NULL, FOLLOW_LINKS))) { free_barg_pages(&barg); kfree((unsigned int)data); return errno; } if(!S_ISREG(i->i_mode)) { iput(i); free_barg_pages(&barg); kfree((unsigned int)data); return -EACCES; } if(check_permission(TO_EXEC, i) < 0) { iput(i); free_barg_pages(&barg); kfree((unsigned int)data); return -EACCES; } if((block = bmap(i, 0, FOR_READING)) < 0) { iput(i); free_barg_pages(&barg); kfree((unsigned int)data); return block; } if(!(buf = bread(i->dev, block, i->sb->s_blocksize))) { iput(i); free_barg_pages(&barg); kfree((unsigned int)data); 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. */ memcpy_b(data, buf->data, i->sb->s_blocksize); brelse(buf); errno = elf_load(i, &barg, sc, data); if(errno == -ENOEXEC) { /* OK, looks like it was not an ELF binary; let's see if it is a script */ memset_b(interpreter, 0, NAME_MAX + 1); memset_b(args, 0, NAME_MAX + 1); errno = script_load(interpreter, args, data); if(!errno) { /* yes, it is! */ iput(i); if((errno = add_strings(&barg, name, interpreter, args))) { free_barg_pages(&barg); kfree((unsigned int)data); return errno; } strcpy(name, interpreter); goto loop; } } if(!errno) { if(i->i_mode & S_ISUID) { current->euid = i->i_uid; } if(i->i_mode & S_ISGID) { current->egid = i->i_gid; } } iput(i); free_barg_pages(&barg); kfree((unsigned int)data); return errno; } #ifdef CONFIG_SYSCALL_6TH_ARG int sys_execve(const char *filename, char *argv[], char *envp[], int arg4, int arg5, int arg6, struct sigcontext *sc) #else int sys_execve(const char *filename, char *argv[], char *envp[], int arg4, int arg5, struct sigcontext *sc) #endif /* CONFIG_SYSCALL_6TH_ARG */ { char *tmp_name; int n, errno; #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 = do_execve(tmp_name, &(*argv), &(*envp), sc))) { free_name(tmp_name); return errno; } strncpy(current->argv0, tmp_name, NAME_MAX); 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; free_name(tmp_name); return 0; } ================================================ FILE: kernel/syscalls/exit.c ================================================ /* * fiwix/kernel/syscalls/exit.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_SYSVIPC #include #endif /* CONFIG_SYSVIPC */ 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->pid); printk("------------------------------\n"); #endif /*__DEBUG__ */ #ifdef CONFIG_SYSVIPC if(current->semundo) { semexit(); } #endif /* CONFIG_SYSVIPC */ release_binary(); current->argv = NULL; current->envp = NULL; init = &proc_table[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->ppid == current) { p->ppid = init; init->children++; current->children--; if(p->state == PROC_ZOMBIE) { send_sig(init, SIGCHLD); if(init->sleep_address == &sys_wait4) { wakeup_proc(init); } } } p = p->next; } 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"); sync_superblocks(0); /* in all devices */ sync_inodes(0); /* in all devices */ sync_buffers(0); /* in all devices */ stop_kernel(); } /* notify the parent about the child's death */ p = current->ppid; send_sig(p, SIGCHLD); if(p->sleep_address == &sys_wait4) { wakeup_proc(p); } 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; } not_runnable(current, 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; } ================================================ FILE: kernel/syscalls/fchdir.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/fchmod.c ================================================ /* * fiwix/kernel/syscalls/fchmod.c * * Copyright 2018-2022, 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(unsigned 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->state |= INODE_DIRTY; return 0; } ================================================ FILE: kernel/syscalls/fchown.c ================================================ /* * fiwix/kernel/syscalls/fchown.c * * Copyright 2018-2022, 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(unsigned 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->state |= INODE_DIRTY; return 0; } ================================================ FILE: kernel/syscalls/fcntl.c ================================================ /* * fiwix/kernel/syscalls/fcntl.c * * Copyright 2018-2022, 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(unsigned int ufd, int cmd, unsigned 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: case F_DUPFD_CLOEXEC: 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]; if (cmd == F_DUPFD_CLOEXEC) { current->fd_flags[new_ufd] |= FD_CLOEXEC; } 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; } ================================================ FILE: kernel/syscalls/fcntl64.c ================================================ /* * fiwix/kernel/syscalls/fcntl64.c * * Copyright 2018-2023, Jordi Sanfeliu. All rights reserved. * Copyright 2023, Richard R. Masters. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include int sys_fcntl64(unsigned int ufd, int cmd, unsigned int arg) { int new_ufd; #ifdef __DEBUG__ printk("(pid %d) sys_fcntl64(%d, %d, 0x%08x)\n", current->pid, ufd, cmd, arg); #endif /*__DEBUG__ */ CHECK_UFD(ufd); switch(cmd) { case F_DUPFD_CLOEXEC: case F_DUPFD: 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]; if (cmd == F_DUPFD_CLOEXEC) { current->fd_flags[new_ufd] |= FD_CLOEXEC; } 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_GETLK64: case F_SETLK64: case F_SETLKW64: printk("(pid %d) sys_fcntl64: WARNING: locks not implemented!\n", current->pid); return 0; default: return -EINVAL; } return 0; } ================================================ FILE: kernel/syscalls/fdatasync.c ================================================ /* * 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); } ================================================ FILE: kernel/syscalls/flock.c ================================================ /* * fiwix/kernel/syscalls/flock.c * * Copyright 2018-2022, 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(unsigned 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); } ================================================ FILE: kernel/syscalls/fork.c ================================================ /* * fiwix/kernel/syscalls/fork.c * * Copyright 2018-2022, 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 void free_vma_table(struct proc *p) { struct vma *vma, *tmp; vma = p->vma_table; while(vma) { tmp = vma; vma = vma->next; kfree((unsigned int)tmp); } } #ifdef CONFIG_SYSCALL_6TH_ARG int sys_fork(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, struct sigcontext *sc) #else int sys_fork(int arg1, int arg2, int arg3, int arg4, int arg5, struct sigcontext *sc) #endif /* CONFIG_SYSCALL_6TH_ARG */ { int count, pages; unsigned int n; unsigned int *child_pgdir; struct sigcontext *stack; struct proc *child, *p; struct vma *vma, *child_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->uid == current->uid) { count++; } p = p->next; } 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; sprintk(child->pidstr, "%d", child->pid); if(!(child_pgdir = (void *)kmalloc(PAGE_SIZE))) { 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; child->flags = 0; child->children = 0; child->cpu_count = (current->cpu_count >>= 1); child->start_time = CURRENT_TICKS; child->sleep_address = NULL; vma = current->vma_table; child->vma_table = NULL; while(vma) { if(!(child_vma = (struct vma *)kmalloc(sizeof(struct vma)))) { kfree((unsigned int)child_pgdir); free_vma_table(child); release_proc(child); return -ENOMEM; } *child_vma = *vma; child_vma->prev = child_vma->next = NULL; if(child_vma->inode) { child_vma->inode->count++; } if(!child->vma_table) { child->vma_table = child_vma; } else { child_vma->prev = child->vma_table->prev; child->vma_table->prev->next = child_vma; } child->vma_table->prev = child_vma; vma = vma->next; } child->sigpending = 0; child->sigexecuting = 0; memset_b(&child->sc, 0, sizeof(struct sigcontext)); memset_b(&child->usage, 0, sizeof(struct rusage)); memset_b(&child->cusage, 0, 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; #ifdef CONFIG_SYSVIPC current->semundo = NULL; #endif /* CONFIG_SYSVIPC */ if(!(child->tss.esp0 = kmalloc(PAGE_SIZE))) { kfree((unsigned int)child_pgdir); free_vma_table(child); release_proc(child); return -ENOMEM; } if(!(pages = clone_pages(child))) { printk("WARNING: %s(): not enough memory when cloning pages.\n", __FUNCTION__); free_page_tables(child); kfree((unsigned int)child_pgdir); free_vma_table(child); 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 */ /* 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++; runnable(child); return child->pid; /* parent returns child's PID */ } ================================================ FILE: kernel/syscalls/fstat.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/fstat64.c ================================================ /* * fiwix/kernel/syscalls/fstat64.c * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Copyright 2023, Richard R. Masters. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #ifdef __DEBUG__ #include #include #endif /*__DEBUG__ */ int sys_fstat64(unsigned int ufd, struct stat64 *statbuf) { struct inode *i; int errno; #ifdef __DEBUG__ printk("(pid %d) sys_fstat64(%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 stat64)))) { 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; } ================================================ FILE: kernel/syscalls/fstatfs.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/fsync.c ================================================ /* * fiwix/kernel/syscalls/fsync.c * * Copyright 2018-2022, 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(unsigned 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; } ================================================ FILE: kernel/syscalls/ftime.c ================================================ /* * 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 = ((CURRENT_TICKS % HZ) * 1000000) / HZ; /* FIXME: 'timezone' and 'dstflag' fields are not used */ return 0; } ================================================ FILE: kernel/syscalls/ftruncate.c ================================================ /* * fiwix/kernel/syscalls/ftruncate.c * * Copyright 2018-2022, 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(unsigned 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; } if(i->fsop && i->fsop->truncate) { inode_lock(i); errno = i->fsop->truncate(i, length); inode_unlock(i); return errno; } return -EINVAL; } ================================================ FILE: kernel/syscalls/ftruncate64.c ================================================ /* * fiwix/kernel/syscalls/ftruncate64.c * * Copyright 2018-2023, Jordi Sanfeliu. All rights reserved. * Copyright 2023, Richard R. Masters. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #ifdef __DEBUG__ #include #include #endif /*__DEBUG__ */ int sys_ftruncate64(unsigned int ufd, __loff_t length) { struct inode *i; int errno; #ifdef __DEBUG__ printk("(pid %d) sys_ftruncate64(%d, %llu)\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((__off_t)length == i->i_size) { return 0; } errno = 0; if(i->fsop && i->fsop->truncate) { inode_lock(i); errno = i->fsop->truncate(i, (__off_t)length); inode_unlock(i); } return errno; } ================================================ FILE: kernel/syscalls/getcwd.c ================================================ /* * fiwix/kernel/syscalls/getcwd.c * * Copyright 2018, Jordi Sanfeliu. All rights reserved. * Copyright 2022, Alwin Berger. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #ifdef __DEBUG__ #include #include #endif /*__DEBUG__ */ int sys_getcwd(char *buf, __size_t size) { int errno; struct dirent *dirent_buf; struct dirent *d_ptr; struct inode *cur; struct inode *up; struct inode *tmp_ino; int tmp_fd, done; int namelength, bytes_read; int diff_dev; __size_t marker; __size_t x; #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; } /* buffer self-allocation not supported */ if(size == 0 || buf == NULL) { return -EINVAL; } /* the shortest possible path is "/" */ if(size < 2) { return -ERANGE; } cur = current->pwd; up = cur; marker = size - 2; /* reserve '\0' at the end */ buf[size - 1] = 0; if(cur == current->root) { /* this case needs special handling, otherwise the loop skips over root */ buf[0] = '/'; buf[1] = '\0'; return 2; } if(!(dirent_buf = (void *)kmalloc(PAGE_SIZE))) { return -ENOMEM; } do { if((errno = parse_namei("..", cur, &up, 0, FOLLOW_LINKS))) { if(cur != current->pwd) { iput(cur); } kfree((unsigned int)dirent_buf); return errno; } if((tmp_fd = get_new_fd(up)) < 0) { iput(up); if(cur != current->pwd) { iput(cur); } kfree((unsigned int)dirent_buf); return tmp_fd; } do { done = 0; bytes_read = up->fsop->readdir(up, &fd_table[tmp_fd], dirent_buf, PAGE_SIZE); if(bytes_read < 0) { release_fd(tmp_fd); iput(up); if(cur != current->pwd) { iput(cur); } kfree((unsigned int)dirent_buf); return bytes_read; } d_ptr = dirent_buf; while((void *) d_ptr < ((void *) dirent_buf + bytes_read)) { /* * If the child is on the same device as the parent the d_ptr->d_ino should be correct. * In this case there is no need to call a namei again. */ diff_dev = up->dev != cur->dev; if(diff_dev) { if(parse_namei(d_ptr->d_name, up, &tmp_ino, 0, FOLLOW_LINKS)) { /* keep going if sibling dirents fail */ break; } } if((!diff_dev && d_ptr->d_ino == cur->inode) || (diff_dev && tmp_ino->inode == cur->inode)) { if(strcmp("..", d_ptr->d_name)) { namelength = strlen(d_ptr->d_name); if(marker < namelength + 1) { release_fd(tmp_fd); iput(up); if(cur != current->pwd) { iput(cur); } if(diff_dev) { iput(tmp_ino); } kfree((unsigned int)dirent_buf); return -ERANGE; } while(--namelength >= 0) { buf[marker--] = d_ptr->d_name[namelength]; } buf[marker--] = '/'; if(diff_dev) { iput(tmp_ino); } done = 1; break; } } if(diff_dev) { iput(tmp_ino); } d_ptr = (struct dirent *) ((void *)d_ptr + d_ptr->d_reclen); } } while(bytes_read != 0 && !done); release_fd(tmp_fd); if(!done) { /* parent dir was fully read, child still not found */ iput(up); if(cur != current->pwd) { iput(cur); } kfree((unsigned int)dirent_buf); return -ENOENT; } if(cur != current->pwd) { iput(cur); } cur = up; } while(cur != current->root); kfree((unsigned int)dirent_buf); iput(cur); /* move the string to the start of the buffer */ for(x = ++marker; x < size; x++) { buf[x - marker] = buf[x]; } /* linux returns the length of the string, so do we */ return size - marker; } ================================================ FILE: kernel/syscalls/getdents.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/getdents64.c ================================================ /* * fiwix/kernel/syscalls/getdents64.c * * Copyright 2018-2023, Jordi Sanfeliu. All rights reserved. * Copyright 2023, Richard R. Masters. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #ifdef __DEBUG__ #include #endif /*__DEBUG__ */ int sys_getdents64(unsigned int ufd, struct dirent64 *dirent, unsigned int count) { struct inode *i; int errno; #ifdef __DEBUG__ printk("(pid %d) sys_getdents64(%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 dirent64)))) { return errno; } i = fd_table[current->fd[ufd]].inode; if(!S_ISDIR(i->i_mode)) { return -ENOTDIR; } if(i->fsop && i->fsop->readdir64) { errno = i->fsop->readdir64(i, &fd_table[current->fd[ufd]], dirent, count); #ifdef __DEBUG__ printk(" -> returning %d\n", errno); #endif /*__DEBUG__ */ return errno; } return -EINVAL; } ================================================ FILE: kernel/syscalls/getegid.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/geteuid.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/getgid.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/getgroups.c ================================================ /* * 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 size is 0, sys_getgroups() shall return the number of group IDs * that it would otherwise return without modifying the array pointed * to by list. */ if(!size) { for(n = 0; n < NGROUPS_MAX; n++) { if(current->groups[n] == -1) { break; } } return n; } 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; } ================================================ FILE: kernel/syscalls/getitimer.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/getpgid.c ================================================ /* * fiwix/kernel/syscalls/getpgid.c * * Copyright 2018-2021, 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->pid == pid) { return p->pgid; } p = p->next; } return -ESRCH; } ================================================ FILE: kernel/syscalls/getpgrp.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/getpid.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/getppid.c ================================================ /* * 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->pid); #endif /*__DEBUG__ */ return current->ppid->pid; } ================================================ FILE: kernel/syscalls/getrlimit.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/getrusage.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/getsid.c ================================================ /* * fiwix/kernel/syscalls/getsid.c * * Copyright 2018-2021, 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->pid == pid) { return p->sid; } p = p->next; } return -ESRCH; } ================================================ FILE: kernel/syscalls/gettimeofday.c ================================================ /* * 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 = ((CURRENT_TICKS % HZ) * 1000000) / HZ; tv->tv_usec += gettimeoffset(); } 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; } ================================================ FILE: kernel/syscalls/getuid.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/ioctl.c ================================================ /* * 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 ufd, int cmd, unsigned int arg) { int errno; struct inode *i; #ifdef __DEBUG__ printk("(pid %d) sys_ioctl(%d, 0x%x, 0x%08x) -> ", current->pid, ufd, cmd, arg); #endif /*__DEBUG__ */ CHECK_UFD(ufd); i = fd_table[current->fd[ufd]].inode; if(i->fsop && i->fsop->ioctl) { errno = i->fsop->ioctl(i, &fd_table[current->fd[ufd]], cmd, arg); #ifdef __DEBUG__ printk("%d\n", errno); #endif /*__DEBUG__ */ return errno; } #ifdef __DEBUG__ printk("%d\n", -ENOTTY); #endif /*__DEBUG__ */ return -ENOTTY; } ================================================ FILE: kernel/syscalls/ioperm.c ================================================ /* * fiwix/kernel/syscalls/ioperm.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #ifdef __DEBUG__ #include #endif /*__DEBUG__ */ /* * This sys_ioperm() implementation, unlike what Linux 2.0 API said, covers all * the I/O address space. That is, up to 65536 I/O ports can be specified using * this system call, which makes sys_iopl() not needed anymore. */ int sys_ioperm(unsigned int from, unsigned int num, int turn_on) { unsigned int n; #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; } if(from + num >= (IO_BITMAP_SIZE * 8)) { return -EINVAL; } /* * The Linux 2.0 API states that if 'turn_on' is non-zero the access to * port must be guaranteed, otherwise the access must be denied. * * The Intel specification works in the opposite way. That is, if bit * is zero the access to port is guaranteed, otherwise is not. * * That's the reason why we need to negate the value of 'turn_on' before * changing the I/O permission bitmap. */ turn_on = !turn_on; for(n = from; n < (from + num); n++) { if(!turn_on) { current->tss.io_bitmap[n / 8] &= ~(1 << (n % 8)); } else { current->tss.io_bitmap[n / 8] |= ~(1 << (n % 8)); } } return 0; } ================================================ FILE: kernel/syscalls/iopl.c ================================================ /* * fiwix/kernel/syscalls/iopl.c * * Copyright 2018-2022, 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 when the port is denied by the I/O bitmap * in TSS. Otherwise the processor would check the I/O permission bitmap to * determine if access to a specific I/O port is allowed. * * This system call is dangerous and must not be used because it permits the * process to execute the Assembly instruction 'cli', which would freeze the * kernel. */ #include #include #include #include #ifdef __DEBUG__ #include #endif /*__DEBUG__ */ #ifdef CONFIG_SYSCALL_6TH_ARG int sys_iopl(int level, int arg2, int arg3, int arg4, int arg5, int arg6, struct sigcontext *sc) #else int sys_iopl(int level, int arg2, int arg3, int arg4, int arg5, struct sigcontext *sc) #endif /* CONFIG_SYSCALL_6TH_ARG */ { #ifdef __DEBUG__ printk("(pid %d) sys_iopl(%d) -> ", current->pid, level); #endif /*__DEBUG__ */ if(level > USER_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; } ================================================ FILE: kernel/syscalls/ipc.c ================================================ /* * fiwix/kernel/syscalls/ipc.c * * Copyright 2022, 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 #endif /*__DEBUG__ */ #ifdef CONFIG_SYSVIPC struct resource ipcmsg_resource = { 0, 0 }; void ipc_init(void) { sem_init(); msg_init(); shm_init(); } int ipc_has_perms(struct ipc_perm *perm, int mode) { if(IS_SUPERUSER) { return 1; } if(current->euid != perm->uid || current->euid != perm->cuid) { mode >>= 3; if(current->egid != perm->gid || current->egid != perm->cgid) { mode >>= 3; } } /* * The user may specify zero for the second argument in xxxget() to * bypass this check, and be able to obtain an identifier for an IPC * object even when the user don't has read or write access to that * IPC object. Later specific IPC calls will return error if the * program attempted an operation requiring read or write permission * on the IPC object. */ if(!mode) { return 1; } if(perm->mode & mode) { return 1; } return 0; } /* * This implementation follows basically the same semantics as in Linux 2.0 * ABI. That is, the sys_ipc() system call acts as a common entry point for * the System V IPC calls for semaphores, message queues and shared memory. * * The only exception is that Fiwix, by default, uses a structure that holds * all arguments needed by all System V IPC functions. This, of course, breaks * compatibility with the Linux 2.0 ABI, unless you rebuild the program under * FiwixOS. For those that are unable to rebuild an old program (no source code * available, etc.), you must rebuild the Fiwix kernel with the configuration * option CONFIG_SYSCALL_6TH_ARG enabled. This will enable full Linux 2.0 ABI * compatibility and the program will run successfully. */ #ifdef CONFIG_SYSCALL_6TH_ARG /* * This option adds more Linux 2.0 ABI compatibility to support the original * sys_ipc() system call, which requires up to 6 arguments. * * Fiwix by default uses a maximum of 5 arguments per system call, so any * binary built on FiwixOS will use a different implementation of the * sys_ipc(), and also any other system call that requires more than 5 * arguments. This breaks compatibility with Linux 2.0 ABI. * * In order to include the 6th argument, Fiwix will use the register 'ebp' * which might lead to some problems. Use this with caution. * * This configuration option should only be used if you need to execute a * Linux 2.0 binary and, for some reason, you cannot rebuild it on FiwixOS. */ int sys_ipc(unsigned int call, int first, int second, int third, void *ptr, int fifth) { struct sysvipc_args orig_args, *args; struct ipc_kludge { struct msgbuf *msgp; int msgtyp; } tmp; int version, errno; union semun *arg; #ifdef __DEBUG__ printk("(pid %d) sys_ipc(%d, %d, %d, %d, 0x%08x, %d)\n", current->pid, call, first, second, third, (int)ptr, fifth); #endif /*__DEBUG__ */ orig_args.arg1 = first; orig_args.arg2 = second; orig_args.arg3 = third; orig_args.ptr = ptr; orig_args.arg5 = fifth; switch(call) { case SEMCTL: if(!ptr) { return -EINVAL; } if((errno = check_user_area(VERIFY_READ, ptr, sizeof(tmp)))) { return errno; } arg = ptr; orig_args.ptr = arg->array; break; case MSGRCV: version = call >> 16; switch(version) { case 0: if(!ptr) { return -EINVAL; } if((errno = check_user_area(VERIFY_READ, ptr, sizeof(tmp)))) { return errno; } memcpy_b(&tmp, (struct ipc_kludge *)ptr, sizeof(tmp)); orig_args.arg3 = tmp.msgtyp; orig_args.ptr = tmp.msgp; orig_args.arg5 = third; break; default: orig_args.arg3 = fifth; orig_args.arg5 = third; break; } break; case SHMAT: version = call >> 16; switch(version) { case 0: if((errno = check_user_area(VERIFY_WRITE, (unsigned int *)third, sizeof(unsigned int)))) { return errno; } orig_args.arg3 = (int)&third; break; default: return -EINVAL; } break; } args = &orig_args; #else int sys_ipc(unsigned int call, struct sysvipc_args *args) { int errno; #ifdef __DEBUG__ printk("(pid %d) sys_ipc(%d, 0x%08x)\n", current->pid, call, (int)args); #endif /*__DEBUG__ */ if((errno = check_user_area(VERIFY_READ, args, sizeof(struct sysvipc_args)))) { return errno; } #endif /* CONFIG_SYSCALL_6TH_ARG */ switch(call) { case SEMOP: return sys_semop(args->arg1, args->ptr, args->arg2); case SEMGET: return sys_semget(args->arg1, args->arg2, args->arg3); case SEMCTL: return sys_semctl(args->arg1, args->arg2, args->arg3, args->ptr); case MSGSND: return sys_msgsnd(args->arg1, args->ptr, args->arg2, args->arg3); case MSGRCV: return sys_msgrcv(args->arg1, args->ptr, args->arg2, args->arg3, args->arg5); case MSGGET: return sys_msgget((key_t)args->arg1, args->arg2); case MSGCTL: return sys_msgctl(args->arg1, args->arg2, args->ptr); case SHMAT: if((errno = sys_shmat(args->arg1, args->ptr, args->arg2, (unsigned int *)&args->arg3))) { return errno; } #ifdef CONFIG_SYSCALL_6TH_ARG memcpy_l((unsigned int *)third, &args->arg3, 1); return 0; #else return args->arg3; #endif /* CONFIG_SYSCALL_6TH_ARG */ case SHMDT: return sys_shmdt(args->ptr); case SHMGET: return sys_shmget(args->arg1, args->arg2, args->arg3); case SHMCTL: return sys_shmctl(args->arg1, args->arg2, args->ptr); } return -EINVAL; } #endif /* CONFIG_SYSVIPC */ ================================================ FILE: kernel/syscalls/kill.c ================================================ /* * fiwix/kernel/syscalls/kill.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #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 > NSIG) { return -EINVAL; } if(pid == -1) { count = 0; FOR_EACH_PROCESS(p) { if(p->pid == INIT || p == current || !can_signal(p)) { p = p->next; continue; } count++; send_sig(p, signum); p = p->next; } return count ? 0 : -ESRCH; } if(!pid) { return kill_pgrp(current->pgid, signum, USER); } if(pid < 1) { return kill_pgrp(-pid, signum, USER); } return kill_pid(pid, signum, USER); } ================================================ FILE: kernel/syscalls/lchown.c ================================================ /* * fiwix/kernel/syscalls/lchown.c * * Copyright 2023, 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_lchown(const char *filename, __uid_t owner, __gid_t group) { struct inode *i; char *tmp_name; int errno; #ifdef __DEBUG__ printk("(pid %d) sys_lchown('%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->state |= INODE_DIRTY; iput(i); free_name(tmp_name); return 0; } ================================================ FILE: kernel/syscalls/link.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/llseek.c ================================================ /* * 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 int offset_high, unsigned int offset_low, __loff_t *result, unsigned int whence) { struct inode *i; __loff_t offset, new_offset; int errno; #ifdef __DEBUG__ printk("(pid %d) sys_llseek(%d, %u, %u, %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; } if(i->fsop && i->fsop->llseek) { fd_table[current->fd[ufd]].offset = new_offset; if((new_offset = i->fsop->llseek(i, new_offset)) < 0) { return (int)new_offset; } } else { return -EPERM; } memcpy_b(result, &new_offset, sizeof(__loff_t)); #ifdef __DEBUG__ printk(" -> returning %lu\n", *result); #endif /*__DEBUG__ */ return 0; } ================================================ FILE: kernel/syscalls/lseek.c ================================================ /* * 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->llseek) { fd_table[current->fd[ufd]].offset = new_offset; new_offset = i->fsop->llseek(i, new_offset); } else { return -EPERM; } #ifdef __DEBUG__ printk(" -> returning %d\n", new_offset); #endif /*__DEBUG__ */ return new_offset; } ================================================ FILE: kernel/syscalls/lstat.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/lstat64.c ================================================ /* * fiwix/kernel/syscalls/lstat64.c * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Copyright 2023, Richard R. Masters. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #ifdef __DEBUG__ #include #include #endif /*__DEBUG__ */ int sys_lstat64(const char *filename, struct stat64 *statbuf) { struct inode *i; char *tmp_name; int errno; #ifdef __DEBUG__ printk("(pid %d) sys_lstat64('%s', 0x%08x) -> returning structure\n", current->pid, filename, (unsigned int)statbuf); #endif /*__DEBUG__ */ if((errno = check_user_area(VERIFY_WRITE, statbuf, sizeof(struct stat64)))) { 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_dev_padding = 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->__st_rdev_padding = 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->st_mtime = i->i_mtime; statbuf->st_ctime = i->i_ctime; iput(i); free_name(tmp_name); return 0; } ================================================ FILE: kernel/syscalls/mkdir.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/mknod.c ================================================ /* * 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 do_mknod(char *pathname, __mode_t mode, __dev_t dev) { struct inode *i, *dir; char *basename; int errno; basename = get_basename(pathname); if((errno = namei(pathname, &i, &dir, !FOLLOW_LINKS))) { if(!dir) { return errno; } } if(!errno) { iput(i); iput(dir); return -EEXIST; } if(IS_RDONLY_FS(dir)) { iput(dir); return -EROFS; } if(check_permission(TO_EXEC | TO_WRITE, dir) < 0) { iput(dir); return -EACCES; } if(dir->fsop && dir->fsop->mknod) { errno = dir->fsop->mknod(dir, basename, mode, dev); } else { errno = -EPERM; } iput(dir); return errno; } int sys_mknod(const char *pathname, __mode_t mode, __dev_t dev) { char *tmp_name; 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; } errno = do_mknod(tmp_name, mode, dev); free_name(tmp_name); return errno; } ================================================ FILE: kernel/syscalls/mmap2.c ================================================ /* * fiwix/kernel/syscalls/mmap2.c * * Copyright 2018-2023, Jordi Sanfeliu. All rights reserved. * Copyright 2023, Richard R. Masters. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #ifdef __DEBUG__ #include #include #endif /*__DEBUG__ */ #ifdef CONFIG_SYSCALL_6TH_ARG #ifdef CONFIG_MMAP2 int sys_mmap2(unsigned int start, unsigned int length, unsigned int prot, unsigned int user_flags, int fd, unsigned int offset) { unsigned int page; struct inode *i; char flags; #ifdef __DEBUG__ printk("(pid %d) sys_mmap2(0x%08x, %d, 0x%02x, 0x%02x, %d, 0x%08x) -> ", current->pid, start, length, prot, user_flags, fd, offset); #endif /*__DEBUG__ */ if(!length) { return -EINVAL; } i = NULL; flags = 0; if(!(user_flags & MAP_ANONYMOUS)) { CHECK_UFD(fd); if(!(i = fd_table[current->fd[fd]].inode)) { return -EBADF; } flags = fd_table[current->fd[fd]].flags & O_ACCMODE; } page = do_mmap(i, start, length, prot, user_flags, offset*4096, P_MMAP, flags, NULL); #ifdef __DEBUG__ printk("0x%08x\n", page); #endif /*__DEBUG__ */ return page; } #endif /* CONFIG_MMAP2 */ #endif /* CONFIG_SYSCALL_6TH_ARG */ ================================================ FILE: kernel/syscalls/mount.c ================================================ /* * fiwix/kernel/syscalls/mount.c * * Copyright 2018-2022, 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_mount(const char *source, const char *target, const char *fstype, unsigned int flags, const void *data) { struct inode *i_source, *i_target; struct mount *mp; struct filesystems *fs; char *tmp_source, *tmp_target, *tmp_fstype; __dev_t dev; int 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(!(mp = get_mount_point(i_target))) { iput(i_target); free_name(tmp_target); return -EINVAL; } fs = mp->fs; if(fs->fsop && fs->fsop->remount_fs) { if((errno = fs->fsop->remount_fs(&mp->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 && !(mp->sb.flags & MS_RDONLY)) { dev = mp->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(&mp->sb); } sync_superblocks(dev); sync_inodes(dev); sync_buffers(dev); } mp->sb.flags &= ~MS_RDONLY; mp->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; } if((errno = malloc_name(fstype, &tmp_fstype)) < 0) { iput(i_target); free_name(tmp_target); return errno; } if(!(fs = get_filesystem(tmp_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(!(mp = add_mount_point(dev, tmp_source, tmp_target))) { 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; } mp->sb.flags = flags; if(fs->fsop->read_superblock) { if((errno = fs->fsop->read_superblock(dev, &mp->sb))) { i_source->fsop->close(i_source, NULL); if(fs->fsop->flags == FSOP_REQUIRES_DEV) { iput(i_source); } iput(i_target); del_mount_point(mp); 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); del_mount_point(mp); free_name(tmp_target); free_name(tmp_fstype); free_name(tmp_source); return -EINVAL; } mp->sb.dir = i_target; mp->fs = fs; fs->mp = mp; i_target->mount_point = mp->sb.root; free_name(tmp_target); free_name(tmp_fstype); free_name(tmp_source); return 0; } ================================================ FILE: kernel/syscalls/mprotect.c ================================================ /* * fiwix/kernel/syscalls/mprotect.c * * Copyright 2018-2022, 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) { return -EINVAL; } if(prot > (PROT_READ | PROT_WRITE | PROT_EXEC)) { return -EINVAL; } length = PAGE_ALIGN(length); if((addr + length) < addr) { return -EINVAL; } if(!(vma = find_vma_region(addr))) { return -ENOMEM; } 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); } ================================================ FILE: kernel/syscalls/msgctl.c ================================================ /* * fiwix/kernel/syscalls/msgctl.c * * Copyright 2022, 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 #endif /*__DEBUG__ */ #ifdef CONFIG_SYSVIPC int sys_msgctl(int msqid, int cmd, struct msqid_ds *buf) { struct msqid_ds *mq; struct msginfo *mi; struct ipc_perm *perm; struct msg *m, *mn; int errno; #ifdef __DEBUG__ printk("(pid %d) sys_msgctl(%d, %d, 0x%x)\n", current->pid, msqid, cmd, (int)buf); #endif /*__DEBUG__ */ if(msqid < 0) { return -EINVAL; } switch(cmd) { case MSG_STAT: case IPC_STAT: if((errno = check_user_area(VERIFY_WRITE, buf, sizeof(struct msqid_ds)))) { return errno; } mq = msgque[msqid % MSGMNI]; if(mq == IPC_UNUSED) { return -EINVAL; } if(!ipc_has_perms(&mq->msg_perm, IPC_R)) { return -EACCES; } memcpy_b(buf, mq, sizeof(struct msqid_ds)); if(cmd == MSG_STAT) { return (mq->msg_perm.seq * MSGMNI) + msqid; } return 0; case IPC_SET: if((errno = check_user_area(VERIFY_READ, buf, sizeof(struct msqid_ds)))) { return errno; } mq = msgque[msqid % MSGMNI]; if(mq == IPC_UNUSED) { return -EINVAL; } perm = &mq->msg_perm; if(!IS_SUPERUSER && current->euid != perm->uid && current->euid != perm->cuid) { return -EPERM; } if(!IS_SUPERUSER && buf->msg_qbytes > MSGMNB) { return -EPERM; } mq->msg_qbytes = buf->msg_qbytes; perm->uid = buf->msg_perm.uid; perm->gid = buf->msg_perm.gid; perm->mode = (perm->mode & ~0777) | (buf->msg_perm.mode & 0777); mq->msg_ctime = CURRENT_TIME; return 0; case IPC_RMID: mq = msgque[msqid % MSGMNI]; if(mq == IPC_UNUSED) { return -EINVAL; } perm = &mq->msg_perm; if(!IS_SUPERUSER && current->euid != perm->uid && current->euid != perm->cuid) { return -EPERM; } if((m = mq->msg_first)) { do { mq->msg_qnum--; mq->msg_cbytes -= m->msg_ts; num_msgs--; mn = m->msg_next; msg_release_md(m); m = mn; } while(m); } msg_release_mq(mq); msgque[msqid % MSGMNI] = (struct msqid_ds *)IPC_UNUSED; num_queues--; msg_seq++; if((msqid % MSGMNI) == max_mqid) { while(max_mqid) { if(msgque[max_mqid] != IPC_UNUSED) { break; } max_mqid--; } } wakeup(mq); return 0; case MSG_INFO: case IPC_INFO: if((errno = check_user_area(VERIFY_WRITE, buf, sizeof(struct msqid_ds)))) { return errno; } mi = (struct msginfo *)buf; if(cmd == MSG_INFO) { mi->msgpool = num_queues; mi->msgmap = num_msgs; mi->msgssz = 0; /* FIXME: pending to do */ mi->msgtql = 0; /* FIXME: pending to do */ mi->msgseg = 0; /* FIXME: pending to do */ } else { mi->msgpool = 0; /* FIXME: pending to do */ mi->msgmap = 0; /* FIXME: pending to do */ mi->msgssz = 0; /* FIXME: pending to do */ mi->msgtql = MSGTQL; mi->msgseg = 0; /* FIXME: pending to do */ } mi->msgmax = MSGMAX; mi->msgmnb = MSGMNB; mi->msgmni = MSGMNI; return max_mqid; } return -EINVAL; } #endif /* CONFIG_SYSVIPC */ ================================================ FILE: kernel/syscalls/msgget.c ================================================ /* * fiwix/kernel/syscalls/msgget.c * * Copyright 2022, 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__ */ #ifdef CONFIG_SYSVIPC struct msqid_ds *msgque[MSGMNI]; unsigned int num_queues; unsigned int num_msgs; unsigned int max_mqid; unsigned int msg_seq; /* FIXME: this should be allocated dynamically */ static struct msqid_ds msgque_pool[MSGMNI]; struct msqid_ds *msg_get_new_mq(void) { int n; for(n = 0; n < MSGMNI; n++) { if(msgque_pool[n].msg_ctime == 0) { msgque_pool[n].msg_ctime = 1; return &msgque_pool[n]; } } return NULL; } void msg_release_mq(struct msqid_ds *mq) { memset_b(mq, 0, sizeof(struct msqid_ds)); } struct msg msg_pool[MSGTQL]; struct msg *msg_get_new_md(void) { unsigned int n; lock_resource(&ipcmsg_resource); for(n = 0; n < MSGTQL; n++) { if(msg_pool[n].msg_stime == 0) { msg_pool[n].msg_stime = 1; unlock_resource(&ipcmsg_resource); return &msg_pool[n]; } } unlock_resource(&ipcmsg_resource); return NULL; } void msg_release_md(struct msg *msg) { lock_resource(&ipcmsg_resource); memset_b(msg, 0, sizeof(struct msg)); unlock_resource(&ipcmsg_resource); } void msg_init(void) { int n; for(n = 0; n < MSGMNI; n++) { msgque[n] = (struct msqid_ds *)IPC_UNUSED; } memset_b(msgque_pool, 0, sizeof(msgque_pool)); memset_b(msg_pool, 0, sizeof(msg_pool)); num_queues = num_msgs = max_mqid = msg_seq = 0; } int sys_msgget(key_t key, int msgflg) { struct msqid_ds *mq; struct ipc_perm *perm; int n; #ifdef __DEBUG__ printk("(pid %d) sys_msgget(%d, 0x%x)\n", current->pid, (int)key, msgflg); #endif /*__DEBUG__ */ if(key == IPC_PRIVATE) { /* create a new message queue */ if(!(mq = msg_get_new_mq())) { return -ENOMEM; } for(n = 0; n < MSGMNI; n++) { if(msgque[n] == (struct msqid_ds *)IPC_UNUSED) { goto init; } } msg_release_mq(mq); return -ENOSPC; } mq = NULL; for(n = 0; n < MSGMNI; n++) { if(msgque[n] == (struct msqid_ds *)IPC_UNUSED) { continue; } if(key == msgque[n]->msg_perm.key) { mq = msgque[n]; break; } } if(!mq) { if(!(msgflg & IPC_CREAT)) { return -ENOENT; } /* create a new message queue */ if(!(mq = msg_get_new_mq())) { return -ENOMEM; } for(n = 0; n < MSGMNI; n++) { if(msgque[n] == (struct msqid_ds *)IPC_UNUSED) { goto init; } } msg_release_mq(mq); return -ENOSPC; } else { if((msgflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL)) { return -EEXIST; } if(!ipc_has_perms(&mq->msg_perm, msgflg)) { return -EACCES; } return (mq->msg_perm.seq * MSGMNI) + n; } init: perm = &mq->msg_perm; perm->key = key; perm->uid = perm->cuid = current->euid; perm->gid = perm->cgid = current->egid; perm->mode = msgflg & 0777; perm->seq = msg_seq; mq->msg_first = mq->msg_last = NULL; mq->msg_stime = mq->msg_rtime = 0; mq->msg_ctime = CURRENT_TIME; mq->unused1 = mq->unused2 = 0; mq->msg_cbytes = mq->msg_qnum = 0; mq->msg_qbytes = MSGMNB; mq->msg_lspid = mq->msg_lrpid = 0; msgque[n] = mq; if(n > max_mqid) { max_mqid = n; } num_queues++; return (mq->msg_perm.seq * MSGMNI) + n; } #endif /* CONFIG_SYSVIPC */ ================================================ FILE: kernel/syscalls/msgrcv.c ================================================ /* * fiwix/kernel/syscalls/msgrcv.c * * Copyright 2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include #include #include #ifdef __DEBUG__ #include #endif /*__DEBUG__ */ #ifdef CONFIG_SYSVIPC int sys_msgrcv(int msqid, void *msgp, __size_t msgsz, int msgtyp, int msgflg) { struct msqid_ds *mq; struct msgbuf *mb; struct msg *m, *mprev; int errno, found, count; #ifdef __DEBUG__ printk("(pid %d) sys_msgrcv(%d, 0x%08x, %d, %d, 0x%x)\n", current->pid, msqid, (int)msgp, msgsz, msgtyp, msgflg); #endif /*__DEBUG__ */ if(msqid < 0) { return -EINVAL; } if((errno = check_user_area(VERIFY_WRITE, msgp, sizeof(void *)))) { return errno; } mq = msgque[msqid % MSGMNI]; if(mq == IPC_UNUSED) { return -EINVAL; } found = 0; mprev = NULL; for(;;) { if(!ipc_has_perms(&mq->msg_perm, IPC_R)) { return -EACCES; } if((m = mq->msg_first)) { if(!msgtyp) { break; } else if(msgtyp > 0) { if(msgflg & MSG_EXCEPT) { while(m) { if(m->msg_type != msgtyp) { found = 1; break; } mprev = m; m = m->msg_next; } } else { while(m) { if(m->msg_type == msgtyp) { found = 1; break; } mprev = m; m = m->msg_next; } } } else { /* FIXME: pending to do */ } } if(found) { break; } if(msgflg & IPC_NOWAIT) { return -ENOMSG; } if(sleep(mq, PROC_INTERRUPTIBLE)) { return -EINTR; } mq = msgque[msqid % MSGMNI]; if(mq == IPC_UNUSED) { return -EIDRM; } } if(msgsz < m->msg_ts) { if(!(msgflg & MSG_NOERROR)) { return -E2BIG; } count = msgsz; } else { count = m->msg_ts; } mb = (struct msgbuf *)msgp; mb->mtype = m->msg_type; memcpy_b(mb->mtext, m->msg_spot, count); lock_resource(&ipcmsg_resource); kfree((unsigned int)m->msg_spot); if(!mprev) { mq->msg_first = m->msg_next; } else { mprev->msg_next = m->msg_next; } mq->msg_rtime = mq->msg_ctime = CURRENT_TIME; mq->msg_qnum--; mq->msg_cbytes -= m->msg_ts; mq->msg_lrpid = current->pid; num_msgs--; unlock_resource(&ipcmsg_resource); msg_release_md(m); wakeup(mq); return count; } #endif /* CONFIG_SYSVIPC */ ================================================ FILE: kernel/syscalls/msgsnd.c ================================================ /* * fiwix/kernel/syscalls/msgsnd.c * * Copyright 2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include #include #include #ifdef __DEBUG__ #include #endif /*__DEBUG__ */ #ifdef CONFIG_SYSVIPC int sys_msgsnd(int msqid, const void *msgp, __size_t msgsz, int msgflg) { struct msqid_ds *mq; struct msgbuf *mb; struct msg *m; int errno; #ifdef __DEBUG__ printk("(pid %d) sys_msgsnd(%d, 0x%08x, %d, 0x%x)\n", current->pid, msqid, (int)msgp, msgsz, msgflg); #endif /*__DEBUG__ */ if(msqid < 0 || msgsz > MSGMAX) { return -EINVAL; } if((errno = check_user_area(VERIFY_READ, msgp, sizeof(void *)))) { return errno; } mb = (struct msgbuf *)msgp; if(mb->mtype < 0) { return -EINVAL; } if((errno = check_user_area(VERIFY_READ, mb->mtext, msgsz))) { return errno; } mq = msgque[msqid % MSGMNI]; if(mq == IPC_UNUSED) { return -EINVAL; } for(;;) { if(!ipc_has_perms(&mq->msg_perm, IPC_W)) { return -EACCES; } if(mq->msg_cbytes + msgsz > mq->msg_qbytes || mq->msg_qnum + 1 > mq->msg_qbytes) { if(msgflg & IPC_NOWAIT) { return -EAGAIN; } if(sleep(mq, PROC_INTERRUPTIBLE)) { return -EINTR; } } if(mq == IPC_UNUSED) { return -EIDRM; } break; } if(!(m = msg_get_new_md())) { return -ENOMEM; } m->msg_next = NULL; m->msg_type = mb->mtype; if(!(m->msg_spot = (void *)kmalloc(PAGE_SIZE))) { msg_release_md(m); return -ENOMEM; } memcpy_b(m->msg_spot, mb->mtext, msgsz); m->msg_stime = CURRENT_TIME; m->msg_ts = msgsz; lock_resource(&ipcmsg_resource); if(!mq->msg_first) { mq->msg_first = mq->msg_last = m; } else { mq->msg_last->msg_next = m; mq->msg_last = m; } mq->msg_stime = mq->msg_ctime = CURRENT_TIME; mq->msg_qnum++; mq->msg_cbytes += msgsz; mq->msg_lspid = current->pid; num_msgs++; unlock_resource(&ipcmsg_resource); wakeup(mq); return 0; } #endif /* CONFIG_SYSVIPC */ ================================================ FILE: kernel/syscalls/munmap.c ================================================ /* * 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); } ================================================ FILE: kernel/syscalls/nanosleep.c ================================================ /* * 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 #include #ifdef __DEBUG__ #include #endif /*__DEBUG__ */ int sys_nanosleep(const struct timespec *req, struct timespec *rem) { int errno, nsec; unsigned int timeout, flags; #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; } /* * Since the current maximum precision of the kernel is only 10ms, we * need to convert any lower request to a minimum of 10ms, even knowing * that this might increase the sleep a bit more than the requested. */ nsec = req->tv_nsec; if(nsec < 10000000L) { nsec *= 10; } /* * Interrupts must be disabled before setting current->timeout in order * to avoid a race condition. Otherwise it might occur that timeout is * so small that it would reach zero before the call to sleep(). In this * case, the process would miss the wakeup() and would stay in the sleep * queue forever. */ timeout = (req->tv_sec * HZ) + (nsec * HZ / 1000000000L); if(timeout) { SAVE_FLAGS(flags); CLI(); current->timeout = timeout; sleep(&sys_nanosleep, PROC_INTERRUPTIBLE); RESTORE_FLAGS(flags); 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; } ================================================ FILE: kernel/syscalls/newfstat.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/newlstat.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/newstat.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/newuname.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/old_mmap.c ================================================ /* * fiwix/kernel/syscalls/old_mmap.c * * Copyright 2018-2022, 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, NULL); #ifdef __DEBUG__ printk("0x%08x\n", page); #endif /*__DEBUG__ */ return page; } ================================================ FILE: kernel/syscalls/old_select.c ================================================ /* * 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 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); } ================================================ FILE: kernel/syscalls/olduname.c ================================================ /* * fiwix/kernel/syscalls/olduname.c * * Copyright 2018-2022, 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, 0, 1); memcpy_b(&uname->nodename, &sys_utsname.nodename, _OLD_UTSNAME_LENGTH); memset_b(&uname->nodename + _OLD_UTSNAME_LENGTH, 0, 1); memcpy_b(&uname->release, &sys_utsname.release, _OLD_UTSNAME_LENGTH); memset_b(&uname->release + _OLD_UTSNAME_LENGTH, 0, 1); memcpy_b(&uname->version, &sys_utsname.version, _OLD_UTSNAME_LENGTH); memset_b(&uname->version + _OLD_UTSNAME_LENGTH, 0, 1); memcpy_b(&uname->machine, &sys_utsname.machine, _OLD_UTSNAME_LENGTH); memset_b(&uname->machine + _OLD_UTSNAME_LENGTH, 0, 1); return 0; } ================================================ FILE: kernel/syscalls/open.c ================================================ /* * fiwix/kernel/syscalls/open.c * * Copyright 2018-2021, 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(!errno) { if(S_ISLNK(i->i_mode) && (flags & O_NOFOLLOW)) { iput(i); iput(dir); free_name(tmp_name); return -ELOOP; } if(!S_ISDIR(i->i_mode) && (flags & O_DIRECTORY)) { iput(i); iput(dir); free_name(tmp_name); return -ENOTDIR; } } if(flags & O_CREAT) { if(!errno && S_ISDIR(i->i_mode)) { iput(i); iput(dir); free_name(tmp_name); return -EISDIR; } if(!errno && (flags & O_EXCL)) { iput(i); iput(dir); free_name(tmp_name); return -EEXIST; } if(!i) { /* * If the file does not exist, you need enough * permission in the directory to create it. */ if(check_permission(TO_EXEC | TO_WRITE, dir) < 0) { iput(i); iput(dir); free_name(tmp_name); return -EACCES; } } if(IS_RDONLY_FS(dir)) { if(!i || S_ISREG(i->i_mode)) { if(flags & (O_RDWR | O_WRONLY | O_TRUNC)) { iput(i); iput(dir); free_name(tmp_name); return -EROFS; } flags &= ~(O_RDWR | O_WRONLY | O_TRUNC); flags |= O_RDONLY; } } if(errno) { /* assumes -ENOENT */ if(dir->fsop && dir->fsop->create) { errno = dir->fsop->create(dir, basename, flags, 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(flags & (O_RDWR | O_WRONLY | O_TRUNC)) { if(S_ISDIR(i->i_mode)) { iput(i); iput(dir); free_name(tmp_name); return -EISDIR; } if(S_ISREG(i->i_mode) && IS_RDONLY_FS(dir)) { iput(i); iput(dir); free_name(tmp_name); return -EROFS; } } 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; } ================================================ FILE: kernel/syscalls/pause.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/personality.c ================================================ /* * 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 int persona) { #ifdef __DEBUG__ printk("(pid %d) sys_personality(%d) -> %d\n", current->pid, persona, persona); #endif /*__DEBUG__ */ return persona; } ================================================ FILE: kernel/syscalls/pipe.c ================================================ /* * 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 #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->mp->sb, S_IFIFO))) { 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; } ================================================ FILE: kernel/syscalls/read.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/readlink.c ================================================ /* * 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; } free_name(tmp_name); if(!S_ISLNK(i->i_mode)) { iput(i); return -EINVAL; } if(i->fsop && i->fsop->readlink) { errno = i->fsop->readlink(i, buffer, bufsize); iput(i); return errno; } iput(i); return -EINVAL; } ================================================ FILE: kernel/syscalls/readv.c ================================================ /* * fiwix/kernel/syscalls/readv.c * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Copyright 2023, Richard R. Masters. * Distributed under the terms of the Fiwix License. */ #include #include #include #ifdef __DEBUG__ #include #include #endif /*__DEBUG__ */ int sys_readv(unsigned int ufd, const struct iovec *iov, int iovcnt) { struct inode *i; int errno; int bytes_read = 0; int vi; /* vector index */ #ifdef __DEBUG__ printk("(pid %d) sys_readv(%d, 0x%08x, %d) -> ", current->pid, ufd, iov, iovcnt); #endif /*__DEBUG__ */ CHECK_UFD(ufd); if(iovcnt < 0 || iovcnt > UIO_MAXIOV) { return -EINVAL; } for (vi = 0; vi < iovcnt; vi++) { const struct iovec *io_read = &iov[vi]; if((errno = check_user_area(VERIFY_WRITE, io_read->iov_base, io_read->iov_len))) { return errno; } if(fd_table[current->fd[ufd]].flags & O_WRONLY) { return -EBADF; } if(!io_read->iov_len) { continue; } if(io_read->iov_len < 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]], io_read->iov_base, io_read->iov_len); if (errno < 0) { return errno; } bytes_read += errno; if (errno < io_read->iov_len) { break; } } else { return -EINVAL; } } #ifdef __DEBUG__ printk("%d\n", bytes_read); #endif /*__DEBUG__ */ return bytes_read; } ================================================ FILE: kernel/syscalls/reboot.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/rename.c ================================================ /* * fiwix/kernel/syscalls/rename.c * * Copyright 2018-2022, 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] == '\0') || (newbasename[0] == '.' && newbasename[1] == '.' && newbasename[2] == '\0')) { errno = -EINVAL; goto end; } oldbasename = get_basename(tmp_oldpath); oldbasename = remove_trailing_slash(oldbasename); if((oldbasename[0] == '.' && oldbasename[1] == '\0') || (oldbasename[0] == '.' && oldbasename[1] == '.' && oldbasename[2] == '\0')) { 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; } ================================================ FILE: kernel/syscalls/rmdir.c ================================================ /* * 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; } free_name(tmp_dirname); if(!S_ISDIR(i->i_mode)) { iput(i); iput(dir); return -ENOTDIR; } if(i == current->root || i->mount_point) { iput(i); iput(dir); return -EBUSY; } if(IS_RDONLY_FS(i)) { iput(i); iput(dir); return -EROFS; } if(i == dir) { iput(i); iput(dir); return -EPERM; } if(check_permission(TO_EXEC | TO_WRITE, dir) < 0) { iput(i); iput(dir); return -EACCES; } /* check sticky permission bit */ if(dir->i_mode & S_ISVTX) { if(check_user_permission(i)) { iput(i); iput(dir); return -EPERM; } } if(i->fsop && i->fsop->rmdir) { errno = i->fsop->rmdir(dir, i); } else { errno = -EPERM; } iput(i); iput(dir); return errno; } ================================================ FILE: kernel/syscalls/select.c ================================================ /* * 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 int set; n = 0; for(;;) { bit = n * __NFDBITS; if(bit >= nfds) { break; } set = rfds->fds_bits[n] | wfds->fds_bits[n] | efds->fds_bits[n]; while(set) { if(__FD_ISSET(bit, rfds) || __FD_ISSET(bit, wfds) || __FD_ISSET(bit, efds)) { CHECK_UFD(bit); } set >>= 1; bit++; } n++; } return 0; } static int do_check(struct inode *i, struct fd *f, int flag) { if(i->fsop && i->fsop->select) { if(i->fsop->select(i, f, 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, &fd_table[current->fd[n]], SEL_R)) { __FD_SET(n, res_rfds); count++; } } if(__FD_ISSET(n, wfds)) { if(do_check(i, &fd_table[current->fd[n]], SEL_W)) { __FD_SET(n, res_wfds); count++; } } if(__FD_ISSET(n, efds)) { if(do_check(i, &fd_table[current->fd[n]], SEL_E)) { __FD_SET(n, res_efds); count++; } } } if(count || !current->timeout || current->sigpending & ~current->sigblocked) { break; } if(sleep(&do_select, PROC_INTERRUPTIBLE)) { return -EINTR; } } return count; } int sys_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { unsigned 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; } t = current->timeout; 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)); } if(timeout) { ticks2tv(t, timeout); } return errno; } ================================================ FILE: kernel/syscalls/semctl.c ================================================ /* * fiwix/kernel/syscalls/semctl.c * * Copyright 2022, 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 #endif /*__DEBUG__ */ #ifdef CONFIG_SYSVIPC int sys_semctl(int semid, int semnum, int cmd, void *arg) { struct semid_ds *ss, *tmp; struct seminfo *si; struct sem_undo *un; struct ipc_perm *perm; struct sem *s; unsigned short int *p; int n, errno, retval, val; #ifdef __DEBUG__ printk("(pid %d) sys_semctl(%d, %d, %d, 0x%x)\n", current->pid, semid, semnum, cmd, (int)arg); #endif /*__DEBUG__ */ if(semid < 0) { return -EINVAL; } switch(cmd) { case IPC_STAT: case SEM_STAT: if((errno = check_user_area(VERIFY_WRITE, arg, sizeof(struct semid_ds)))) { return errno; } if(cmd == IPC_STAT) { ss = semset[semid % SEMMNI]; } else { ss = semset[semid]; } if(ss == IPC_UNUSED) { return -EINVAL; } if(!ipc_has_perms(&ss->sem_perm, IPC_R)) { return -EACCES; } if(cmd == IPC_STAT) { retval = 0; } else { retval = (ss->sem_perm.seq * SEMMNI) + semid; } memcpy_b(arg, ss, sizeof(struct semid_ds)); return retval; case IPC_SET: if((errno = check_user_area(VERIFY_READ, arg, sizeof(struct semid_ds)))) { return errno; } ss = semset[semid % SEMMNI]; if(ss == IPC_UNUSED) { return -EINVAL; } perm = &ss->sem_perm; if(!IS_SUPERUSER && current->euid != perm->uid && current->euid != perm->cuid) { return -EPERM; } tmp = (struct semid_ds *)arg; perm->uid = tmp->sem_perm.uid; perm->gid = tmp->sem_perm.gid; perm->mode = (perm->mode & ~0777) | (tmp->sem_perm.mode & 0777); ss->sem_ctime = CURRENT_TIME; return 0; case IPC_RMID: ss = semset[semid % SEMMNI]; if(ss == IPC_UNUSED) { return -EINVAL; } perm = &ss->sem_perm; if(!IS_SUPERUSER && current->euid != perm->uid && current->euid != perm->cuid) { return -EPERM; } un = ss->undo; while(un) { if(semnum == un->sem_num) { un->semadj = 0; } un = un->id_next; } for(n = 0; n < ss->sem_nsems; n++) { s = ss->sem_base + n; if(s->semncnt) { wakeup(&s->semncnt); } if(s->semzcnt) { wakeup(&s->semzcnt); } } num_sems -= ss->sem_nsems; sem_release_ss(ss); semset[semid % SEMMNI] = (struct semid_ds *)IPC_UNUSED; num_semsets--; sem_seq++; if((semid % SEMMNI) == max_semid) { while(max_semid) { if(semset[max_semid] != IPC_UNUSED) { break; } max_semid--; } } wakeup(ss); return 0; case IPC_INFO: case SEM_INFO: if((errno = check_user_area(VERIFY_WRITE, arg, sizeof(struct seminfo)))) { return errno; } si = (struct seminfo *)arg; if(cmd == IPC_INFO) { si->semusz = sizeof(struct sem_undo); si->semaem = 0; /* FIXME: pending to do */ } else { si->semusz = num_semsets; si->semaem = num_sems; } si->semmap = 0; /* FIXME: pending to do */ si->semmni = SEMMNI; si->semmns = SEMMNS; si->semmnu = 0; /* FIXME: pending to do */ si->semmsl = SEMMSL; si->semopm = SEMOPM; si->semume = 0; /* FIXME: pending to do */ si->semvmx = SEMVMX; return max_semid; case GETPID: case GETVAL: case GETALL: case GETNCNT: case GETZCNT: ss = semset[semid % SEMMNI]; if(ss == IPC_UNUSED) { return -EINVAL; } if(!ipc_has_perms(&ss->sem_perm, IPC_R)) { return -EACCES; } s = ss->sem_base + semnum; switch(cmd) { case GETPID: return s->sempid; case GETVAL: return s->semval; case GETALL: if((errno = check_user_area(VERIFY_WRITE, arg, ss->sem_nsems * sizeof(short int)))) { return errno; } p = (unsigned short int *)arg; for(n = 0; n < ss->sem_nsems; n++) { memcpy_b(p, &ss->sem_base[n].semval, sizeof(short int)); p++; } return 0; case GETNCNT: return s->semncnt; case GETZCNT: return s->semzcnt; } case SETVAL: ss = semset[semid % SEMMNI]; if(ss == IPC_UNUSED) { return -EINVAL; } if(!ipc_has_perms(&ss->sem_perm, IPC_W)) { return -EACCES; } val = (int)(int*)arg; if(val < 0 || val > SEMVMX) { return -ERANGE; } if(semnum < 0 || semnum > ss->sem_nsems) { return -EINVAL; } s = ss->sem_base + semnum; s->semval = val; ss->sem_ctime= CURRENT_TIME; un = ss->undo; while(un) { if(semnum == un->sem_num) { un->semadj = 0; } un = un->id_next; } if(s->semncnt) { wakeup(&s->semncnt); } if(s->semzcnt) { wakeup(&s->semzcnt); } return 0; case SETALL: ss = semset[semid % SEMMNI]; if(ss == IPC_UNUSED) { return -EINVAL; } if((errno = check_user_area(VERIFY_READ, arg, ss->sem_nsems * sizeof(short int)))) { return errno; } if(!ipc_has_perms(&ss->sem_perm, IPC_W)) { return -EACCES; } p = (unsigned short int *)arg; for(n = 0; n < ss->sem_nsems; n++) { ss->sem_base[n].semval = *p; p++; s = ss->sem_base + n; if(s->semncnt) { wakeup(&s->semncnt); } if(s->semzcnt) { wakeup(&s->semzcnt); } } ss->sem_ctime= CURRENT_TIME; un = ss->undo; while(un) { if(semnum == un->sem_num) { un->semadj = 0; } un = un->id_next; } return 0; } return -EINVAL; } #endif /* CONFIG_SYSVIPC */ ================================================ FILE: kernel/syscalls/semget.c ================================================ /* * fiwix/kernel/syscalls/semget.c * * Copyright 2022, 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__ */ #ifdef CONFIG_SYSVIPC static struct resource ipcsem_resource = { 0, 0 }; struct semid_ds *semset[SEMMNI]; unsigned int num_semsets; unsigned int num_sems; unsigned int max_semid; unsigned int sem_seq; /* FIXME: this should be allocated dynamically */ static struct semid_ds semset_pool[SEMMNI]; struct semid_ds *sem_get_new_ss(void) { int n; for(n = 0; n < SEMMNI; n++) { if(semset_pool[n].sem_ctime == 0) { semset_pool[n].sem_ctime = 1; return &semset_pool[n]; } } return NULL; } void sem_release_ss(struct semid_ds *ss) { memset_b(ss, 0, sizeof(struct semid_ds)); } struct sem sma_pool[SEMMNS]; struct sem *sem_get_new_sma(void) { unsigned int n; lock_resource(&ipcsem_resource); for(n = 0; n < SEMMNS; n += SEMMSL) { if(sma_pool[n].sempid < 0) { sma_pool[n].sempid = 0; unlock_resource(&ipcsem_resource); return &sma_pool[n]; } } unlock_resource(&ipcsem_resource); return NULL; } void sem_release_sma(struct sem *sma) { lock_resource(&ipcsem_resource); memset_b(sma, 0, sizeof(struct sem) * SEMMSL); sma->sempid = -1; unlock_resource(&ipcsem_resource); } struct sem_undo semundo_pool[SEMMSL]; struct sem_undo *sem_get_new_su(void) { int n; for(n = 0; n < SEMMSL; n++) { if(semundo_pool[n].semid < 0) { semundo_pool[n].semid = 0; return &semundo_pool[n]; } } return NULL; } void sem_release_su(struct sem_undo *su) { memset_b(su, 0, sizeof(struct sem_undo)); su->semid = -1; } void sem_init(void) { int n; for(n = 0; n < SEMMNI; n++) { semset[n] = (struct semid_ds *)IPC_UNUSED; } memset_b(semset_pool, 0, sizeof(semset_pool)); memset_b(sma_pool, 0, sizeof(sma_pool)); for(n = 0; n < SEMMNS; n += SEMMSL) { sma_pool[n].sempid = -1; } memset_b(semundo_pool, 0, sizeof(semundo_pool)); for(n = 0; n < SEMMSL; n++) { semundo_pool[n].semid = -1; } num_semsets = num_sems = max_semid = sem_seq = 0; } int sys_semget(key_t key, int nsems, int semflg) { struct semid_ds *ss; struct ipc_perm *perm; int n; #ifdef __DEBUG__ printk("(pid %d) sys_semget(%d, %d, 0x%x)\n", current->pid, (int)key, nsems, semflg); #endif /*__DEBUG__ */ if(nsems < 0 || nsems > SEMMSL) { return -EINVAL; } if(key == IPC_PRIVATE) { /* create a new semaphore set */ if(!nsems) { return -EINVAL; } if(num_sems + nsems > SEMMNS) { return -ENOSPC; } if(!(ss = sem_get_new_ss())) { return -ENOMEM; } for(n = 0; n < SEMMNI; n++) { if(semset[n] == (struct semid_ds *)IPC_UNUSED) { goto init; } } sem_release_ss(ss); return -ENOSPC; } ss = NULL; for(n = 0; n < SEMMNI; n++) { if(semset[n] == (struct semid_ds *)IPC_UNUSED) { continue; } if(key == semset[n]->sem_perm.key) { ss = semset[n]; break; } } if(!ss) { if(!(semflg & IPC_CREAT)) { return -ENOENT; } /* create a new semaphore set */ if(!nsems) { return -EINVAL; } if(num_sems + nsems > SEMMNS) { return -ENOSPC; } if(!(ss = sem_get_new_ss())) { return -ENOMEM; } for(n = 0; n < SEMMNI; n++) { if(semset[n] == (struct semid_ds *)IPC_UNUSED) { goto init; } } sem_release_ss(ss); return -ENOSPC; } else { if((semflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL)) { return -EEXIST; } if(!ipc_has_perms(&ss->sem_perm, semflg)) { return -EACCES; } if(nsems > ss->sem_nsems) { return -EINVAL; } return (ss->sem_perm.seq * SEMMNI) + n; } init: perm = &ss->sem_perm; perm->key = key; perm->uid = perm->cuid = current->euid; perm->gid = perm->cgid = current->egid; perm->mode = semflg & 0777; perm->seq = sem_seq; ss->sem_otime = 0; ss->sem_ctime = CURRENT_TIME; ss->sem_base = sem_get_new_sma(); ss->undo = NULL; ss->sem_nsems = nsems; semset[n] = ss; if(n > max_semid) { max_semid = n; } num_sems += nsems; num_semsets++; return (ss->sem_perm.seq * SEMMNI) + n; } #endif /* CONFIG_SYSVIPC */ ================================================ FILE: kernel/syscalls/semop.c ================================================ /* * fiwix/kernel/syscalls/semop.c * * Copyright 2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include #include #ifdef __DEBUG__ #include #endif /*__DEBUG__ */ #ifdef CONFIG_SYSVIPC static void semundo_ops(struct semid_ds *ss, struct sembuf *sops, int max) { struct sem *s; int n; for(n = 0; n < max; n++) { s = ss->sem_base + sops[n].sem_num; s->semval -= sops[n].sem_op; } } void semexit(void) { struct semid_ds *ss; struct sem_undo *su, **sup, *ssu, **ssup; struct sem *s; sup = ¤t->semundo; while(*sup) { su = *sup; ss = semset[su->semid % SEMMNI]; if(ss != IPC_UNUSED) { ssup = &ss->undo; while(*ssup) { ssu = *ssup; *ssup = ssu->id_next; if(su == ssu) { s = ss->sem_base + ssu->sem_num; if((ssu->semadj + s->semval) >= 0) { s->semval += ssu->semadj; s->sempid = current->pid; ss->sem_otime = CURRENT_TIME; if(s->semncnt) { wakeup(&s->semncnt); } if(!s->semval && s->semzcnt) { wakeup(&s->semzcnt); } } } ssup = &ssu->id_next; } } *sup = su->proc_next; sem_release_su(su); } current->semundo = NULL; } int sys_semop(int semid, struct sembuf *sops, int nsops) { struct semid_ds *ss; struct sem *s; struct sem_undo *su; int need_alter, need_undo; int n, errno; #ifdef __DEBUG__ printk("(pid %d) sys_semop(%d, 0x%x, %d)\n", current->pid, semid, (int)sops, nsops); #endif /*__DEBUG__ */ if(semid < 0 || nsops <= 0) { return -EINVAL; } if(nsops > SEMOPM) { return -E2BIG; } if((errno = check_user_area(VERIFY_READ, sops, sizeof(struct sembuf)))) { return errno; } ss = semset[semid % SEMMNI]; if(ss == IPC_UNUSED) { return -EINVAL; } /* check permissions and ranges for all semaphore operations */ need_alter = 0; for(n = 0; n < nsops; n++) { if(sops[n].sem_num > ss->sem_nsems) { return -EFBIG; } /* only negative and positive operations ... */ if(sops[n].sem_op) { /* will alter semaphores */ need_alter++; } } if(!ipc_has_perms(&ss->sem_perm, need_alter ? IPC_W : IPC_R)) { return -EACCES; } need_undo = 0; loop: for(n = 0; n < nsops; n++) { s = ss->sem_base + sops[n].sem_num; /* positive semaphore operation */ if(sops[n].sem_op > 0) { if((sops[n].sem_op + s->semval) > SEMVMX) { /* reverse all semaphore ops */ semundo_ops(ss, sops, n); return -ERANGE; } if(sops[n].sem_flg & SEM_UNDO) { need_undo = 1; } s->semval += sops[n].sem_op; if(s->semncnt) { wakeup(&s->semncnt); } } /* negative semaphore operation */ if(sops[n].sem_op < 0) { if((sops[n].sem_op + s->semval) >= 0) { if(sops[n].sem_flg & SEM_UNDO) { need_undo = 1; } s->semval += sops[n].sem_op; if(!s->semval && s->semzcnt) { wakeup(&s->semzcnt); } } else { /* reverse all semaphore ops */ semundo_ops(ss, sops, n); if(sops[n].sem_flg & IPC_NOWAIT) { return -EAGAIN; } s->semncnt++; errno = sleep(&s->semncnt, PROC_INTERRUPTIBLE); ss = semset[semid % SEMMNI]; if(ss == IPC_UNUSED) { return -EIDRM; } s->semncnt--; if(errno) { return -EINTR; } goto loop; /* start loop from beginning */ } } /* semaphore operation is zero */ if(!sops[n].sem_op) { if(s->semval) { /* reverse all semaphore ops */ semundo_ops(ss, sops, n); if(sops[n].sem_flg & IPC_NOWAIT) { return -EAGAIN; } s->semzcnt++; errno = sleep(&s->semzcnt, PROC_INTERRUPTIBLE); ss = semset[semid % SEMMNI]; if(ss == IPC_UNUSED) { return -EIDRM; } s->semzcnt--; if(errno) { return -EINTR; } goto loop; /* start loop from beginning */ } } } if(need_undo) { for(n = 0; n < nsops; n++) { if(sops[n].sem_flg & SEM_UNDO) { su = current->semundo; while(su) { if(su->semid == semid && su->sem_num == sops[n].sem_num) { break; } su = su->proc_next; } if(!su) { if(!(su = sem_get_new_su())) { semundo_ops(ss, sops, n); return -ENOMEM; } su->proc_next = current->semundo; su->id_next = ss->undo; su->semid = semid; su->semadj = 0; su->sem_num = sops[n].sem_num; current->semundo = su; ss->undo = su; } su->semadj -= sops[n].sem_op; } } } for(n = 0; n < nsops; n++) { s = ss->sem_base + sops[n].sem_num; s->sempid = current->pid; } ss->sem_otime = CURRENT_TIME; return 0; } #endif /* CONFIG_SYSVIPC */ ================================================ FILE: kernel/syscalls/setdomainname.c ================================================ /* * fiwix/kernel/syscalls/setdomainname.c * * Copyright 2018-2022, 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; char *tmp_name; #ifdef __DEBUG__ printk("(pid %d) sys_setdomainname('%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.domainname, tmp_name, length); sys_utsname.domainname[length] = 0; free_name(tmp_name); return 0; } ================================================ FILE: kernel/syscalls/setfsgid.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/setfsuid.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/setgid.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/setgroups.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/sethostname.c ================================================ /* * fiwix/kernel/syscalls/sethostname.c * * Copyright 2018-2022, 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] = 0; free_name(tmp_name); return 0; } ================================================ FILE: kernel/syscalls/setitimer.c ================================================ /* * 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); } ================================================ FILE: kernel/syscalls/setpgid.c ================================================ /* * fiwix/kernel/syscalls/setpgid.c * * Copyright 2018-2021, 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; } if(!(p = get_proc_by_pid(pid))) { return -EINVAL; } if(!pgid) { pgid = p->pid; } if(p != current && p->ppid != current) { return -ESRCH; } if(p->sid == p->pid || p->sid != current->sid) { return -EPERM; } { struct proc *p; FOR_EACH_PROCESS(p) { if(p->pgid == pgid && p->sid != current->sid) { return -EPERM; } p = p->next; } } if(p != current && p->flags & PF_PEXEC) { return -EACCES; } p->pgid = pgid; #ifdef __DEBUG__ printk(" -> 0\n"); #endif /*__DEBUG__ */ return 0; } ================================================ FILE: kernel/syscalls/setregid.c ================================================ /* * 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 != (__uid_t)-1) { if(gid != (__uid_t)-1 || (current->egid >= 0 && current->gid != egid)) { current->sgid = egid; } current->egid = egid; } if(gid != (__uid_t)-1) { current->gid = gid; } } else { if(egid != (__uid_t)-1 && (current->gid == egid || current->egid == egid || current->sgid == egid)) { if(gid != (__uid_t)-1 || (current->egid >= 0 && current->gid != egid)) { current->sgid = egid; } current->egid = egid; } else { return -EPERM; } if(gid != (__uid_t)-1 && (current->gid == gid || current->egid == gid)) { current->gid = gid; } else { return -EPERM; } } return 0; } ================================================ FILE: kernel/syscalls/setreuid.c ================================================ /* * 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 != (__uid_t)-1) { if(uid != (__uid_t)-1 || (current->euid >= 0 && current->uid != euid)) { current->suid = euid; } current->euid = euid; } if(uid != (__uid_t)-1) { current->uid = uid; } } else { if(euid != (__uid_t)-1 && (current->uid == euid || current->euid == euid || current->suid == euid)) { if(uid != (__uid_t)-1 || (current->euid >= 0 && current->uid != euid)) { current->suid = euid; } current->euid = euid; } else { return -EPERM; } if(uid != (__uid_t)-1 && (current->uid == uid || current->euid == uid)) { current->uid = uid; } else { return -EPERM; } } #ifdef __DEBUG__ printk(" 0\n"); #endif /*__DEBUG__ */ return 0; } ================================================ FILE: kernel/syscalls/setrlimit.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/setsid.c ================================================ /* * fiwix/kernel/syscalls/setsid.c * * Copyright 2018-2021, 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; } p = p->next; } current->sid = current->pgid = current->pid; current->ctty = NULL; return current->sid; } ================================================ FILE: kernel/syscalls/settimeofday.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/setuid.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/sgetmask.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/shmat.c ================================================ /* * fiwix/kernel/syscalls/shmat.c * * Copyright 2022, 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 #ifdef CONFIG_SYSVIPC int shm_map_page(struct vma *vma, unsigned int cr2) { struct shmid_ds *seg; struct page *pg; unsigned int addr, index; seg = (struct shmid_ds *)vma->object; index = (cr2 - vma->start) / PAGE_SIZE; addr = seg->shm_pages[index]; if(!addr) { if(!(addr = map_page(current, cr2, 0, vma->prot))) { printk("%s(): Oops, map_page() returned 0!\n", __FUNCTION__); return 1; } seg->shm_pages[index] = addr; shm_rss++; } else { if(!(addr = map_page(current, cr2, V2P(addr), vma->prot))) { printk("%s(): Oops, map_page() returned 0!\n", __FUNCTION__); return 1; } } pg = &page_table[V2P(addr) >> PAGE_SHIFT]; pg->count++; return 0; } int sys_shmat(int shmid, char *shmaddr, int shmflg, unsigned int *raddr) { struct shmid_ds *seg; struct vma *sega; unsigned int addr; int errno; #ifdef __DEBUG__ printk("(pid %d) sys_shmat(%d, 0x%x, %d, 0x%x)\n", current->pid, shmid, (int)shmaddr, shmflg, (int)raddr); #endif /*__DEBUG__ */ if(shmid < 0) { return -EINVAL; } seg = shmseg[shmid % SHMMNI]; if(seg == IPC_UNUSED) { return -EINVAL; } addr = (unsigned int)shmaddr; if(addr) { if(shmflg & SHM_RND) { addr &= ~(SHMLBA - 1); } else { if(addr & (SHMLBA - 1)) { return -EINVAL; } } if(find_vma_intersection(addr, addr + seg->shm_segsz)) { return -EINVAL; } } else { if(!(addr = get_unmapped_vma_region(seg->shm_segsz))) { return -ENOMEM; } } if(!ipc_has_perms(&seg->shm_perm, shmflg & SHM_RDONLY ? IPC_R : IPC_R | IPC_W)) { return -EACCES; } if(!(sega = shm_get_new_attach(seg))) { return -ENOMEM; } sega->start = addr; sega->end = addr + seg->shm_segsz; sega->prot = PROT_READ | PROT_WRITE | PROT_EXEC; sega->flags = MAP_PRIVATE | MAP_FIXED; sega->offset = 0; sega->s_type = P_SHM; sega->inode = NULL; sega->o_mode = shmflg & SHM_RDONLY ? O_RDONLY : O_RDWR; seg->shm_nattch++; errno = do_mmap(NULL, addr, seg->shm_segsz, sega->prot, sega->flags, sega->offset, sega->s_type, sega->o_mode, seg); if(errno < 0 && errno > -PAGE_SIZE) { return errno; } seg->shm_atime = CURRENT_TIME; seg->shm_lpid = current->pid; *raddr = addr; return 0; } #endif /* CONFIG_SYSVIPC */ ================================================ FILE: kernel/syscalls/shmctl.c ================================================ /* * fiwix/kernel/syscalls/shmctl.c * * Copyright 2022, 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 #endif /*__DEBUG__ */ #ifdef CONFIG_SYSVIPC int sys_shmctl(int shmid, int cmd, struct shmid_ds *buf) { struct shmid_ds *seg; struct shminfo *si; struct shm_info *s_i; struct ipc_perm *perm; int errno; #ifdef __DEBUG__ printk("(pid %d) sys_shmctl(%d, %d, 0x%x)\n", current->pid, shmid, cmd, (int)buf); #endif /*__DEBUG__ */ if(shmid < 0) { return -EINVAL; } switch(cmd) { case IPC_STAT: case SHM_STAT: if((errno = check_user_area(VERIFY_WRITE, buf, sizeof(struct shmid_ds)))) { return errno; } if(cmd == SHM_STAT) { if(shmid > max_segid) { return -EINVAL; } seg = shmseg[shmid]; } else { seg = shmseg[shmid % SHMMNI]; } if(seg == IPC_UNUSED) { return -EINVAL; } if(!ipc_has_perms(&seg->shm_perm, IPC_R)) { return -EACCES; } memcpy_b(buf, seg, sizeof(struct shmid_ds)); /* private kernel information zeroed */ buf->shm_npages = 0; buf->shm_pages = 0; buf->shm_attaches = 0; if(cmd == SHM_STAT) { return (seg->shm_perm.seq * SHMMNI) + shmid; } return 0; case IPC_SET: if((errno = check_user_area(VERIFY_READ, buf, sizeof(struct shmid_ds)))) { return errno; } seg = shmseg[shmid % SHMMNI]; if(seg == IPC_UNUSED) { return -EINVAL; } perm = &seg->shm_perm; if(!IS_SUPERUSER && current->euid != perm->uid && current->euid != perm->cuid) { return -EPERM; } perm->uid = buf->shm_perm.uid; perm->gid = buf->shm_perm.gid; perm->mode = (perm->mode & ~0777) | (buf->shm_perm.mode & 0777); seg->shm_ctime = CURRENT_TIME; return 0; case IPC_RMID: seg = shmseg[shmid % SHMMNI]; if(seg == IPC_UNUSED) { return -EINVAL; } perm = &seg->shm_perm; if(!IS_SUPERUSER && current->euid != perm->uid && current->euid != perm->cuid) { return -EPERM; } perm->mode |= SHM_DEST; if(!seg->shm_nattch) { free_seg(shmid); } return 0; case IPC_INFO: if((errno = check_user_area(VERIFY_WRITE, buf, sizeof(struct shminfo)))) { return errno; } si = (struct shminfo *)buf; si->shmmax = SHMMAX; si->shmmin = SHMMIN; si->shmmni = SHMMNI; si->shmseg = SHMSEG; si->shmall = SHMALL; return max_segid; case SHM_INFO: if((errno = check_user_area(VERIFY_WRITE, buf, sizeof(struct shm_info)))) { return errno; } s_i = (struct shm_info *)buf; s_i->used_ids = num_segs; s_i->shm_tot = shm_tot; s_i->shm_rss = shm_rss; s_i->shm_swp = 0; /* FIXME: pending to do */ s_i->swap_attempts = 0; /* FIXME: pending to do */ s_i->swap_successes = 0; /* FIXME: pending to do */ return max_segid; } return -EINVAL; } #endif /* CONFIG_SYSVIPC */ ================================================ FILE: kernel/syscalls/shmdt.c ================================================ /* * fiwix/kernel/syscalls/shmdt.c * * Copyright 2022, 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 #ifdef CONFIG_SYSVIPC int sys_shmdt(char *shmaddr) { struct vma *vma; struct shmid_ds *seg; unsigned int addr; int n; #ifdef __DEBUG__ printk("(pid %d) sys_shmdt(0x%x)\n", current->pid, (int)shmaddr); #endif /*__DEBUG__ */ addr = (unsigned int)shmaddr; if(!(vma = find_vma_region(addr))) { printk("WARNING: %s(): no vma region found!\n", __FUNCTION__); return 0; } if(vma->s_type != P_SHM) { printk("WARNING: %s(): vma region is not a shared memory!\n", __FUNCTION__); return 0; } if(!(seg = (struct shmid_ds *)vma->object)) { printk("WARNING: %s(): object is NULL!\n", __FUNCTION__); return 0; } for(n = 0; n < NUM_ATTACHES_PER_SEG; n++) { if(seg->shm_attaches[n].start == addr) { do_munmap(addr, seg->shm_attaches[n].end - seg->shm_attaches[n].start); shm_release_attach(&seg->shm_attaches[n]); seg->shm_nattch--; } } return 0; } #endif /* CONFIG_SYSVIPC */ ================================================ FILE: kernel/syscalls/shmget.c ================================================ /* * fiwix/kernel/syscalls/shmget.c * * Copyright 2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #ifdef __DEBUG__ #include #endif /*__DEBUG__ */ #ifdef CONFIG_SYSVIPC struct shmid_ds *shmseg[SHMMNI]; unsigned int num_segs; unsigned int max_segid; unsigned int shm_seq; unsigned int shm_tot; unsigned int shm_rss; /* FIXME: this should be allocated dynamically */ static struct shmid_ds shmseg_pool[SHMMNI]; struct shmid_ds *shm_get_new_seg(void) { int n; for(n = 0; n < SHMMNI; n++) { if(shmseg_pool[n].shm_ctime == 0) { shmseg_pool[n].shm_ctime = 1; if(!(shmseg_pool[n].shm_pages = (unsigned int *)kmalloc(PAGE_SIZE))) { return NULL; } memset_b(shmseg_pool[n].shm_pages, 0, PAGE_SIZE); return &shmseg_pool[n]; } } return NULL; } void shm_release_seg(struct shmid_ds *seg) { kfree((unsigned int)seg->shm_pages); if(seg->shm_attaches) { kfree((unsigned int)seg->shm_attaches); } memset_b(seg, 0, sizeof(struct shmid_ds)); } void free_seg(int shmid) { struct shmid_ds *seg; int npages; seg = shmseg[shmid % SHMMNI]; npages = (seg->shm_segsz + (PAGE_SIZE - 1)) >> PAGE_SHIFT; num_segs--; shm_tot -= npages; shm_seq++; shmseg[shmid % SHMMNI] = (struct shmid_ds *)IPC_UNUSED; if((shmid % SHMMNI) == max_segid) { while(max_segid) { if(shmseg[max_segid] != IPC_UNUSED) { break; } max_segid--; } } shm_release_seg(seg); } struct vma *shm_get_new_attach(struct shmid_ds *seg) { int n; if(!seg->shm_attaches) { if(!(seg->shm_attaches = (void *)kmalloc(PAGE_SIZE))) { return NULL; } memset_b(seg->shm_attaches, 0, PAGE_SIZE); } for(n = 0; n < NUM_ATTACHES_PER_SEG; n++) { if(!seg->shm_attaches[n].start && !seg->shm_attaches[n].end) { return &seg->shm_attaches[n]; } } return NULL; } void shm_release_attach(struct vma *attach) { memset_b(attach, 0, sizeof(struct vma)); } void shm_init(void) { int n; for(n = 0; n < SHMMNI; n++) { shmseg[n] = (struct shmid_ds *)IPC_UNUSED; } memset_b(shmseg_pool, 0, sizeof(shmseg_pool)); num_segs = max_segid = shm_seq = shm_tot = shm_rss = 0; } int sys_shmget(key_t key, __size_t size, int shmflg) { struct shmid_ds *seg; struct ipc_perm *perm; int n, npages; #ifdef __DEBUG__ printk("(pid %d) sys_shmget(%d, %d, 0x%x)\n", current->pid, (int)key, size, shmflg); #endif /*__DEBUG__ */ if(size < 0 || size > SHMMAX) { return -EINVAL; } if(key == IPC_PRIVATE) { /* create a new segment */ if(size < SHMMIN) { return -EINVAL; } npages = (size + (PAGE_SIZE - 1)) >> PAGE_SHIFT; if(shm_tot + npages >= SHMALL) { return -ENOSPC; } if(!(seg = shm_get_new_seg())) { return -ENOMEM; } for(n = 0; n < SHMMNI; n++) { if(shmseg[n] == (struct shmid_ds *)IPC_UNUSED) { goto init; } } shm_release_seg(seg); return -ENOSPC; } seg = NULL; for(n = 0; n < SHMMNI; n++) { if(shmseg[n] == (struct shmid_ds *)IPC_UNUSED) { continue; } if(key == shmseg[n]->shm_perm.key) { seg = shmseg[n]; break; } } if(!seg) { if(!(shmflg & IPC_CREAT)) { return -ENOENT; } /* create a new segment */ if(size < SHMMIN) { return -EINVAL; } npages = (size + (PAGE_SIZE - 1)) >> PAGE_SHIFT; if(shm_tot + npages >= SHMALL) { return -ENOSPC; } if(!(seg = shm_get_new_seg())) { return -ENOMEM; } for(n = 0; n < SHMMNI; n++) { if(shmseg[n] == (struct shmid_ds *)IPC_UNUSED) { goto init; } } shm_release_seg(seg); return -ENOSPC; } else { if((shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL)) { return -EEXIST; } if(!ipc_has_perms(&seg->shm_perm, shmflg)) { return -EACCES; } if(size > seg->shm_segsz) { return -EINVAL; } return (seg->shm_perm.seq * SHMMNI) + n; } init: perm = &seg->shm_perm; perm->key = key; perm->uid = perm->cuid = current->euid; perm->gid = perm->cgid = current->egid; perm->mode = shmflg & 0777; perm->seq = shm_seq; seg->shm_segsz = size; seg->shm_atime = seg->shm_dtime = 0; seg->shm_ctime = CURRENT_TIME; seg->shm_cpid = current->pid; seg->shm_lpid = 0; seg->shm_nattch = 0; seg->shm_npages = 0; seg->shm_attaches = 0; shmseg[n] = seg; if(n > max_segid) { max_segid = n; } num_segs++; shm_tot += npages; return (seg->shm_perm.seq * SHMMNI) + n; } #endif /* CONFIG_SYSVIPC */ ================================================ FILE: kernel/syscalls/sigaction.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/signal.c ================================================ /* * fiwix/kernel/syscalls/signal.c * * Copyright 2018-2021, 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(unsigned int)))) { 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; } ================================================ FILE: kernel/syscalls/sigpending.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/sigprocmask.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/sigreturn.c ================================================ /* * fiwix/kernel/syscalls/sigreturn.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #ifdef __DEBUG__ #include #endif /*__DEBUG__ */ #ifdef CONFIG_SYSCALL_6TH_ARG int sys_sigreturn(unsigned int signum, int arg2, int arg3, int arg4, int arg5, int arg6, struct sigcontext *sc) #else int sys_sigreturn(unsigned int signum, int arg2, int arg3, int arg4, int arg5, struct sigcontext *sc) #endif /* CONFIG_SYSCALL_6TH_ARG */ { #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; } ================================================ FILE: kernel/syscalls/sigsuspend.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/socketcall.c ================================================ /* * fiwix/kernel/syscalls/socketcall.c * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #ifdef __DEBUG__ #include #endif /*__DEBUG__ */ int sys_socketcall(int call, unsigned int *args) { #ifdef CONFIG_NET int errno; #ifdef __DEBUG__ printk("(pid %d) sys_socketcall(%d, 0x%08x)\n", current->pid, call, args); #endif /*__DEBUG__ */ switch(call) { case SYS_SOCKET: if((errno = check_user_area(VERIFY_READ, args, sizeof(unsigned int) * 3))) { return errno; } return socket(args[0], args[1], args[2]); case SYS_BIND: if((errno = check_user_area(VERIFY_READ, args, sizeof(unsigned int) * 3))) { return errno; } return bind(args[0], (struct sockaddr *)args[1], args[2]); case SYS_CONNECT: if((errno = check_user_area(VERIFY_READ, args, sizeof(unsigned int) * 3))) { return errno; } return connect(args[0], (struct sockaddr *)args[1], args[2]); case SYS_LISTEN: if((errno = check_user_area(VERIFY_READ, args, sizeof(unsigned int) * 2))) { return errno; } return listen(args[0], args[1]); case SYS_ACCEPT: if((errno = check_user_area(VERIFY_READ, args, sizeof(unsigned int) * 3))) { return errno; } return accept(args[0], (struct sockaddr *)args[1], (unsigned int *)args[2]); case SYS_GETSOCKNAME: if((errno = check_user_area(VERIFY_READ, args, sizeof(unsigned int) * 3))) { return errno; } return getname(args[0], (struct sockaddr *)args[1], (unsigned int *)args[2], SYS_GETSOCKNAME); case SYS_GETPEERNAME: if((errno = check_user_area(VERIFY_READ, args, sizeof(unsigned int) * 3))) { return errno; } return getname(args[0], (struct sockaddr *)args[1], (unsigned int *)args[2], SYS_GETPEERNAME); case SYS_SOCKETPAIR: if((errno = check_user_area(VERIFY_READ, args, sizeof(unsigned int) * 4))) { return errno; } return socketpair(args[0], args[1], args[2], (int *)args[3]); case SYS_SEND: if((errno = check_user_area(VERIFY_READ, args, sizeof(unsigned int) * 4))) { return errno; } return send(args[0], (void *)args[1], args[2], args[3]); case SYS_RECV: if((errno = check_user_area(VERIFY_READ, args, sizeof(unsigned int) * 4))) { return errno; } return recv(args[0], (void *)args[1], args[2], args[3]); case SYS_SENDTO: if((errno = check_user_area(VERIFY_READ, args, sizeof(unsigned int) * 6))) { return errno; } return sendto(args[0], (void *)args[1], args[2], args[3], (struct sockaddr *)args[4], args[5]); case SYS_RECVFROM: if((errno = check_user_area(VERIFY_READ, args, sizeof(unsigned int) * 6))) { return errno; } return recvfrom(args[0], (void *)args[1], args[2], args[3], (struct sockaddr *)args[4], (int *)args[5]); case SYS_SHUTDOWN: if((errno = check_user_area(VERIFY_READ, args, sizeof(unsigned int) * 2))) { return errno; } return shutdown(args[0], args[1]); case SYS_SETSOCKOPT: if((errno = check_user_area(VERIFY_READ, args, sizeof(unsigned int) * 5))) { return errno; } return setsockopt(args[0], args[1], args[2], (void *)args[3], args[4]); case SYS_GETSOCKOPT: if((errno = check_user_area(VERIFY_READ, args, sizeof(unsigned int) * 5))) { return errno; } return getsockopt(args[0], args[1], args[2], (void *)args[3], (socklen_t *)args[4]); } return -EINVAL; #else return -ENOSYS; #endif /* CONFIG_NET */ } ================================================ FILE: kernel/syscalls/ssetmask.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/stat.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/stat64.c ================================================ /* * fiwix/kernel/syscalls/stat64.c * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Copyright 2023, Richard R. Masters. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #ifdef __DEBUG__ #include #include #endif /*__DEBUG__ */ int sys_stat64(const char *filename, struct stat64 *statbuf) { struct inode *i; char *tmp_name; int errno; #ifdef __DEBUG__ printk("(pid %d) sys_stat64('%s', 0x%08x) -> returning structure\n", current->pid, filename, (unsigned int)statbuf); #endif /*__DEBUG__ */ if((errno = check_user_area(VERIFY_WRITE, statbuf, sizeof(struct stat64)))) { 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_dev_padding = 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->__st_rdev_padding = 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->st_atime_nsec = 0; statbuf->st_mtime = i->i_mtime; statbuf->st_mtime_nsec = 0; statbuf->st_ctime = i->i_ctime; statbuf->st_ctime_nsec = 0; iput(i); free_name(tmp_name); return 0; } ================================================ FILE: kernel/syscalls/statfs.c ================================================ /* * 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; } free_name(tmp_name); if(i->sb && i->sb->fsop && i->sb->fsop->statfs) { i->sb->fsop->statfs(i->sb, statfsbuf); iput(i); return 0; } iput(i); return -ENOSYS; } ================================================ FILE: kernel/syscalls/stime.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/symlink.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/sync.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/sysinfo.c ================================================ /* * fiwix/kernel/syscalls/sysinfo.c * * Copyright 2018-2022, 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, 0, 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_size * 1024; tmp_info.totalswap = 0; tmp_info.freeswap = 0; FOR_EACH_PROCESS(p) { tmp_info.procs++; p = p->next; } memcpy_b(info, &tmp_info, sizeof(struct sysinfo)); return 0; } ================================================ FILE: kernel/syscalls/syslog.c ================================================ /* * fiwix/kernel/syscalls/syslog.c * * Copyright 2025, 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_syslog(int type, char *buffer, int len) { char c; int errno, n, count; #ifdef __DEBUG__ printk("(pid %d) sys_syslog(%d, 0x%x, %d)\n", current->pid, type, buffer, len); #endif /*__DEBUG__ */ if(type != SYSLOG_READ_ALL && !IS_SUPERUSER) { return -EPERM; } if(type == SYSLOG_CLOSE || type == SYSLOG_OPEN) { return 0; } if(type == SYSLOG_READ || type == SYSLOG_READ_ALL || type == SYSLOG_READ_CLEAR) { if(!buffer || len < 0) { return -EINVAL; } if(!len) { return 0; } if((errno = check_user_area(VERIFY_WRITE, buffer, len))) { return errno; } count = 0; if(type == SYSLOG_READ || type == SYSLOG_READ_CLEAR) { while(!log_new_chars) { sleep(&sys_syslog, PROC_INTERRUPTIBLE); if(current->sigpending & ~current->sigblocked) { return -ERESTART; } } n = (log_write - log_new_chars) & (LOG_BUF_LEN - 1); while(log_new_chars && count < len) { if(!log_buf[n]) { break; } c = log_buf[n]; log_new_chars--; n++; n &= LOG_BUF_LEN - 1; *buffer = c; buffer++; count++; } if(type == SYSLOG_READ_CLEAR) { log_new_chars = 0; } } else { len = MIN(LOG_BUF_LEN, len); len = MIN(log_size, len); n = log_read; while(len) { if(!log_buf[n]) { break; } c = log_buf[n]; n++; n &= LOG_BUF_LEN - 1; *buffer = c; buffer++; len--; count++; } } return count; } if(type == SYSLOG_CLEAR) { log_new_chars = 0; return 0; } if(type == SYSLOG_CONSOLE_OFF) { console_loglevel = 1; return 0; } if(type == SYSLOG_CONSOLE_ON) { console_loglevel = DEFAULT_CONSOLE_LOGLEVEL; return 0; } if(type == SYSLOG_CONSOLE_LEVEL) { if(len < 1 || len > 8) { return -EINVAL; } console_loglevel = len; return 0; } if(type == SYSLOG_SIZE_BUFFER) { return LOG_BUF_LEN; } return -EINVAL; } ================================================ FILE: kernel/syscalls/time.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/times.c ================================================ /* * 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 CURRENT_TICKS; } ================================================ FILE: kernel/syscalls/truncate.c ================================================ /* * 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; } free_name(tmp_name); if(S_ISDIR(i->i_mode)) { iput(i); return -EISDIR; } if(IS_RDONLY_FS(i)) { iput(i); return -EROFS; } if(check_permission(TO_WRITE, i) < 0) { iput(i); return -EACCES; } if(length == i->i_size) { iput(i); return 0; } if(i->fsop && i->fsop->truncate) { inode_lock(i); errno = i->fsop->truncate(i, length); inode_unlock(i); iput(i); return errno; } iput(i); return -EINVAL; } ================================================ FILE: kernel/syscalls/truncate64.c ================================================ /* * fiwix/kernel/syscalls/truncate64.c * * Copyright 2023, 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_truncate64(const char *path, __loff_t length) { struct inode *i; char *tmp_name; int errno; #ifdef __DEBUG__ printk("(pid %d) sys_truncate64(%s, %llu)\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; } free_name(tmp_name); if(S_ISDIR(i->i_mode)) { iput(i); return -EISDIR; } if(IS_RDONLY_FS(i)) { iput(i); return -EROFS; } if(check_permission(TO_WRITE, i) < 0) { iput(i); return -EACCES; } if((__off_t)length == i->i_size) { iput(i); return 0; } if(i->fsop && i->fsop->truncate) { inode_lock(i); errno = i->fsop->truncate(i, (__off_t)length); inode_unlock(i); iput(i); return errno; } iput(i); return -EINVAL; } ================================================ FILE: kernel/syscalls/umask.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/umount.c ================================================ /* * 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); } ================================================ FILE: kernel/syscalls/umount2.c ================================================ /* * fiwix/kernel/syscalls/umount2.c * * Copyright 2018-2022, 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 = { 0, 0 }; int sys_umount2(const char *target, int flags) { struct inode *i_target; struct mount *mp = 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(!(mp = 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 = mp->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, 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); del_mount_point(mp); unlock_resource(&umount_resource); return 0; } ================================================ FILE: kernel/syscalls/uname.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/unlink.c ================================================ /* * 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; } ================================================ FILE: kernel/syscalls/ustat.c ================================================ /* * fiwix/kernel/syscalls/ustat.c * * Copyright 2018-2022, 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, 0, sizeof(struct ustat)); ubuf->f_tfree = statfsbuf.f_bfree; ubuf->f_tinode = statfsbuf.f_ffree; return 0; } return -ENOSYS; } ================================================ FILE: kernel/syscalls/utime.c ================================================ /* * 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->state |= INODE_DIRTY; iput(i); free_name(tmp_name); return 0; } ================================================ FILE: kernel/syscalls/utimes.c ================================================ /* * fiwix/kernel/syscalls/utimes.c * * Copyright 2018-2023, Jordi Sanfeliu. All rights reserved. * Copyright 2023, Richard R. Masters. 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_utimes(const char *filename, struct timeval times[2]) { struct inode *i; char *tmp_name; int errno; #ifdef __DEBUG__ printk("(pid %d) sys_utimes('%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 timeval) * 2))) { 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[0].tv_sec; i->i_mtime = times[1].tv_sec; } i->i_ctime = CURRENT_TIME; i->state |= INODE_DIRTY; iput(i); free_name(tmp_name); return 0; } ================================================ FILE: kernel/syscalls/wait4.c ================================================ /* * fiwix/kernel/syscalls/wait4.c * * Copyright 2018-2021, 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) { p = p->next; 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) { p = p->next; 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); } } p = p->next; 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; } ================================================ FILE: kernel/syscalls/waitpid.c ================================================ /* * 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 ? *status : 0, options); #endif /*__DEBUG__ */ return sys_wait4(pid, status, options, NULL); } ================================================ FILE: kernel/syscalls/write.c ================================================ /* * 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_RDWR | 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->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; } ================================================ FILE: kernel/syscalls/writev.c ================================================ /* * fiwix/kernel/syscalls/writev.c * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Copyright 2023, Richard R. Masters. * Distributed under the terms of the Fiwix License. */ #include #include #include #ifdef __DEBUG__ #include #include #endif /*__DEBUG__ */ int sys_writev(int ufd, const struct iovec *iov, int iovcnt) { struct inode *i; int errno; int bytes_written = 0; int vi; /* vector index */ #ifdef __DEBUG__ printk("(pid %d) sys_writev(%d, 0x%08x, %d) -> ", current->pid, ufd, iov, iovcnt); #endif /*__DEBUG__ */ CHECK_UFD(ufd); if(iovcnt < 0 || iovcnt > UIO_MAXIOV) { return -EINVAL; } for (vi = 0; vi < iovcnt; vi++) { const struct iovec *io_write = &iov[vi]; if(!io_write->iov_len) { continue; } if((errno = check_user_area(VERIFY_READ, io_write->iov_base, io_write->iov_len))) { return errno; } if(fd_table[current->fd[ufd]].flags & O_RDONLY) { return -EBADF; } if(io_write->iov_len < 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]], io_write->iov_base, io_write->iov_len); if (errno < 0) { return errno; } bytes_written += errno; } else { return -EINVAL; } } #ifdef __DEBUG__ printk("%d\n", bytes_written); #endif /*__DEBUG__ */ return bytes_written; } ================================================ FILE: kernel/syscalls.c ================================================ /* * fiwix/kernel/syscalls.c * * Copyright 2018-2022, 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__ */ static int verify_address(int type, const void *addr, unsigned int size) { struct vma *vma; unsigned int start, gs; /* no need to verify anything if the caller is the kernel */ GET_GS(gs); if(gs == KERNEL_DS) { return 0; } /* * The vma_table of the INIT process is not setup yet when it * calls sys_open() and sys_execve() from init_trampoline(), * but these calls are trusted. */ if(!current->vma_table) { return 0; } start = (unsigned int)addr; if(!(vma = find_vma_region(start))) { /* * We need to check here if addr looks like a possible * non-existent user stack address. If so, just return 0 * and let 'do_page_fault()' to handle the imminent page * fault as soon as the kernel will try to access it. */ vma = current->vma_table->prev; if(vma) { if(vma->s_type == P_STACK) { if(start < vma->start && start > vma->prev->end) { return 0; } } } 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; } void free_name(const char *name) { kfree((unsigned int)name); } /* * This function has two objectives: * * 1. verifies the memory address validity of the char pointer supplied by the * user and, at the same time, limits its length to PAGE_SIZE (4096) bytes. * 2. creates a copy of 'string' in the kernel data space. */ int malloc_name(const char *string, char **name) { char *b; int n, errno; if((errno = verify_address(PROT_READ, string, 0))) { return errno; } if(!(b = (char *)kmalloc(PAGE_SIZE))) { return -ENOMEM; } *name = b; for(n = 0; n < PAGE_SIZE; n++) { if(!(*b = *string)) { return 0; } b++; string++; } 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) { return verify_address(type, addr, size); } int check_permission(int mask, struct inode *i) { __uid_t uid; if(current->flags & PF_USEREAL) { uid = current->uid; } else { uid = current->euid; } if(mask & TO_EXEC) { if(!(i->i_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) { return -EACCES; } } 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 i386 ABI system call (plus some from Linux 2.2 and Linux 2.4) */ 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_lchown, 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_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, #ifdef CONFIG_SYSVIPC sys_ipc, #else NULL, /* sys_ipc */ #endif /* CONFIG_SYSVIPC */ 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 */ sys_readv, /* 145 */ 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, /* 165 */ NULL, NULL, NULL, NULL, NULL, /* 170 */ NULL, NULL, NULL, NULL, NULL, /* 175 */ NULL, NULL, NULL, NULL, NULL, /* 180 */ NULL, sys_chown, sys_getcwd, NULL, NULL, /* 185 */ NULL, NULL, NULL, NULL, sys_fork, /* 190 (sys_vfork) */ NULL, #ifdef CONFIG_MMAP2 sys_mmap2, #else NULL, #endif sys_truncate64, sys_ftruncate64, sys_stat64, /* 195 */ sys_lstat64, sys_fstat64, NULL, NULL, NULL, /* 200 */ NULL, NULL, NULL, NULL, NULL, /* 205 */ NULL, NULL, NULL, NULL, NULL, /* 210 */ NULL, sys_chown32, NULL, NULL, NULL, /* 215 */ NULL, NULL, NULL, NULL, sys_getdents64, /* 220 */ sys_fcntl64, NULL, NULL, NULL, NULL, /* 225 */ NULL, NULL, NULL, NULL, NULL, /* 230 */ NULL, NULL, NULL, NULL, NULL, /* 235 */ NULL, NULL, NULL, NULL, NULL, /* 240 */ NULL, NULL, NULL, NULL, NULL, /* 245 */ NULL, NULL, NULL, NULL, NULL, /* 250 */ NULL, NULL, NULL, NULL, NULL, /* 255 */ NULL, NULL, NULL, NULL, NULL, /* 260 */ NULL, NULL, NULL, NULL, NULL, /* 265 */ NULL, NULL, NULL, NULL, NULL, /* 270 */ sys_utimes, }; 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. */ #ifdef CONFIG_SYSCALL_6TH_ARG int do_syscall(unsigned int num, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, struct sigcontext sc) #else int do_syscall(unsigned int num, int arg1, int arg2, int arg3, int arg4, int arg5, struct sigcontext sc) #endif /* CONFIG_SYSCALL_6TH_ARG */ { 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)≻ #ifdef CONFIG_SYSCALL_6TH_ARG return sys_func(arg1, arg2, arg3, arg4, arg5, arg6, &sc); #else return sys_func(arg1, arg2, arg3, arg4, arg5, &sc); #endif /* CONFIG_SYSCALL_6TH_ARG */ } ================================================ FILE: kernel/timer.c ================================================ /* * fiwix/kernel/timer.c * * Copyright 2018-2022, 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) */ #define LATCH (OSCIL / HZ) 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 struct bh timer_bh = { 0, &irq_timer_bh, NULL }; static struct bh callouts_bh = { 0, &do_callouts_bh, NULL }; static struct interrupt irq_config_timer = { 0, "timer", &irq_timer, NULL }; static unsigned int count_active_procs(void) { int counter; struct proc *p; counter = 0; FOR_EACH_PROCESS(p) { if(p->state == PROC_RUNNING || (p->state == PROC_SLEEPING && p->flags & PF_NOTINTERRUPT)) { counter += FIXED_1; } p = p->next; } 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; } } } void add_callout(struct callout_req *creq, unsigned int ticks) { unsigned 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, 0, 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); } void del_callout(struct callout_req *creq) { unsigned 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); } void irq_timer(int num, struct sigcontext *sc) { if((++CURRENT_TICKS % HZ) == 0) { CURRENT_TIME++; kstat.uptime++; } timer_bh.flags |= BH_ACTIVE; } unsigned int tv2ticks(const struct timeval *tv) { return((tv->tv_sec * HZ) + tv->tv_usec * HZ / 1000000); } void ticks2tv(int ticks, struct timeval *tv) { tv->tv_sec = ticks / HZ; tv->tv_usec = (ticks % HZ) * 1000000 / HZ; } 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 int mktime(struct tm *tm) { int n, total_days; unsigned int seconds; total_days = 0; for(n = UNIX_EPOCH; n < tm->tm_year; n++) { total_days += DAYS_PER_YEAR(n); } for(n = 0; n < (tm->tm_month - 1); n++) { total_days += month[n]; if(n == 1) { total_days += LEAP_YEAR(tm->tm_year) ? 1 : 0; } } total_days += (tm->tm_mday - 1); seconds = total_days * SECS_PER_DAY; seconds += tm->tm_hour * SECS_PER_HOUR; seconds += tm->tm_min * SECS_PER_MIN; seconds += tm->tm_sec; return seconds; } void irq_timer_bh(struct sigcontext *sc) { struct proc *p; 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); } } } 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->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); } } p = p->next; } /* callouts */ if(callout_head) { if(callout_head->expires > 0) { callout_head->expires--; if(!callout_head->expires) { callouts_bh.flags |= BH_ACTIVE; } } else { printk("%s(): callout losing ticks.\n", __FUNCTION__); callouts_bh.flags |= BH_ACTIVE; } } if(current->pid > IDLE && --current->cpu_count <= 0) { current->cpu_count = 0; need_resched = 1; } } void do_callouts_bh(struct sigcontext *sc) { struct callout *c; void (*fn)(unsigned int); unsigned int arg; while(callout_head) { if(callout_head->expires) { break; } if(!can_lock_area(AREA_CALLOUT)) { break; } 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 tm tm; /* read date and time from CMOS */ tm.tm_sec = cmos_read_date(CMOS_SEC); tm.tm_min = cmos_read_date(CMOS_MIN); tm.tm_hour = cmos_read_date(CMOS_HOUR); tm.tm_mday = cmos_read_date(CMOS_DAY); tm.tm_month = cmos_read_date(CMOS_MONTH); tm.tm_year = cmos_read_date(CMOS_YEAR); cmos_century = cmos_read_date(CMOS_CENTURY); tm.tm_year += cmos_century * 100; kstat.boot_time = CURRENT_TIME = mktime(&tm); } 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; } int gettimeoffset(void) { int count; count = pit_getcounter0(); count = (LATCH - count) * TICK; count /= LATCH; return count; } void timer_init(void) { int n; struct callout *c; add_bh(&timer_bh); add_bh(&callouts_bh); pit_init(HZ); memset_b(callout_pool, 0, 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\ttype=PIT Hz=%d\n", TIMER_IRQ, HZ); if(!register_irq(TIMER_IRQ, &irq_config_timer)) { enable_irq(TIMER_IRQ); } } ================================================ FILE: kernel/traps.c ================================================ /* * fiwix/kernel/traps.c * * Copyright 2018-2022, 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 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; } const char *elf_lookup_symbol(unsigned int addr) { Elf32_Shdr *vsymtab, *vstrtab; Elf32_Sym *sym; unsigned int n; vsymtab = (Elf32_Shdr *)P2V((unsigned int)symtab); vstrtab = (Elf32_Shdr *)P2V((unsigned int)strtab); sym = (Elf32_Sym *)P2V(vsymtab->sh_addr); for(n = 0; n < vsymtab->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 *)P2V(vstrtab->sh_addr) + sym->st_name; } } return NULL; } void stack_backtrace(void) { int n; unsigned int addr, *sp; const char *str; printk("Stack:\n"); GET_ESP(sp); /* eip, cs, eflags, oldesp and oldss cannot be counted here */ sp += (sizeof(struct sigcontext) / sizeof(unsigned int)) - 5; sp = (unsigned int *)P2V((unsigned int)sp); for(n = 1; n <= 32; n++) { printk(" %08x", *sp); sp++; if(!(n % 8)) { printk("\n"); } } printk("Kernel backtrace:\n"); GET_ESP(sp); /* eip, cs, eflags, oldesp and oldss cannot be counted here */ sp += (sizeof(struct sigcontext) / sizeof(unsigned int)) - 5; sp = (unsigned int *)P2V((unsigned int)sp); for(n = 0; n < 256; n++) { addr = *sp; str = elf_lookup_symbol(addr); if(str) { printk("<0x%08x> %s()\n", addr, str); } sp++; } } int dump_registers(unsigned int trap, struct sigcontext *sc) { unsigned int cr2; int errno; printk("\n"); if(trap == 14) { /* Page Fault */ GET_CR2(cr2); printk("%s at 0x%08x (%s) with error code 0x%02x%s", traps_table[trap].name, cr2, sc->err & PFAULT_W ? "writing" : "reading", sc->err, sc->err & PAGE_USER ? "\n" : " in kernel mode.\n"); } 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"); } errno = 0; if(current) { printk("Process '%s' with pid %d", current->argv0, current->pid); /* panic if the exception has been in kernel mode */ if(current->flags & PF_KPROC || sc->cs == KERNEL_CS) { errno = 1; } } else { printk("['current' is NULL!]"); errno = 1; } if(sc->cs == KERNEL_CS) { printk(" in '%s()'", elf_lookup_symbol(sc->eip)); } printk(".\n"); printk(" cs: 0x%04x\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%04x\t es: 0x%04x\t fs: 0x%04x\t gs: 0x%04x\n", sc->ds, sc->es, sc->fs, sc->gs); if(sc->cs == KERNEL_CS) { stack_backtrace(); } return errno; } ================================================ FILE: lib/Makefile ================================================ # fiwix/lib/Makefile # # Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. # Distributed under the terms of the Fiwix License. # .c.o: $(CC) $(CFLAGS) -c -o $@ $< OBJS = ctype.o string.o printk.o sysconsole.o all: $(OBJS) clean: rm -f *.o ================================================ FILE: lib/ctype.c ================================================ /* * 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 */ }; ================================================ FILE: lib/printk.c ================================================ /* * fiwix/lib/printk.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include #include #include #define MAX_BUF 1024 /* printk() and sprintk() size limit */ static char buf[MAX_BUF]; static char newline = 1; char log_buf[LOG_BUF_LEN]; /* circular buffer */ unsigned int log_read, log_write, log_size, log_new_chars; int console_loglevel = DEFAULT_CONSOLE_LOGLEVEL; static void puts(char *buffer, int msg_level) { struct tty *tty; int n; char *p, *l; #ifdef CONFIG_QEMU_DEBUGCON if(kstat.flags & KF_HAS_DEBUGCON) { p = buffer; while(*p) { if(p == buffer && strlen(buffer) > 3) { if(p[0] == '<' && p[1] >= '0' && p[1] <= '7' && p[2] == '>') { p = p + 3; } } if(msg_level < console_loglevel) { outport_b(QEMU_DEBUG_PORT, *(p++)); } } } #endif /* CONFIG_QEMU_DEBUGCON */ tty = NULL; for(n = 0; n < NR_SYSCONSOLES; n++) { if(!sysconsole_table[n].dev) { continue; } if(sysconsole_table[n].dev == MKDEV(VCONSOLES_MAJOR, 0)) { tty = get_tty(MKDEV(VCONSOLES_MAJOR, 0)); } else { tty = sysconsole_table[n].tty; } } l = p = buffer; while(*l) { if(tty && *p) { if(p == buffer && strlen(buffer) > 3) { if(p[0] == '<' && p[1] >= '0' && p[1] <= '7' && p[2] == '>') { p = p + 3; } } if(msg_level < console_loglevel) { charq_putchar(&tty->write_q, *p); } tty->output(tty); p++; } log_write &= LOG_BUF_LEN - 1; log_buf[log_write++] = *l; if(log_size < LOG_BUF_LEN) { log_size++; } else { log_read++; log_read &= LOG_BUF_LEN - 1; } log_new_chars = log_new_chars < LOG_BUF_LEN ? log_new_chars + 1 : log_new_chars; l++; } wakeup(&sys_syslog); wakeup(&do_select); } /* * 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 * * length modifiers (e.g: %ld or %lu) * -------------------------------------------------------- * l long long int arguments * * flags (e.g: %05d or %-6s) * -------------------------------------------------------- * 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) * the string is right-justified * (default is left-justified) */ static int do_printk(char *buffer, const char *format, va_list args) { char sw_neg, in_identifier, n_pad, lf, sw_l; char str[32 + 1], ch_pad, basecase, c; char nullstr[7] = { '<', 'N', 'U', 'L', 'L', '>', '\0' }; char *ptr_s, *p; int num, count, level_found; char simplechar; unsigned int unum, digit; long long int lnum; unsigned long long int lunum; static char msg_level = -1; sw_neg = in_identifier = n_pad = lf = sw_l = 0; count = 0; basecase = 'A'; ch_pad = ' '; p = NULL; /* * Checks the log level (e.g: <4>) on every new line and adds the * level mark (if needed), but only if the caller function was printk(). */ if(newline == 1) { if(msg_level < 0 && buffer == buf) { level_found = 0; if(strlen(format) > 3) { if(format[0] == '<' && format[1] >= '0' && format[1] <= '7' && format[2] == '>') { msg_level = format[1] - '0'; level_found = 1; } } if(!level_found) { msg_level = DEFAULT_MESSAGE_LOGLEVEL; *(buffer++) = '<'; *(buffer++) = msg_level + '0'; *(buffer++) = '>'; } } newline = 0; } /* assumes buffer has a maximum size of MAX_BUF */ while((c = *(format++)) && count < MAX_BUF) { if(!in_identifier) { memset_b(str, 0, sizeof(str)); } if((c != '%') && !in_identifier) { if(c == '\n') { newline = 1; msg_level = -1; } *(buffer++) = c; } else { in_identifier = 1; switch(c = *(format)) { case 'd': #ifdef CONFIG_PRINTK64 if(sw_l) { lnum = va_arg(args, long long int); if(lnum < 0) { lnum *= -1; sw_neg = 1; } ptr_s = str; do { *(ptr_s++) = '0' + (lnum % 10); } while(lnum /= 10); } else { #endif /* CONFIG_PRINTK64 */ 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); #ifdef CONFIG_PRINTK64 } #endif /* CONFIG_PRINTK64 */ 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; sw_l = 0; break; case 'u': #ifdef CONFIG_PRINTK64 if(sw_l) { lunum = va_arg(args, unsigned long long int); ptr_s = str; do { *(ptr_s++) = '0' + (lunum % 10); } while(lunum /= 10); } else { #endif /* CONFIG_PRINTK64 */ unum = va_arg(args, unsigned int); ptr_s = str; do { *(ptr_s++) = '0' + (unum % 10); } while(unum /= 10); #ifdef CONFIG_PRINTK64 } #endif /* CONFIG_PRINTK64 */ 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; sw_l = 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; sw_l = 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; sw_l = 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; sw_l = 0; break; case 'c': simplechar = va_arg(args, int); *(buffer++) = simplechar; format++; in_identifier = 0; lf = 0; sw_l = 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; } if(lf) { while(num-- > 0 && count < MAX_BUF) { *(buffer++) = ' '; count++; } } while((c = *(ptr_s++)) && count < MAX_BUF) { *(buffer++) = c; count++; } while(num-- > 0 && count < MAX_BUF) { *(buffer++) = ' '; count++; } format++; n_pad = 0; in_identifier = 0; lf = 0; sw_l = 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 'l': sw_l = 1; break; case '-': lf = 1; break; case '%': *(buffer++) = c; format++; in_identifier = 0; break; } } count++; } *buffer = 0; return msg_level; } void flush_log_buf(struct tty *tty) { int n; static char msg_level = -1; n = log_read; while(n < log_size) { if(msg_level < 0) { msg_level = log_buf[n + 1] - '0'; n = n + 3; } if(msg_level < console_loglevel) { if(charq_putchar(&tty->write_q, log_buf[n]) < 0) { tty->output(tty); continue; } } if(log_buf[n] == '\n') { msg_level = -1; } n++; } tty->output(tty); } void printk(const char *format, ...) { va_list args; int msg_level; va_start(args, format); msg_level = do_printk(buf, format, args); puts(buf, msg_level); va_end(args); } int sprintk(char *buffer, const char *format, ...) { va_list args; va_start(args, format); do_printk(buffer, format, args); newline = 1; va_end(args); return strlen(buffer); } int snprintk(char *str, unsigned int size, const char *format, ...) { va_list args; char *buffer; if(size > MAX_BUF) { printk("%(): size %d too big, truncating to MAX_BUF (%d bytes).", __FUNCTION__, size, MAX_BUF - 1); size = MAX_BUF - 1; } if(!(buffer = (char *)kmalloc(MAX_BUF))) { return 0; } va_start(args, format); sprintk(buffer, format, args); va_end(args); strncpy(str, buffer, size); str[size - 1] = '\0'; kfree((unsigned int)buffer); return strlen(str); } ================================================ FILE: lib/string.c ================================================ /* * fiwix/lib/string.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #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(PAGE_SIZE))) { 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] == '\0' || buf[n] == ' ') { buf[n] = 0; } 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 && *str1) { 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 = 0; /* 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 = 0; /* NULL-terminated */ } char *strcat(char *dest, const char *src) { char *orig; orig = dest; while(*dest) { dest++; } while(*src) { *dest = *src; dest++; src++; } *dest = 0; /* NULL-terminated */ 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 = 0; /* NULL-terminated */ return orig; } int strlen(const char *str) { int n; n = 0; while(str && *str) { n++; str++; } return n; } char *strchr(const char *str, int c) { while(*str) { if(*str == (char)c) { break; } str++; } return (char *)str; } char *strrchr(const char *str, int c) { int len; if((len = strlen(str))) { while(--len) { if(str[len] == (char)c) { break; } } } return (char *)(str + len); } int strtol(const char *nptr, char **endptr, int base) { int neg, value; value = neg = 0; while(ISSPACE((int)*nptr)) { nptr++; } if(*nptr == '-') { neg = 1; nptr++; } else if(*nptr == '+') { nptr++; } if(!base) { if(*nptr == '0') { base = 8; nptr++; if(*nptr == 'x' || *nptr == 'X') { base = 16; nptr++; } } else { base = 10; } } while(*nptr) { if(ISXDIGIT((int)*nptr)) { if(ISDIGIT((int)*nptr)) { if((*nptr - '0') < base) { value = (value * base) + (*nptr - '0'); } } else { value = (value * base) + (TOUPPER((*nptr - 'A') + 10)); } } else { break; } nptr++; } if(endptr) { *endptr = (char *)nptr; } return neg ? -value : value; } char *get_basename(const char *path) { char *basename; char c; basename = NULL; while(path) { while(*path == '/') { path++; } if(*path != '\0') { 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 = 0; p--; } return path; } int is_dir(const char *path) { while(*(path + 1)) { path++; } if(*path == '/') { return 1; } return 0; } int atoi(const char *str) { return strtol(str, (char **)NULL, 10); } void memcpy_b(void *dest, const void *src, unsigned int count) { unsigned char *d; const unsigned char *s; d = (unsigned char *)dest; s = (const unsigned char *)src; while(count--) { *d = *s; d++; s++; } } void memcpy_w(void *dest, const void *src, unsigned int count) { unsigned short int *d; const unsigned short int *s; d = (unsigned short int *)dest; s = (const unsigned short int *)src; while(count--) { *d = *s; d++; s++; } } void memcpy_l(void *dest, const void *src, unsigned int count) { unsigned int *d; const unsigned int *s; d = (unsigned int *)dest; s = (const 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++; } } int memcmp(const void *str1, const void *str2, unsigned int count) { const unsigned char *s1; const unsigned char *s2; s1 = (const unsigned char *)str1; s2 = (const unsigned char *)str2; while(count--) { if(*s1 != *s2) { return s1 < s2 ? -1 : 1; } s1++; s2++; } return 0; } void *memmove(void *dest, void const *src, int count) { if (dest < src) { memcpy_b (dest, src, count); return dest; } else { char *p = dest; char const *q = src; count = count - 1; while (count >= 0) { p[count] = q[count]; count = count - 1; } } return dest; } ================================================ FILE: lib/sysconsole.c ================================================ /* * fiwix/lib/sysconsole.c * * Copyright 2024, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include struct sysconsole sysconsole_table[NR_SYSCONSOLES]; int add_sysconsoledev(__dev_t dev) { int n; for(n = 0; n < NR_SYSCONSOLES; n++) { if(!sysconsole_table[n].dev) { sysconsole_table[n].dev = dev; return 1; } } return 0; } void register_console(struct tty *tty) { int n; for(n = 0; n < NR_SYSCONSOLES; n++) { if(sysconsole_table[n].dev == tty->dev) { sysconsole_table[n].tty = tty; break; } } } void sysconsole_init(void) { memset_b(sysconsole_table, 0, sizeof(sysconsole_table)); memset_b(log_buf, 0, sizeof(log_buf)); log_read = log_write = log_size = log_new_chars = 0; } ================================================ FILE: mm/Makefile ================================================ # fiwix/mm/Makefile # # Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. # Distributed under the terms of the Fiwix License. # .c.o: $(CC) $(CFLAGS) -c -o $@ $< OBJS = bios_map.o buddy_low.o memory.o page.o alloc.o fault.o mmap.o swapper.o all: $(OBJS) clean: rm -f *.o ================================================ FILE: mm/alloc.c ================================================ /* * fiwix/mm/alloc.c * * Copyright 2018, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include /* * The kmalloc() function acts like a front-end for the two * memory allocators currently supported: * * - buddy_low() for requests up to 2048KB. * - get_free_page() rest of requests up to PAGE_SIZE. */ unsigned int kmalloc(__size_t size) { struct page *pg; int max_size; unsigned int addr; /* check if size can be managed by buddy_low */ max_size = bl_blocksize[BUDDY_MAX_LEVEL - 1]; if(size + sizeof(struct bl_head) <= max_size) { size += sizeof(struct bl_head); return bl_malloc(size); } /* FIXME: pending to implement buddy_high */ if(size > PAGE_SIZE) { printk("WARNING: %s(): size (%d) is bigger than PAGE_SIZE!\n", __FUNCTION__, size); return 0; } if((pg = get_free_page())) { addr = pg->page << PAGE_SHIFT; return P2V(addr); } /* out of memory! */ return 0; } void kfree(unsigned int addr) { struct page *pg; unsigned paddr; paddr = V2P(addr); pg = &page_table[paddr >> PAGE_SHIFT]; if(pg->flags & PAGE_BUDDYLOW) { bl_free(addr); } else { release_page(pg); } } ================================================ FILE: mm/bios_map.c ================================================ /* * fiwix/mm/bios_map.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include struct bios_mem_map bios_mem_map[NR_BIOS_MM_ENT]; struct bios_mem_map kernel_mem_map[NR_BIOS_MM_ENT]; static char *bios_mem_type[] = { NULL, "available", "reserved", "ACPI Reclaim", "ACPI NVS", "unusable", "disabled" }; static void bios_map_add(unsigned int from, unsigned int to, int from_type, int to_type) { int n; for(n = 0; n < NR_BIOS_MM_ENT; n++) { if(!kernel_mem_map[n].type) { if(from_type == to_type) { printk("memory 0x%08x%08x-0x%08x%08x %s\n", 0, from, 0, to - 1, bios_mem_type[to_type] ); } else { printk("memory 0x%08x%08x-0x%08x%08x %s -> %s\n", 0, from, 0, to - 1, bios_mem_type[from_type], bios_mem_type[to_type] ); } kernel_mem_map[n].from = from; kernel_mem_map[n].to = to; kernel_mem_map[n].from_hi = 0; kernel_mem_map[n].to_hi = 0; kernel_mem_map[n].type = to_type; break; } } if(n >= NR_BIOS_MM_ENT) { printk("WARNING: %s(): no more entries in kernel_mem_map[].\n", __FUNCTION__); return; } } /* check if an specific address is available in the BIOS memory map */ int is_addr_in_bios_map(unsigned int addr) { int n, retval; struct bios_mem_map *bmm; retval = 0; bmm = &kernel_mem_map[0]; for(n = 0; n < NR_BIOS_MM_ENT; n++, bmm++) { if(bmm->to && bmm->type == MULTIBOOT_MEMORY_AVAILABLE && !bmm->from_hi && !bmm->to_hi) { if(addr >= bmm->from && addr < (bmm->to & PAGE_MASK)) { retval = 1; } } } /* this second pass is necessary because the array is not sorted */ bmm = &kernel_mem_map[0]; for(n = 0; n < NR_BIOS_MM_ENT; n++, bmm++) { if(bmm->to && bmm->type == MULTIBOOT_MEMORY_RESERVED && !bmm->from_hi && !bmm->to_hi) { if(addr >= bmm->from && addr < (bmm->to & PAGE_MASK)) { retval = 0; } } } return retval; } void bios_map_reserve(unsigned int from, unsigned int to) { if(is_addr_in_bios_map(from)) { bios_map_add(from, to, MULTIBOOT_MEMORY_AVAILABLE, MULTIBOOT_MEMORY_RESERVED); if (page_table_size) { reserve_pages(from, to); } } } void bios_map_init(struct multiboot_mmap_entry *bmmap_addr, unsigned int bmmap_length) { struct multiboot_mmap_entry *bmmap; unsigned int from_high, from_low, to_high, to_low; unsigned long long to, to_orig; int n, type; bmmap = bmmap_addr; kstat.physical_pages = 0; if(bmmap) { n = 0; while((unsigned int)bmmap < (unsigned int)bmmap_addr + bmmap_length) { from_high = (unsigned int)(bmmap->addr >> 32); from_low = (unsigned int)(bmmap->addr & 0xFFFFFFFF); to_orig = (bmmap->addr + bmmap->len); /* preserve original end address */ to = (bmmap->addr + bmmap->len) - 1; to_high = (unsigned int)(to >> 32); to_low = (unsigned int)(to & 0xFFFFFFFF); type = (int)bmmap->type; printk("%s 0x%08x%08x-0x%08x%08x %s\n", n ? " " : "memory", from_high, from_low, to_high, to_low, bios_mem_type[type] ); /* restore the original end address */ to_high = (unsigned int)(to_orig >> 32); to_low = (unsigned int)(to_orig & 0xFFFFFFFF); if(n < NR_BIOS_MM_ENT && bmmap->len) { bios_mem_map[n].from = from_low; bios_mem_map[n].from_hi = from_high; bios_mem_map[n].to = to_low; bios_mem_map[n].to_hi = to_high; bios_mem_map[n].type = type; /* only memory addresses below 4GB are counted */ if(!from_high && !to_high) { if(type == MULTIBOOT_MEMORY_AVAILABLE) { from_low &= PAGE_MASK; to_low &= PAGE_MASK; /* the first MB is not counted here */ if(from_low >= 0x100000) { kstat.physical_pages += (to_low - from_low) / PAGE_SIZE; } } } n++; } bmmap = (struct multiboot_mmap_entry *)((unsigned int)bmmap + bmmap->size + sizeof(bmmap->size)); } kstat.physical_pages += (1024 >> 2); /* add the first MB as a whole */ if(kstat.physical_pages > (GDT_BASE >> PAGE_SHIFT)) { printk("WARNING: detected a total of %dMB of available memory below 4GB.\n", (kstat.physical_pages << 2) / 1024); } } else { printk("WARNING: your BIOS has not provided a memory map.\n"); bios_mem_map[0].from = 0; bios_mem_map[0].to = kparms.memsize * 1024; bios_mem_map[0].from_hi = 0; bios_mem_map[0].to_hi = 0; bios_mem_map[0].type = MULTIBOOT_MEMORY_AVAILABLE; bios_mem_map[1].from = 0x00100000; bios_mem_map[1].to = (kparms.extmemsize + 1024) * 1024; bios_mem_map[1].from_hi = 0; bios_mem_map[1].to_hi = 0; bios_mem_map[1].type = MULTIBOOT_MEMORY_AVAILABLE; kstat.physical_pages = (kparms.extmemsize + 1024) >> 2; } /* * Truncate physical memory to upper kernel address space size (1GB or 2GB), since * currently all memory is permanently mapped there. */ if(kstat.physical_pages > (GDT_BASE >> PAGE_SHIFT)) { kstat.physical_pages = (GDT_BASE >> PAGE_SHIFT); printk("WARNING: only up to %dGB of physical memory will be used.\n", GDT_BASE >> 30); } memcpy_b(kernel_mem_map, bios_mem_map, NR_BIOS_MM_ENT * sizeof(struct bios_mem_map)); } ================================================ FILE: mm/buddy_low.c ================================================ /* * fiwix/mm/buddy_low.c * * Copyright 2022, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ /* * This buddy algorithm is intended to handle memory requests smaller * than a PAGE_SIZE. */ #include #include #include #include static struct bl_head *freelist[BUDDY_MAX_LEVEL + 1]; static struct bl_head *get_buddy(struct bl_head *block) { int mask; mask = 1 << (block->level + 5); return (struct bl_head *)((unsigned int)block ^ mask); } static void deallocate(struct bl_head *block) { struct bl_head **h, *buddy, *p; struct page *pg; unsigned int addr, paddr; int level; level = block->level; buddy = get_buddy(block); p = freelist[level]; while(p != NULL) { if(p == buddy) { break; } p = p->next; } if(p == buddy) { /* remove buddy from its free list */ if(buddy->next) { buddy->next->prev = buddy->prev; } if(buddy->prev) { buddy->prev->next = buddy->next; } if(buddy == freelist[level]) { freelist[level] = buddy->next; } /* deallocate block and its buddy as one single block */ if(level < BUDDY_MAX_LEVEL - 1) { if(block > buddy) { buddy->level++; deallocate(buddy); } else { block->level++; deallocate(block); } } if(level == BUDDY_MAX_LEVEL - 1) { addr = (unsigned int)block; paddr = V2P(addr); pg = &page_table[paddr >> PAGE_SHIFT]; pg->flags &= ~PAGE_BUDDYLOW; kfree(addr); kstat.buddy_low_num_pages--; } } else { /* buddy not free, put block on its free list */ h = &freelist[level]; if(!*h) { *h = block; block->prev = block->next = NULL; } else { block->next = *h; block->prev = NULL; (*h)->prev = block; *h = block; } } } static struct bl_head *allocate(int size) { struct bl_head *block, *buddy; struct page *pg; unsigned int addr, paddr; int level; for(level = 0; bl_blocksize[level] < size; level++); if(level == BUDDY_MAX_LEVEL) { if((addr = kmalloc(PAGE_SIZE))) { paddr = V2P(addr); pg = &page_table[paddr >> PAGE_SHIFT]; pg->flags |= PAGE_BUDDYLOW; block = (struct bl_head *)addr; } else { printk("WARNING: %s(): not enough memory!\n", __FUNCTION__); return NULL; } kstat.buddy_low_num_pages++; block->prev = block->next = NULL; return block; } if(freelist[level] != NULL) { /* we have a block on freelist */ block = freelist[level]; if(block->next) { block->next->prev = block->prev; } if(block->prev) { block->prev->next = block->next; } freelist[level] = block->next; } else { /* split a bigger block */ block = allocate(bl_blocksize[level + 1]); if(block != NULL) { /* put the buddy on the free list */ block->level = level; buddy = get_buddy(block); buddy->level = level; buddy->prev = buddy->next = NULL; freelist[level] = buddy; } } return block; } unsigned int bl_malloc(__size_t size) { struct bl_head *block; int level; for(level = 0; bl_blocksize[level] < size; level++); kstat.buddy_low_count[level]++; kstat.buddy_low_mem_requested += bl_blocksize[level]; block = allocate(size); return block ? (unsigned int)(block + 1) : 0; } void bl_free(unsigned int addr) { struct bl_head *block; int level; block = (struct bl_head *)addr; block--; level = block->level; kstat.buddy_low_count[level]--; kstat.buddy_low_mem_requested -= bl_blocksize[level]; deallocate(block); } void buddy_low_init(void) { memset_b(freelist, 0, sizeof(freelist)); } ================================================ FILE: mm/fault.c ================================================ /* * fiwix/mm/fault.c * * Copyright 2018-2022, 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 /* send the SIGSEGV signal to the ofending process */ static void send_sigsegv(struct sigcontext *sc) { #if defined(CONFIG_VERBOSE_SEGFAULTS) dump_registers(14, sc); printk("Memory map:\n"); show_vma_regions(current); #endif /* CONFIG_VERBOSE_SEGFAULTS */ 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 pde, pte, addr; struct page *pg; int page; 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 copy-on-write 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(!(addr = kmalloc(PAGE_SIZE))) { printk("%s(): not enough memory!\n", __FUNCTION__); return 1; } current->rss++; memcpy_b((void *)addr, (void *)P2V((page << PAGE_SHIFT)), PAGE_SIZE); pgtbl[pte] = V2P(addr) | 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 copy-on-write 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 addr, file_offset; struct page *pg; if(!vma) { if(cr2 >= (sc->oldesp - 32) && cr2 < PAGE_OFFSET) { if(!(vma = find_vma_region(PAGE_OFFSET - 1))) { printk("WARNING: %s(): process %d doesn't have an stack region in vma_table!\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_lock(pg); addr = (unsigned int)pg->data; page_unlock(pg); } } if(!pg) { if(!(addr = map_page(current, cr2, 0, vma->prot))) { printk("%s(): Oops, map_page() returned 0!\n", __FUNCTION__); return 1; } pg = &page_table[V2P(addr) >> PAGE_SHIFT]; if(bread_page(pg, vma->inode, file_offset, vma->prot, vma->flags)) { unmap_page(cr2); return 1; } current->usage.ru_majflt++; } } else { current->usage.ru_minflt++; addr = 0; #ifdef CONFIG_SYSVIPC if(vma->s_type == P_SHM) { if(shm_map_page(vma, cr2)) { return 1; } } #endif /* CONFIG_SYSVIPC */ } if(vma->flags & ZERO_PAGE) { if(!addr) { if(!(addr = map_page(current, cr2, 0, vma->prot))) { printk("%s(): Oops, map_page() returned 0!\n", __FUNCTION__); return 1; } } memset_b((void *)(addr & PAGE_MASK), 0, 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| U1 K1| U1 K1| * |doesn't have | U2 | K2| K2| U2 | U2 K2| U2 K2| * |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? * (!vma page in user-mode, page-fault during read or write) * * K1 - !vma + kernel + PF + (read | write) -> STACK? * (!vma page in kernel-mode, page-fault during read or write) * K2 - !vma + kernel + PV + (read | write) -> PANIC * (!vma page in kernel-mode, page-violation during read or write) */ void do_page_fault(unsigned int trap, struct sigcontext *sc) { unsigned int cr2; struct vma *vma; int panic; 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; /* in kernel mode */ } else { /* * The kernel may incur in a page fault when trying to * access a possible user stack address. In that case, * sc->oldesp doesn't point to the user stack, but to * the kernel stack, because the page fault was raised * in kernel mode. * We need to get the original user sigcontext struct * from the current kernel stack, in order to obtain * the user stack pointer sc->oldesp, and see if CR2 * looks like a user stack address. */ struct sigcontext *usc; /* * Since the page fault was raised in kernel mode, the * exception occurred at the same privilege level, hence * the %ss and %esp registers were not saved. */ usc = (struct sigcontext *)((unsigned int *)sc->esp + 16); usc += 1; /* does it look like a user stack address? */ if(cr2 >= (usc->oldesp - 32) && cr2 < PAGE_OFFSET) { if((!page_not_present(vma, cr2, usc))) { return; } } /* no */ } } panic = dump_registers(trap, sc); show_vma_regions(current); if(panic) { PANIC(""); } do_exit(SIGTERM); } ================================================ FILE: mm/memory.c ================================================ /* * fiwix/mm/memory.c * * Copyright 2018-2023, Jordi Sanfeliu. All rights reserved. * Portions Copyright 2024, Greg Haerr. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define KERNEL_TEXT_SIZE ((int)_etext - (PAGE_OFFSET + KERNEL_ADDR)) #define KERNEL_DATA_SIZE ((int)_edata - (int)_etext) #define KERNEL_BSS_SIZE ((int)_end - (int)_edata) unsigned int *kpage_dir; unsigned int proc_table_size = 0; unsigned int buffer_hash_table_size = 0; unsigned int inode_table_size = 0; unsigned int inode_hash_table_size = 0; unsigned int fd_table_size = 0; unsigned int page_table_size = 0; unsigned int page_hash_table_size = 0; unsigned int map_kaddr(unsigned int *page_dir, unsigned int from, unsigned int to, unsigned int addr, int flags) { unsigned int n; unsigned int paddr; unsigned int *pgtbl; unsigned int pde, pte; paddr = addr; for(n = from; n < to; n += PAGE_SIZE) { pde = GET_PGDIR(n); pte = GET_PGTBL(n); if(!(page_dir[pde] & ~PAGE_MASK)) { if (!addr) { paddr = kmalloc(PAGE_SIZE); if (!paddr) { printk("%s(): no memory\n", __FUNCTION__); return 0; } paddr = V2P(paddr); } page_dir[pde] = paddr | flags; memset_b((void *)(paddr + PAGE_OFFSET), 0, PAGE_SIZE); paddr += PAGE_SIZE; } pgtbl = (unsigned int *)((page_dir[pde] & PAGE_MASK) + PAGE_OFFSET); pgtbl[pte] = n | flags; } return paddr; } void bss_init(void) { memset_b((void *)((int)_edata), 0, KERNEL_BSS_SIZE); } /* * This function creates a Page Directory covering all physical memory * pages and places it at the end of the memory. This ensures that it * won't be clobbered by a large initrd image. * * It returns the address of the PD to be activated by the CR3 register. */ unsigned int setup_tmp_pgdir(unsigned int magic, unsigned int info) { int n, pd; unsigned int addr, memksize; unsigned int *pgtbl; struct multiboot_info *mbi; if(magic != MULTIBOOT_BOOTLOADER_MAGIC) { /* 4MB of memory assumed */ memksize = 4096 - 1024; /* mem_upper */ } else { mbi = (struct multiboot_info *)(PAGE_OFFSET + info); if(!(mbi->flags & MULTIBOOT_INFO_MEMORY)) { /* 4MB of memory assumed */ memksize = 4096 - 1024; /* mem_upper */ } else { memksize = (unsigned int)mbi->mem_upper; /* CONFIG_VM_SPLIT22 marks the maximum physical memory supported */ if(memksize > ((0xFFFFFFFF - PAGE_OFFSET) / 1024)) { memksize = (0xFFFFFFFF - PAGE_OFFSET) / 1024; } } } addr = PAGE_OFFSET + (memksize * 1024) - memksize; addr = PAGE_ALIGN(addr); kpage_dir = (unsigned int *)addr; memset_b(kpage_dir, 0, PAGE_SIZE); addr += PAGE_SIZE; pgtbl = (unsigned int *)addr; memset_b(pgtbl, 0, memksize); for(n = 0; n < (memksize + 1024) / sizeof(unsigned int); n++) { pgtbl[n] = (n << PAGE_SHIFT) | PAGE_PRESENT | PAGE_RW; if(!(n % 1024)) { pd = n / 1024; kpage_dir[pd] = (unsigned int)(addr + (PAGE_SIZE * pd) + GDT_BASE) | PAGE_PRESENT | PAGE_RW; kpage_dir[GET_PGDIR(PAGE_OFFSET) + pd] = (unsigned int)(addr + (PAGE_SIZE * pd) + GDT_BASE) | PAGE_PRESENT | PAGE_RW; } } return (unsigned int)kpage_dir - PAGE_OFFSET; } /* 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_addr, c_addr; unsigned int n, 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_table; pages = 0; while(vma) { if(vma->flags & MAP_SHARED) { vma = vma->next; continue; } for(n = vma->start; n < vma->end; n += PAGE_SIZE) { pde = GET_PGDIR(n); pte = GET_PGTBL(n); if(src_pgdir[pde] & PAGE_PRESENT) { src_pgtbl = (unsigned int *)P2V((src_pgdir[pde] & PAGE_MASK)); if(!(dst_pgdir[pde] & PAGE_PRESENT)) { if(!(c_addr = kmalloc(PAGE_SIZE))) { printk("%s(): returning 0!\n", __FUNCTION__); return 0; } current->rss++; pages++; dst_pgdir[pde] = V2P(c_addr) | PAGE_PRESENT | PAGE_RW | PAGE_USER; memset_b((void *)c_addr, 0, PAGE_SIZE); } dst_pgtbl = (unsigned int *)P2V((dst_pgdir[pde] & PAGE_MASK)); if(src_pgtbl[pte] & PAGE_PRESENT) { if (src_pgtbl[pte] & PAGE_NOALLOC) { dst_pgtbl[pte] = src_pgtbl[pte]; continue; } p_addr = src_pgtbl[pte] >> PAGE_SHIFT; pg = &page_table[p_addr]; if(pg->flags & PAGE_RESERVED) { continue; } src_pgtbl[pte] &= ~PAGE_RW; /* mark writable pages as copy-on-write */ if(vma->prot & PROT_WRITE) { pg->flags |= PAGE_COW; } dst_pgtbl[pte] = src_pgtbl[pte]; if(!is_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++; } } } vma = vma->next; } return pages; } int free_page_tables(struct proc *p) { unsigned int *pgdir; 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] = 0; count++; } } return count; } unsigned int map_page(struct proc *p, unsigned int vaddr, unsigned int addr, unsigned int prot) { return map_page_flags(p, vaddr, addr, prot, 0); } unsigned int map_page_flags(struct proc *p, unsigned int vaddr, unsigned int addr, unsigned int prot, int flags) { unsigned int *pgdir, *pgtbl; unsigned int newaddr; 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(!(newaddr = kmalloc(PAGE_SIZE))) { return 0; } p->rss++; pgdir[pde] = V2P(newaddr) | PAGE_PRESENT | PAGE_RW | PAGE_USER; memset_b((void *)newaddr, 0, PAGE_SIZE); } pgtbl = (unsigned int *)P2V((pgdir[pde] & PAGE_MASK)); if(!(pgtbl[pte] & PAGE_PRESENT)) { /* allocating page */ if(!addr) { if(!(addr = kmalloc(PAGE_SIZE))) { return 0; } addr = V2P(addr); p->rss++; } pgtbl[pte] = addr | PAGE_PRESENT | PAGE_USER | flags; } if(prot & PROT_WRITE) { pgtbl[pte] |= PAGE_RW; } return P2V(addr); } int unmap_page(unsigned int vaddr) { unsigned int *pgdir, *pgtbl; unsigned int addr, desc; 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; } desc = pgtbl[pte]; addr = desc & PAGE_MASK; pgtbl[pte] = 0; if (!(desc & PAGE_NOALLOC)) { kfree(P2V(addr)); } current->rss--; return 0; } /* * This function initializes and setups the kernel page directory and page * tables. It also reserves areas of contiguous memory spaces for internal * structures and for the RAMdisk drives. */ void mem_init(void) { unsigned int sizek; unsigned int physical_memory, physical_page_tables; unsigned int *pgtbl; int n, pages, last_ramdisk; physical_page_tables = (kstat.physical_pages / 1024) + ((kstat.physical_pages % 1024) ? 1 : 0); physical_memory = (kstat.physical_pages << PAGE_SHIFT); /* in bytes */ /* align _last_data_addr to the next page */ _last_data_addr = PAGE_ALIGN(_last_data_addr); /* Page Directory */ kpage_dir = (unsigned int *)_last_data_addr; memset_b(kpage_dir, 0, PAGE_SIZE); _last_data_addr += PAGE_SIZE; /* Page Tables */ pgtbl = (unsigned int *)_last_data_addr; memset_b(pgtbl, 0, 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++) { pgtbl[n] = (n << PAGE_SHIFT) | PAGE_PRESENT | PAGE_RW; if(!(n % 1024)) { kpage_dir[GET_PGDIR(PAGE_OFFSET) + (n / 1024)] = (unsigned int)&pgtbl[n] | PAGE_PRESENT | PAGE_RW; } } activate_kpage_dir(); /* since Page Directory is now activated we can use virtual addresses */ kpage_dir = (unsigned int *)P2V((unsigned int)kpage_dir); _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(!is_addr_in_bios_map(V2P(_last_data_addr) + proc_table_size)) { PANIC("Not enough memory for proc_table.\n"); } proc_table = (struct proc *)_last_data_addr; _last_data_addr += proc_table_size; /* reserve memory space for buffer_hash_table */ kstat.max_buffers_size = kstat.physical_pages * (PAGE_SIZE / 1024); kstat.max_buffers_size = (kstat.max_buffers_size * BUFFER_PERCENTAGE) / 100; n = (kstat.max_buffers_size * 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; if(!is_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; /* calculate the inode table size */ 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; /* reserve memory space for inode_hash_table */ kstat.max_inodes = inode_table_size / sizeof(struct inode); n = (kstat.max_inodes * INODE_HASH_PERCENTAGE) / 100; n = MAX(n, 10); /* 10 inode hash buckets 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; if(!is_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); if(!is_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 RAMdisk drives */ last_ramdisk = 0; if(kparms.ramdisksize > 0 || ramdisk_table[0].addr) { /* * If the 'initrd=' parameter was supplied, then the first * RAMdisk drive was already assigned to the initrd image. */ if(ramdisk_table[0].addr) { ramdisk_table[0].addr += PAGE_OFFSET; last_ramdisk = 1; } for(; last_ramdisk < ramdisk_minors; last_ramdisk++) { if(!is_addr_in_bios_map(V2P(_last_data_addr) + (kparms.ramdisksize * 1024))) { kparms.ramdisksize = 0; ramdisk_minors -= RAMDISK_DRIVES; printk("WARNING: RAMdisk drive disabled (not enough physical memory).\n"); break; } ramdisk_table[last_ramdisk].addr = (char *)_last_data_addr; ramdisk_table[last_ramdisk].size = kparms.ramdisksize; _last_data_addr += kparms.ramdisksize * 1024; } } /* * FIXME: this is ugly! * It should go in console_init() once we have a proper kernel memory/page management. */ #include for(n = 1; n <= NR_VCONSOLES; n++) { vc_screen[n] = (short int *)_last_data_addr; _last_data_addr += (video.columns * video.lines * 2); } /* * FIXME: this is ugly! * It should go in console_init() once we have a proper kernel memory/page management. */ vcbuf = (short int *)_last_data_addr; _last_data_addr += (video.columns * video.lines * SCREENS_LOG * 2 * sizeof(short int)); #ifdef CONFIG_KEXEC if(kexec_size > 0) { bios_map_reserve(KEXEC_BOOT_ADDR, KEXEC_BOOT_ADDR + (PAGE_SIZE * 2)); ramdisk_minors++; if(last_ramdisk < ramdisk_minors) { if(!is_addr_in_bios_map(V2P(_last_data_addr) + (kexec_size * 1024))) { kexec_size = 0; ramdisk_minors--; printk("WARNING: RAMdisk drive for kexec disabled (not enough physical memory).\n"); } else { ramdisk_table[last_ramdisk].addr = (char *)_last_data_addr; ramdisk_table[last_ramdisk].size = kexec_size; _last_data_addr += kexec_size * 1024; } } } #endif /* CONFIG_KEXEC */ /* the last one must be the page_table structure */ n = (kstat.physical_pages * PAGE_HASH_PER_10K) / 10000; n = MAX(n, 1); /* 1 page for the hash table as minimum */ n = MIN(n, MAX_PAGES_HASH); page_hash_table_size = n * PAGE_SIZE; if(!is_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; _last_data_addr += page_hash_table_size; page_table_size = PAGE_ALIGN(kstat.physical_pages * sizeof(struct page)); if(!is_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; _last_data_addr += page_table_size; page_init(kstat.physical_pages); buddy_low_init(); } void mem_stats(void) { kstat.kernel_reserved <<= 2; kstat.physical_reserved <<= 2; printk("\n"); printk("memory: total=%dKB, user=%dKB, kernel=%dKB, reserved=%dKB\n", kstat.physical_pages << 2, kstat.total_mem_pages << 2, kstat.kernel_reserved, kstat.physical_reserved); printk("tables: procs=%d (%dKB), opens=%d (%dKB), pages=%dKB, inodes=%d\n", NR_PROCS, proc_table_size / 1024, NR_OPENS, fd_table_size / 1024, page_table_size / 1024, kstat.max_inodes); printk("hash tables: buffers=%d (%dKB), inodes=%d (%dKB), pages=%d (%dKB)\n", buffer_hash_table_size / sizeof(unsigned int), buffer_hash_table_size / 1024, inode_hash_table_size / sizeof(unsigned int), inode_hash_table_size / 1024, page_hash_table_size / sizeof(unsigned int), page_hash_table_size / 1024); printk("kernel: text=%dKB, data=%dKB, bss=%dKB\n\n", KERNEL_TEXT_SIZE / 1024, KERNEL_DATA_SIZE / 1024, KERNEL_BSS_SIZE / 1024); } ================================================ FILE: mm/mmap.c ================================================ /* * fiwix/mm/mmap.c * * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved. * Portions Copyright 2024, Greg Haerr. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include #include #include void merge_vma_regions(struct vma *, struct vma *); 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; if(!p) { return; } vma = p->vma_table; n = 0; printk("num address range flag offset dev inode mod section cnt\n"); printk("---- --------------------- ---- ---------- ----- ---------- --- ------- ----\n"); while(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; #ifdef CONFIG_SYSVIPC case P_SHM: section = "shm "; break; #endif /* CONFIG_SYSVIPC */ 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); vma = vma->next; n++; } if(!n) { printk("[no vma regions]\n"); } } /* insert a vma structure into vma_table sorted by address */ static void insert_vma_region(struct vma *vma) { struct vma *vmat; vmat = current->vma_table; while(vmat) { if(vmat->start > vma->start) { break; } vmat = vmat->next; } if(!vmat) { /* append */ vma->prev = current->vma_table->prev; current->vma_table->prev->next = vma; current->vma_table->prev = vma; } else { /* insert */ vma->prev = vmat->prev; vma->next = vmat; if(vmat == current->vma_table) { /* insert in the head */ current->vma_table = vma; } else { /* insert in the middle */ vmat->prev->next = vma; } vmat->prev = vma; } if(vma->inode) { vma->inode->count++; } if(vma != vma->prev && vma->start >= vma->prev->start && vma->start <= vma->prev->end) { merge_vma_regions(vma->prev, vma); } } static void add_vma_region(struct vma *vma) { unsigned int flags; SAVE_FLAGS(flags); CLI(); if(!current->vma_table) { current->vma_table = vma; current->vma_table->prev = vma; if(vma->inode) { vma->inode->count++; } } else { insert_vma_region(vma); } RESTORE_FLAGS(flags); } static void del_vma_region(struct vma *vma) { unsigned int flags; struct vma *tmp; tmp = vma; if(!vma->next && !vma->prev) { printk("WARNING: %s(): trying to delete an unexistent vma region (%x).\n", __FUNCTION__, vma->start); return; } SAVE_FLAGS(flags); CLI(); if(vma->next) { vma->next->prev = vma->prev; } if(vma->prev) { if(vma != current->vma_table) { vma->prev->next = vma->next; } } if(!vma->next) { current->vma_table->prev = vma->prev; } if(vma == current->vma_table) { current->vma_table = vma->next; } RESTORE_FLAGS(flags); kfree((unsigned int)tmp); } static int can_be_merged(struct vma *a, struct vma *b) { if((a->end == b->start) && (a->prot == b->prot) && (a->flags == b->flags) && (a->offset == b->offset) && (a->s_type == b->s_type) && #ifdef CONFIG_SYSVIPC (a->s_type != P_SHM) && #endif /* CONFIG_SYSVIPC */ (a->inode == b->inode)) { return 1; } return 0; } static int free_vma_region(struct vma *vma, unsigned int start, __ssize_t length) { struct vma *new; if(start + length < vma->end) { if(!(new = (struct vma *)kmalloc(sizeof(struct vma)))) { return -ENOMEM; } memset_b(new, 0, sizeof(struct vma)); 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; } else { new = NULL; } if(vma->start == start) { if(vma->inode) { iput(vma->inode); } del_vma_region(vma); } else { vma->end = start; } if(new) { add_vma_region(new); } return 0; } /* this assumes that vma_table is sorted by address */ void merge_vma_regions(struct vma *a, struct vma *b) { struct vma *new; if(b->start == a->end) { if(can_be_merged(a, b)) { a->end = b->end; del_vma_region(b); return; } } if((b->start < a->end)) { if(!(new = (struct vma *)kmalloc(sizeof(struct vma)))) { return; } memset_b(new, 0, sizeof(struct vma)); new->start = b->end; new->end = a->end; new->prot = a->prot; new->flags = a->flags; new->offset = a->offset; new->s_type = a->s_type; new->inode = a->inode; new->o_mode = a->o_mode; free_vma_pages(a, b->start, b->end - b->start); invalidate_tlb(); a->end = b->start; if(a->start == a->end) { del_vma_region(a); } if(new->start >= new->end) { kfree((unsigned int)new); } else { insert_vma_region(new); } } } void free_vma_pages(struct vma *vma, unsigned int start, __size_t length) { unsigned int n, offset; unsigned int *pgdir, *pgtbl; unsigned int pde, pte; struct page *pg; int page; 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) { if (!(pgtbl[pte] & PAGE_NOALLOC)) { /* 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) { offset = start - vma->start + vma->offset + n * PAGE_SIZE; write_page(pg, vma->inode, offset, PAGE_SIZE); } kfree(P2V(pgtbl[pte]) & PAGE_MASK); } current->rss--; #ifdef CONFIG_SYSVIPC if(vma->object) { shm_rss--; } #endif /* CONFIG_SYSVIPC */ pgtbl[pte] = 0; /* 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] = 0; } } } } } void release_binary(void) { struct vma *vma, *tmp; vma = current->vma_table; while(vma) { tmp = vma->next; free_vma_pages(vma, vma->start, vma->end - vma->start); free_vma_region(vma, vma->start, vma->end - vma->start); vma = tmp; } invalidate_tlb(); } struct vma *find_vma_region(unsigned int addr) { struct vma *vma; if(!addr) { return NULL; } addr &= PAGE_MASK; vma = current->vma_table; while(vma) { if((addr >= vma->start) && (addr < vma->end)) { return vma; } vma = vma->next; } return NULL; } struct vma *find_vma_intersection(unsigned int start, unsigned int end) { struct vma *vma; vma = current->vma_table; while(vma) { if(end <= vma->start) { break; } if(start < vma->end) { return vma; } vma = vma->next; } return NULL; } int expand_heap(unsigned int new) { struct vma *vma, *heap; vma = current->vma_table; heap = NULL; while(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 */ } vma = vma->next; } /* out of memory! */ return 1; } /* return the first free address that matches with the size of length */ unsigned int get_unmapped_vma_region(unsigned int length) { unsigned int addr; struct vma *vma; if(!length) { return 0; } addr = MMAP_START; vma = current->vma_table; while(vma) { if(vma->start < MMAP_START) { vma = vma->next; continue; } if(vma->start - addr >= length) { return PAGE_ALIGN(addr); } addr = PAGE_ALIGN(vma->end); vma = vma->next; } return 0; } 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, void *object) { struct vma *vma; int errno; if(!(length = PAGE_ALIGN(length))) { return start; } if(start > PAGE_OFFSET || start + length > PAGE_OFFSET) { return -EINVAL; } /* 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; } /* anonymous mapping */ } else { if((flags & MAP_TYPE) != MAP_PRIVATE) { return -EINVAL; } /* anonymous objects must be filled with zeros */ flags |= ZERO_PAGE; #ifdef CONFIG_SYSVIPC /* ... except for SHM regions */ if(type == P_SHM) { flags &= ~ZERO_PAGE; } #endif /* CONFIG_SYSVIPC */ } 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 = (struct vma *)kmalloc(sizeof(struct vma)))) { return -ENOMEM; } memset_b(vma, 0, sizeof(struct vma)); 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; #ifdef CONFIG_SYSVIPC vma->object = (struct shmid_ds *)object; #endif /* CONFIG_SYSVIPC */ do_munmap(start, length); /* clear old maps */ if(i && i->fsop->mmap) { if((errno = i->fsop->mmap(i, vma))) { free_vma_region(vma, start, length); kfree((unsigned int)vma); return errno; } } add_vma_region(vma); return start; } int do_munmap(unsigned int addr, __size_t length) { struct vma *vma; unsigned int size; if(addr & ~PAGE_MASK) { return -EINVAL; } length = PAGE_ALIGN(length); while(length) { if(!(vma = find_vma_region(addr))) { break; } if((addr + length) > vma->end) { size = vma->end - addr; } else { size = length; } free_vma_pages(vma, addr, size); invalidate_tlb(); free_vma_region(vma, addr, size); length -= size; addr += size; } return 0; } int do_mprotect(struct vma *vma, unsigned int addr, __size_t length, int prot) { struct vma *new; if(!(new = (struct vma *)kmalloc(sizeof(struct vma)))) { return -ENOMEM; } memset_b(new, 0, sizeof(struct vma)); 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; add_vma_region(new); return 0; } ================================================ FILE: mm/page.c ================================================ /* * fiwix/mm/page.c * * Copyright 2018-2022, 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 #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, 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, 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 insert_on_free_list(struct page *pg) { 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++; } static void remove_from_free_list(struct page *pg) { if(!kstat.free_pages) { return; } 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; } if(!kstat.free_pages) { page_head = NULL; } } void page_lock(struct page *pg) { unsigned int flags; for(;;) { SAVE_FLAGS(flags); CLI(); if(pg->flags & PAGE_LOCKED) { RESTORE_FLAGS(flags); sleep(pg, PROC_UNINTERRUPTIBLE); } else { break; } } pg->flags |= PAGE_LOCKED; RESTORE_FLAGS(flags); } void page_unlock(struct page *pg) { unsigned int flags; SAVE_FLAGS(flags); CLI(); pg->flags &= ~PAGE_LOCKED; wakeup(pg); RESTORE_FLAGS(flags); } struct page *get_free_page(void) { unsigned int flags; struct page *pg; repeat: /* if the number of pages is low then reclaim some buffers */ if(kstat.free_pages <= kstat.min_free_pages) { /* reclaim memory from buffer cache */ wakeup(&kswapd); if(!kstat.free_pages) { sleep(&get_free_page, PROC_UNINTERRUPTIBLE); if(!kstat.free_pages) { if(kstat.pages_reclaimed) { goto repeat; } /* definitely out of memory! (no more pages) */ printk("WARNING: %s(): out of memory and swapping is not implemented yet, sorry.\n", __FUNCTION__); printk("%s(): pid %d ran out of memory. OOM killer needed!\n", __FUNCTION__, current->pid); return NULL; } } /* this reduces the number of iterations */ if(kstat.min_free_pages > NR_BUF_RECLAIM) { kstat.min_free_pages -= NR_BUF_RECLAIM; } } else { /* recalculate if free memory is back to normal levels */ if(kstat.min_free_pages <= NR_BUF_RECLAIM) { if(kstat.free_pages > NR_BUF_RECLAIM) { kstat.min_free_pages = (kstat.total_mem_pages * FREE_PAGES_RATIO) / 100; } } } SAVE_FLAGS(flags); CLI(); if(!(pg = page_head)) { printk("WARNING: page_head returned NULL! (free_pages = %d)\n", kstat.free_pages); RESTORE_FLAGS(flags); return NULL; } remove_from_free_list(pg); remove_from_hash(pg); /* remove it from its old hash */ pg->count = 1; pg->inode = 0; pg->offset = 0; pg->dev = 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->inode && pg->offset == offset && pg->dev == inode->dev) { if(!pg->count) { remove_from_free_list(pg); } pg->count++; return pg; } pg = pg->next_hash; } return NULL; } void release_page(struct page *pg) { unsigned int flags; if(!is_valid_page(pg->page)) { PANIC("Unexpected inconsistency in hash_table. Missing page %d (0x%x).\n", pg->page, pg->page); } if(!pg->count) { printk("WARNING: %s(): trying to free an already freed page (%d)!\n", __FUNCTION__, pg->page); return; } if(--pg->count > 0) { return; } SAVE_FLAGS(flags); CLI(); insert_on_free_list(pg); /* remove all flags except PAGE_RESERVED */ pg->flags &= PAGE_RESERVED; /* if page is not cached then place it at the head of the free list */ if(!pg->inode) { page_head = pg; } RESTORE_FLAGS(flags); /* * We need to wait for free pages to be far greater than NR_BUF_RECLAIM, * otherwise get_free_pages() could run out of pages _again_, and it * would think that 'definitely there are no more free pages', killing * the current process prematurely. */ if(kstat.free_pages > (NR_BUF_RECLAIM * 3)) { wakeup(&get_free_page); } } int is_valid_page(int page) { return (page >= 0 && page < NR_PAGES); } void invalidate_inode_pages(struct inode *i) { struct page *pg; __off_t offset; for(offset = 0; offset < i->i_size; offset += PAGE_SIZE) { if((pg = search_page_hash(i, offset))) { page_lock(pg); release_page(pg); page_unlock(pg); remove_from_hash(pg); } } } 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 - 1); /* mod 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); } } } int write_page(struct page *pg, struct inode *i, __off_t offset, unsigned int length) { struct fd fdt; unsigned int size; int errno; size = MIN(i->i_size - offset, length); fdt.inode = i; fdt.flags = 0; fdt.count = 0; fdt.offset = offset; if(i->fsop && i->fsop->write) { errno = i->fsop->write(i, &fdt, 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, retval; struct device *d; struct blk_request brh, *br, *tmp; blksize = i->sb->s_blocksize; retval = size_read = 0; tmp = NULL; if(!(d = get_device(BLK_DEV, i->dev))) { printk("WARNING: %s(): device major %d not found!\n", __FUNCTION__, MAJOR(i->dev)); return 1; } memset_b(&brh, 0, sizeof(struct blk_request)); page_lock(pg); /* cache any read-only or public (shared) pages */ if(!(prot & PROT_WRITE) || flags & MAP_SHARED) { pg->inode = i->inode; pg->offset = offset; pg->dev = i->dev; insert_to_hash(pg); } while(size_read < PAGE_SIZE) { if(!(br = (struct blk_request *)kmalloc(sizeof(struct blk_request)))) { printk("WARNING: %s(): no more free memory for block requests.\n", __FUNCTION__); retval = 1; break; } if((block = bmap(i, offset + size_read, FOR_READING)) < 0) { retval = 1; break; } memset_b(br, 0, sizeof(struct blk_request)); br->dev = i->dev; br->block = block; br->flags = block ? 0 : BRF_NOBLOCK; br->size = blksize; br->device = d; br->fn = d->fsop->read_block; br->head_group = &brh; if(!brh.next_group) { brh.next_group = br; } else { tmp->next_group = br; } tmp = br; size_read += blksize; } if(!retval) { retval = gbread(d, &brh); } /* * We must zero retval if is not negative because block drivers * that still don't use the new I/O mechanism will return the * bytes read, and this value could be interpreted below as an error. */ retval = retval < 0 ? retval : 0; br = brh.next_group; size_read = 0; while(br) { if(!retval) { if(br->block) { memcpy_b(pg->data + size_read, br->buffer->data, br->size); br->buffer->flags |= BUFFER_VALID; } else { /* fill the hole with zeros */ memset_b(pg->data + size_read, 0, br->size); } size_read += br->size; } if(br->block) { brelse(br->buffer); } tmp = br->next_group; kfree((unsigned int)br); br = tmp; } page_unlock(pg); return retval; } int file_read(struct inode *i, struct fd *f, char *buffer, __size_t count) { __size_t total_read; unsigned int addr, poffset, bytes; struct page *pg; inode_lock(i); if(f->offset > i->i_size) { f->offset = i->i_size; } total_read = 0; for(;;) { count = (f->offset + count > i->i_size) ? i->i_size - f->offset : count; if(!count) { break; } poffset = f->offset & (PAGE_SIZE - 1); /* mod PAGE_SIZE */ if(!(pg = search_page_hash(i, f->offset & PAGE_MASK))) { if(!(addr = kmalloc(PAGE_SIZE))) { inode_unlock(i); printk("%s(): returning -ENOMEM\n", __FUNCTION__); return -ENOMEM; } pg = &page_table[V2P(addr) >> PAGE_SHIFT]; if(bread_page(pg, i, f->offset & PAGE_MASK, 0, MAP_SHARED)) { kfree(addr); inode_unlock(i); printk("%s(): returning -EIO\n", __FUNCTION__); return -EIO; } } else { addr = (unsigned int)pg->data; } 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; f->offset += bytes; kfree(addr); page_unlock(pg); } inode_unlock(i); return total_read; } void reserve_pages(unsigned int from, unsigned int to) { struct page *pg; while(from < to) { pg = &page_table[from >> PAGE_SHIFT]; pg->data = NULL; pg->flags = PAGE_RESERVED; kstat.physical_reserved++; remove_from_hash(pg); remove_from_free_list(pg); from += PAGE_SIZE; } /* recalculate */ kstat.total_mem_pages = kstat.free_pages; kstat.min_free_pages = (kstat.total_mem_pages * FREE_PAGES_RATIO) / 100; } void page_init(int pages) { struct page *pg; unsigned int n, addr; memset_b(page_table, 0, page_table_size); memset_b(page_hash_table, 0, page_hash_table_size); for(n = 0; n < pages; n++) { pg = &page_table[n]; pg->page = n; addr = n << PAGE_SHIFT; if(addr >= KERNEL_ADDR && addr < V2P(_last_data_addr)) { pg->flags = PAGE_RESERVED; kstat.kernel_reserved++; continue; } /* reserve the kernel stack page */ if(addr == 0x0000F000) { pg->flags = PAGE_RESERVED; kstat.physical_reserved++; continue; } /* * Some memory addresses are reserved, like the memory between * 0xA0000 and 0x100000 and other addresses, mostly used by the * VGA graphics adapter and BIOS. */ if(!is_addr_in_bios_map(addr)) { pg->flags = PAGE_RESERVED; kstat.physical_reserved++; continue; } pg->data = (char *)P2V(addr); insert_on_free_list(pg); } kstat.total_mem_pages = kstat.free_pages; kstat.min_free_pages = (kstat.total_mem_pages * FREE_PAGES_RATIO) / 100; } ================================================ FILE: mm/swapper.c ================================================ /* * fiwix/mm/swapper.c * * Copyright 2018-2022, 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 /* kswapd continues the kernel initialization */ int kswapd(void) { STI(); #ifdef CONFIG_SYSVIPC ipc_init(); #endif /* CONFIG_SYSVIPC */ /* char devices */ memdev_init(); serial_init(); lp_init(); #ifdef CONFIG_UNIX98_PTYS pty_init(); #endif /* CONFIG_UNIX98_PTYS */ /* network */ #ifdef CONFIG_NET net_init(); #endif /* CONFIG_NET */ /* block devices */ ramdisk_init(); floppy_init(); ata_init(); /* starting system */ mem_stats(); fs_init(); mount_root(); init_init(); /* make sure interrupts are enabled after initializing devices */ STI(); for(;;) { sleep(&kswapd, PROC_INTERRUPTIBLE); if((kstat.pages_reclaimed = reclaim_buffers())) { continue; } wakeup(&get_free_page); } } ================================================ FILE: net/Makefile ================================================ # fiwix/net/Makefile # # Copyright 2023, Jordi Sanfeliu. All rights reserved. # Distributed under the terms of the Fiwix License. # .c.o: $(CC) $(CFLAGS) -c -o $@ $< OBJS = domains.o socket.o packet.o core.o unix.o ipv4.o all: $(OBJS) clean: rm -f *.o ================================================ FILE: net/core.c ================================================ /* * fiwix/net/core.c * * Copyright 2025, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #ifdef CONFIG_NET int dev_ioctl(int cmd, void *arg) { int n; switch(cmd) { default: return -EINVAL; } } #endif /* CONFIG_NET */ ================================================ FILE: net/domains.c ================================================ /* * fiwix/net/domains.c * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #ifdef CONFIG_NET struct domain_table domains[] = { { AF_UNIX, "AF_UNIX", &unix_ops }, { AF_INET, "AF_INET", &ipv4_ops }, { 0, 0, 0 } }; struct proto_ops unix_ops = { unix_create, unix_free, unix_bind, unix_listen, unix_connect, unix_accept, unix_getname, unix_socketpair, unix_send, unix_recv, unix_sendto, unix_recvfrom, unix_read, unix_write, unix_ioctl, unix_select, unix_shutdown, unix_setsockopt, unix_getsockopt, unix_init, }; struct proto_ops ipv4_ops = { ipv4_create, ipv4_free, ipv4_bind, ipv4_listen, ipv4_connect, ipv4_accept, ipv4_getname, ipv4_socketpair, ipv4_send, ipv4_recv, ipv4_sendto, ipv4_recvfrom, ipv4_read, ipv4_write, ipv4_ioctl, ipv4_select, ipv4_shutdown, ipv4_setsockopt, ipv4_getsockopt, ipv4_init, }; int assign_proto(struct socket *so, int domain) { struct domain_table *d; d = &domains[0]; while(d->ops) { if(d->family == domain) { so->ops = d->ops; return 0; } d++; } return -EINVAL; } void net_init(void) { struct domain_table *d; struct proto_ops *ops; d = &domains[0]; while(d->ops) { ops = d->ops; ops->init(); d++; } /* call to the external TCP/IP API */ ext_init(); } #endif /* CONFIG_NET */ ================================================ FILE: net/ipv4.c ================================================ /* * fiwix/net/ipv4.c * * Copyright 2025, 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 #ifdef CONFIG_NET struct ipv4_info *ipv4_socket_head; static struct resource packet_resource = { 0, 0 }; static void add_ipv4_socket(struct ipv4_info *ip4) { struct ipv4_info *h; if((h = ipv4_socket_head)) { while(h->next) { h = h->next; } h->next = ip4; } else { ipv4_socket_head = ip4; } } static void remove_ipv4_socket(struct ipv4_info *ip4) { struct ipv4_info *h; if(ipv4_socket_head == ip4) { ipv4_socket_head = ip4->next; return; } h = ipv4_socket_head; while(h && h->next != ip4) { h = h->next; } if(h && h->next == ip4) { h->next = ip4->next; } } int ipv4_create(struct socket *s, int domain, int type, int protocol) { int fd; struct ipv4_info *ip4; if((fd = ext_open(domain, type, protocol)) < 0) { return fd; } s->fd_ext = fd; ip4 = &s->u.ipv4_info; memset_b(ip4, 0, sizeof(struct ipv4_info)); ip4->count = 1; ip4->socket = s; add_ipv4_socket(ip4); return fd; } void ipv4_free(struct socket *s) { int errno; struct ipv4_info *ip4; if((errno = ext_close(s->fd_ext)) < 0) { return errno; } s->fd_ext = 0; ip4 = &s->u.ipv4_info; remove_ipv4_socket(ip4); return errno; } int ipv4_bind(struct socket *s, const struct sockaddr *addr, int addrlen) { return ext_bind(s->fd_ext, addr, addrlen); } int ipv4_listen(struct socket *s, int backlog) { return ext_listen(s->fd_ext, backlog); } int ipv4_connect(struct socket *s, const struct sockaddr *addr, int addrlen) { return ext_connect(s->fd_ext, addr, addrlen); } int ipv4_accept(struct socket *s, struct sockaddr *addr, unsigned int *addrlen) { int fd, ufd; struct socket *sc; struct ipv4_info *ip4; if((fd = ext_accept(s->fd_ext, addr, addrlen)) < 0) { return fd; } sc = NULL; if((ufd = sock_alloc(&sc)) < 0) { return ufd; } sc->type = s->type; sc->ops = s->ops; sc->fd_ext = fd; ip4 = &s->u.ipv4_info; memset_b(ip4, 0, sizeof(struct ipv4_info)); ip4->count = 1; ip4->socket = sc; add_ipv4_socket(ip4); return ufd; } int ipv4_getname(struct socket *s, struct sockaddr *addr, unsigned int *addrlen, int call) { return -EOPNOTSUPP; } int ipv4_socketpair(struct socket *s1, struct socket *s2) { return -EOPNOTSUPP; } int ipv4_send(struct socket *s, struct fd *f, const char *buffer, __size_t count, int flags) { if(flags & ~MSG_DONTWAIT) { return -EINVAL; } return ipv4_write(s, f, buffer, count); } int ipv4_recv(struct socket *s, struct fd *f, char *buffer, __size_t count, int flags) { if(flags & ~MSG_DONTWAIT) { return -EINVAL; } return ipv4_read(s, f, buffer, count); } int ipv4_sendto(struct socket *s, struct fd *f, const char *buffer, __size_t count, int flags, const struct sockaddr *addr, int addrlen) { return ext_sendto(s->fd_ext, buffer, count, addr, addrlen); } int ipv4_recvfrom(struct socket *s, struct fd *f, char *buffer, __size_t count, int flags, struct sockaddr *addr, int *addrlen) { return ext_recvfrom(s->fd_ext, buffer, count, addr, addrlen); } int ipv4_read(struct socket *s, struct fd *f, char *buffer, __size_t count) { return ext_read(s->fd_ext, buffer, count); } int ipv4_write(struct socket *s, struct fd *f, const char *buffer, __size_t count) { return ext_write(s->fd_ext, buffer, count); } int ipv4_ioctl(struct socket *s, struct fd *f, int cmd, unsigned int arg) { int errno; if((errno = ext_ioctl(s->fd_ext, cmd, (void *)arg)) < 0) { switch(cmd) { default: errno = dev_ioctl(cmd, (void *)arg); break; } } return errno; } int ipv4_select(struct socket *s, int flag) { return -EOPNOTSUPP; } int ipv4_shutdown(struct socket *s, int how) { return -EOPNOTSUPP; } int ipv4_setsockopt(struct socket *s, int level, int optname, const void *optval, socklen_t optlen) { return -EOPNOTSUPP; } int ipv4_getsockopt(struct socket *s, int level, int optname, void *optval, socklen_t *optlen) { return -EOPNOTSUPP; } int ipv4_init(void) { ipv4_socket_head = NULL; return 0; } #endif /* CONFIG_NET */ ================================================ FILE: net/packet.c ================================================ /* * fiwix/net/packet.c * * Copyright 2024, Jordi Sanfeliu. All rights reserved. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #ifdef CONFIG_NET struct packet *peek_packet(struct packet *queue_head) { return queue_head; } struct packet *remove_packet_from_queue(struct packet **queue_head) { struct packet *p; if((p = *queue_head)) { *queue_head = (*queue_head)->next; } return p; } void append_packet_to_queue(struct packet *p, struct packet **queue_head) { struct packet *h; if((h = *queue_head)) { while(h->next) { h = h->next; } h->next = p; } else { *queue_head = p; } } #endif /* CONFIG_NET */ ================================================ FILE: net/socket.c ================================================ /* * fiwix/net/socket.c * * Copyright 2023, 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 #ifdef __DEBUG__ #include #endif /*__DEBUG__ */ #ifdef CONFIG_NET static int check_sd(int sd) { struct inode *i; CHECK_UFD(sd); i = fd_table[current->fd[sd]].inode; if(!i || !S_ISSOCK(i->i_mode)) { return -ENOTSOCK; } return 0; } static struct socket *get_socket(int fd) { struct inode *i; i = fd_table[current->fd[fd]].inode; return &i->u.sockfs.sock; } struct socket *get_socket_from_queue(struct socket *ss) { unsigned int flags; struct socket *sc; sc = NULL; SAVE_FLAGS(flags); CLI(); if((sc = ss->queue_head)) { ss->queue_head = sc->next_queue; ss->queue_len--; } RESTORE_FLAGS(flags); return sc; } /* append a socket to the list of pending connections */ int insert_socket_to_queue(struct socket *ss, struct socket *sc) { unsigned int flags; struct socket *s; if(ss->queue_len + 1 > ss->queue_limit) { printk("WARNING: backlog exceeded!\n"); return -ECONNREFUSED; } SAVE_FLAGS(flags); CLI(); if((s = ss->queue_head)) { while(s->next_queue) { s = s->next_queue; } s->next_queue = sc; } else { ss->queue_head = sc; } RESTORE_FLAGS(flags); ss->queue_len++; return 0; } int sock_alloc(struct socket **s) { int fd, ufd; struct filesystems *fs; struct inode *i; struct socket *ns; if(!(fs = get_filesystem("sockfs"))) { printk("WARNING: %s(): sockfs filesystem is not registered!\n", __FUNCTION__); return -EINVAL; } if(!(i = ialloc(&fs->mp->sb, S_IFSOCK))) { return -EINVAL; } if((fd = get_new_fd(i)) < 0) { iput(i); return -ENFILE; } if((ufd = get_new_user_fd(0)) < 0) { release_fd(fd); iput(i); return -EMFILE; } current->fd[ufd] = fd; i = fd_table[fd].inode; ns = &i->u.sockfs.sock; ns->state = SS_UNCONNECTED; fd_table[fd].flags = O_RDWR; ns->fd = &fd_table[fd]; *s = ns; return ufd; } void sock_free(struct socket *s) { int fd, ufd, n; struct inode *i; ufd = -1; /* pointer arithmetic */ fd = ((unsigned int)s->fd - (unsigned int)&fd_table[0]) / sizeof(struct fd); for(n = 0; n < OPEN_MAX; n++) { if(current->fd[n] == fd) { ufd = n; break; } } if(ufd >= 0) { release_user_fd(ufd); } if(!(--fd_table[fd].count)) { i = s->fd->inode; iput(i); release_fd(fd); } if(s->ops) { s->ops->free(s); } wakeup(s); } int socket(int domain, int type, int protocol) { int ufd; struct socket *s; int errno; #ifdef __DEBUG__ printk("(pid %d) socket(%d, %d, %d)\n", current->pid, domain, type, protocol); #endif /*__DEBUG__ */ if(type != SOCK_STREAM && type != SOCK_DGRAM) { return -EINVAL; } s = NULL; if((ufd = sock_alloc(&s)) < 0) { return ufd; } s->type = type; if(assign_proto(s, domain)) { sock_free(s); return -EINVAL; } if((errno = s->ops->create(s, domain, type, protocol)) < 0) { sock_free(s); return errno; } return ufd; } int bind(int sd, struct sockaddr *addr, int addrlen) { struct socket *s; int errno; #ifdef __DEBUG__ printk("(pid %d) bind(%d, 0x%08x, %d)\n", current->pid, sd, (int)addr, addrlen); #endif /*__DEBUG__ */ if((errno = check_sd(sd)) < 0) { return errno; } s = get_socket(sd); if((errno = check_user_area(VERIFY_READ, addr, addrlen))) { return errno; } return s->ops->bind(s, addr, addrlen); } int listen(int sd, int backlog) { struct socket *ss; int errno; #ifdef __DEBUG__ printk("(pid %d) listen(%d, %d)\n", current->pid, sd, backlog); #endif /*__DEBUG__ */ if((errno = check_sd(sd)) < 0) { return errno; } ss = get_socket(sd); if(ss->type != SOCK_STREAM) { return -EOPNOTSUPP; } ss->flags |= SO_ACCEPTCONN; backlog = backlog < 0 ? 0 : backlog; ss->queue_limit = MIN(backlog, SOMAXCONN); return ss->ops->listen(ss, backlog); } int connect(int sd, struct sockaddr *addr, int addrlen) { struct socket *sc; int errno; #ifdef __DEBUG__ printk("(pid %d) connect(%d, 0x%08x, %d)\n", current->pid, sd, (int)addr, addrlen); #endif /*__DEBUG__ */ if((errno = check_sd(sd)) < 0) { return errno; } sc = get_socket(sd); if((errno = check_user_area(VERIFY_READ, addr, addrlen))) { return errno; } return sc->ops->connect(sc, addr, addrlen); } int accept(int sd, struct sockaddr *addr, unsigned int *addrlen) { struct socket *ss; int errno; #ifdef __DEBUG__ printk("(pid %d) accept(%d, 0x%08x, 0x%08x)\n", current->pid, sd, (int)addr, addrlen); #endif /*__DEBUG__ */ if((errno = check_sd(sd)) < 0) { return errno; } ss = get_socket(sd); if(!(ss->flags & SO_ACCEPTCONN)) { return -EINVAL; } if(ss->type != SOCK_STREAM) { return -EOPNOTSUPP; } return ss->ops->accept(ss, addr, addrlen); } int getname(int sd, struct sockaddr *addr, unsigned int *addrlen, int call) { struct socket *s; int errno; #ifdef __DEBUG__ if(call == SYS_GETSOCKNAME) { printk("(pid %d) getsockname(%d, 0x%08x, 0x%08x)\n", current->pid, sd, (int)addr, addrlen); } else { printk("(pid %d) getpeername(%d, 0x%08x, 0x%08x)\n", current->pid, sd, (int)addr, addrlen); } #endif /*__DEBUG__ */ if((errno = check_sd(sd)) < 0) { return errno; } s = get_socket(sd); return s->ops->getname(s, addr, addrlen, call); } int socketpair(int domain, int type, int protocol, int sockfd[2]) { int ufd1, ufd2; struct socket *s1, *s2; int errno; #ifdef __DEBUG__ printk("(pid %d) socketpair(%d, %d, %d, 0x%08x)\n", current->pid, domain, type, protocol, sockfd); #endif /*__DEBUG__ */ if((errno = check_user_area(VERIFY_WRITE, sockfd, sizeof(int) * 2))) { return errno; } /* create first socket */ s1 = NULL; if((ufd1 = sock_alloc(&s1)) < 0) { return ufd1; } s1->type = type; if(assign_proto(s1, domain)) { sock_free(s1); return -EINVAL; } /* check if socketpair() is supported by the domain */ if(!s1->ops->socketpair) { sock_free(s1); return -EINVAL; } if((errno = s1->ops->create(s1, domain, type, protocol)) < 0) { sock_free(s1); return errno; } /* create second socket */ s2 = NULL; if((ufd2 = sock_alloc(&s2)) < 0) { sock_free(s1); return ufd2; } s2->type = type; assign_proto(s2, domain); if((errno = s2->ops->create(s2, domain, type, protocol)) < 0) { sock_free(s1); sock_free(s2); return errno; } if((errno = s1->ops->socketpair(s1, s2)) < 0) { sock_free(s1); sock_free(s2); return errno; } sockfd[0] = ufd1; sockfd[1] = ufd2; return 0; } int send(int sd, const void *buf, __size_t len, int flags) { struct socket *s; struct fd fdt; int errno; #ifdef __DEBUG__ printk("(pid %d) send(%d, 0x%08x, %d, %d)\n", current->pid, sd, (int)buf, len, flags); #endif /*__DEBUG__ */ if((errno = check_sd(sd)) < 0) { return errno; } s = get_socket(sd); if((errno = check_user_area(VERIFY_READ, buf, len))) { return errno; } fdt.flags = s->fd->flags | ((flags & MSG_DONTWAIT) ? O_NONBLOCK : 0); return s->ops->send(s, &fdt, buf, len, flags); } int recv(int sd, void *buf, __size_t len, int flags) { struct socket *s; struct fd fdt; int errno; #ifdef __DEBUG__ printk("(pid %d) recv(%d, 0x%08x, %d, %d)\n", current->pid, sd, (int)buf, len, flags); #endif /*__DEBUG__ */ if((errno = check_sd(sd)) < 0) { return errno; } s = get_socket(sd); if((errno = check_user_area(VERIFY_WRITE, buf, len))) { return errno; } fdt.flags = s->fd->flags | ((flags & MSG_DONTWAIT) ? O_NONBLOCK : 0); return s->ops->recv(s, &fdt, buf, len, flags); } int sendto(int sd, const void *buf, __size_t len, int flags, const struct sockaddr *addr, int addrlen) { struct socket *s; struct fd fdt; int errno; #ifdef __DEBUG__ printk("(pid %d) sendto(%d, 0x%08x, %d, %d, 0x%08x, %d)\n", current->pid, sd, (int)buf, len, flags, (int)addr, addrlen); #endif /*__DEBUG__ */ if((errno = check_sd(sd)) < 0) { return errno; } s = get_socket(sd); if((errno = check_user_area(VERIFY_READ, buf, len))) { return errno; } fdt.flags = s->fd->flags | ((flags & MSG_DONTWAIT) ? O_NONBLOCK : 0); return s->ops->sendto(s, &fdt, buf, len, flags, addr, addrlen); } int recvfrom(int sd, void *buf, __size_t len, int flags, struct sockaddr *addr, int *addrlen) { struct socket *s; struct fd fdt; char ret_addr[108]; int errno, ret_len, bytes_read; #ifdef __DEBUG__ printk("(pid %d) recvfrom(%d, 0x%08x, %d, %d, 0x%08x, 0x%08x)\n", current->pid, sd, (int)buf, len, flags, (int)addr, addrlen); #endif /*__DEBUG__ */ if((errno = check_sd(sd)) < 0) { return errno; } s = get_socket(sd); if((errno = check_user_area(VERIFY_WRITE, buf, len))) { return errno; } fdt.flags = s->fd->flags | ((flags & MSG_DONTWAIT) ? O_NONBLOCK : 0); memset_b(ret_addr, 0, 108); if((errno = s->ops->recvfrom(s, &fdt, buf, len, flags, (struct sockaddr *)ret_addr, &ret_len)) < 0) { return errno; } bytes_read = errno; if(ret_len && addr) { if((errno = check_user_area(VERIFY_WRITE, addr, ret_len))) { return errno; } memcpy_b(addr, ret_addr, ret_len); } return bytes_read; } int shutdown(int sd, int how) { struct socket *s; int errno; #ifdef __DEBUG__ printk("(pid %d) shutdown(%d, %d)\n", current->pid, sd, how); #endif /*__DEBUG__ */ if((errno = check_sd(sd)) < 0) { return errno; } s = get_socket(sd); return s->ops->shutdown(s, how); } int setsockopt(int sd, int level, int optname, const void *optval, socklen_t optlen) { struct socket *s; int errno; #ifdef __DEBUG__ printk("(pid %d) setsockopt(%d, %d, %d, %x, %d)\n", current->pid, sd, level, optname, (int)optval, optlen); #endif /*__DEBUG__ */ if((errno = check_sd(sd)) < 0) { return errno; } s = get_socket(sd); return s->ops->setsockopt(s, level, optname, optval, optlen); } int getsockopt(int sd, int level, int optname, void *optval, socklen_t *optlen) { struct socket *s; int errno; #ifdef __DEBUG__ printk("(pid %d) getsockopt(%d, %d, %d, %x, %x)\n", current->pid, sd, level, optname, (int)optval, (int)optlen); #endif /*__DEBUG__ */ if((errno = check_sd(sd)) < 0) { return errno; } s = get_socket(sd); return s->ops->getsockopt(s, level, optname, optval, optlen); } #endif /* CONFIG_NET */ ================================================ FILE: net/unix.c ================================================ /* * fiwix/net/unix.c * * Copyright 2023, Jordi Sanfeliu. All rights reserved. * Portions Copyright 2024, Greg Haerr. * Distributed under the terms of the Fiwix License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_NET struct unix_info *unix_socket_head; static struct resource packet_resource = { 0, 0 }; static void add_unix_socket(struct unix_info *u) { struct unix_info *h; if((h = unix_socket_head)) { while(h->next) { h = h->next; } h->next = u; } else { unix_socket_head = u; } } static void remove_unix_socket(struct unix_info *u) { struct unix_info *h; if(unix_socket_head == u) { unix_socket_head = u->next; return; } h = unix_socket_head; while(h && h->next != u) { h = h->next; } if(h && h->next == u) { h->next = u->next; } } static struct unix_info *lookup_unix_socket(char *path, struct inode *i) { struct unix_info *u; u = unix_socket_head; while(u) { if(u->sun) { if(!strcmp(u->sun->sun_path, path) && u->inode == i) { return u; } } u = u->next; } return NULL; } int unix_create(struct socket *s, int domain, int type, int protocol) { struct unix_info *u; u = &s->u.unix_info; memset_b(u, 0, sizeof(struct unix_info)); u->count = 1; u->socket = s; add_unix_socket(u); return 0; } void unix_free(struct socket *s) { struct unix_info *u; u = &s->u.unix_info; if(!(--u->count)) { if(u->data) { kfree((unsigned int)u->data); } if(u->sun) { kfree((unsigned int)u->sun); } if(u->inode) { iput(u->inode); } u->peer = NULL; remove_unix_socket(u); return; } if(u->peer) { if(!--u->peer->count) { remove_unix_socket(u->peer); } if(u->peer->socket) { u->peer->socket->state = SS_DISCONNECTING; } wakeup(u->peer); wakeup(&do_select); } remove_unix_socket(u); return; } int unix_bind(struct socket *s, const struct sockaddr *addr, int addrlen) { struct inode *i; struct sockaddr_un *su; int errno; su = (struct sockaddr_un *)addr; if(su->sun_family != AF_UNIX) { return -EINVAL; } if(addrlen < 0 || addrlen > sizeof(struct sockaddr_un)) { return -EINVAL; } if(s->u.unix_info.sun) { return -EINVAL; } if(!(s->u.unix_info.sun = (struct sockaddr_un *)kmalloc(sizeof(struct sockaddr_un)))) { return -ENOMEM; } memset_b(s->u.unix_info.sun, 0, sizeof(struct sockaddr_un)); memcpy_b(s->u.unix_info.sun, su, addrlen); s->u.unix_info.sun_len = addrlen; errno = do_mknod((char *)su->sun_path, S_IFSOCK | (S_IRWXU | S_IRWXG | S_IRWXO), 0); if(errno < 0) { kfree((unsigned int)s->u.unix_info.sun); s->u.unix_info.sun = NULL; if(errno == -EEXIST) { errno = -EADDRINUSE; } return errno; } if((errno = namei(su->sun_path, &i, NULL, FOLLOW_LINKS))) { kfree((unsigned int)s->u.unix_info.sun); s->u.unix_info.sun = NULL; return errno; } s->u.unix_info.inode = i; return errno; } int unix_listen(struct socket *s, int backlog) { return 0; } int unix_connect(struct socket *sc, const struct sockaddr *addr, int addrlen) { struct inode *i; struct sockaddr_un *su; struct unix_info *up; char *tmp_name; int errno; su = (struct sockaddr_un *)addr; if(su->sun_family != AF_UNIX) { return -EINVAL; } if(addrlen < 0 || addrlen > sizeof(struct sockaddr_un)) { return -EINVAL; } if((errno = malloc_name(su->sun_path, &tmp_name)) < 0) { return errno; } if((errno = namei(tmp_name, &i, NULL, FOLLOW_LINKS))) { free_name(tmp_name); return errno; } if(!(up = lookup_unix_socket(tmp_name, i))) { iput(i); free_name(tmp_name); return -ECONNREFUSED; } iput(i); free_name(tmp_name); if((errno = insert_socket_to_queue(up->socket, sc))) { return errno; } wakeup(up->socket); sleep(sc, PROC_INTERRUPTIBLE); return 0; } int unix_accept(struct socket *ss, struct sockaddr *addr, unsigned int *addrlen) { int ufd; struct socket *sc, *nss; struct unix_info *uc, *us; int errno; while(!(sc = get_socket_from_queue(ss))) { if(ss->fd->flags & O_NONBLOCK) { return -EAGAIN; } if(sleep(ss, PROC_INTERRUPTIBLE)) { return -EINTR; } } nss = NULL; if((ufd = sock_alloc(&nss)) < 0) { return ufd; } nss->type = ss->type; nss->ops = ss->ops; if((errno = nss->ops->create(nss, 0, 0, 0)) < 0) { sock_free(nss); return errno; } uc = &sc->u.unix_info; us = &nss->u.unix_info; if(!(uc->data = (char *)kmalloc(PIPE_BUF))) { sock_free(nss); return -ENOMEM; } us->data = uc->data; us->sun = uc->sun; us->sun_len = uc->sun_len; us->peer = uc; us->count++; uc->peer = us; /* server socket */ uc->count++; sc->state = SS_CONNECTED; nss->state = SS_CONNECTED; wakeup(sc); wakeup(&do_select); if(addr) { nss->ops->getname(nss, addr, addrlen, SYS_GETPEERNAME); } return ufd; } int unix_getname(struct socket *s, struct sockaddr *addr, unsigned int *addrlen, int call) { struct unix_info *u; int len, errno; if((errno = check_user_area(VERIFY_WRITE, addrlen, sizeof(int)))) { return errno; } len = *addrlen; if(call == SYS_GETSOCKNAME) { u = &s->u.unix_info; } else { /* SYS_GETPEERNAME */ u = s->u.unix_info.peer; } if(len > u->sun_len) { len = u->sun_len; } if(len) { if((errno = check_user_area(VERIFY_WRITE, addr, len))) { return errno; } memcpy_b(addr, u->sun, len); } return 0; } int unix_socketpair(struct socket *s1, struct socket *s2) { struct unix_info *u1, *u2; u1 = &s1->u.unix_info; u2 = &s2->u.unix_info; if(!(u1->data = (char *)kmalloc(PIPE_BUF))) { return -ENOMEM; } u2->data = u1->data; u1->count++; u2->count++; u1->peer = u2; u2->peer = u1; s1->state = SS_CONNECTED; s2->state = SS_CONNECTED; return 0; } int unix_send(struct socket *s, struct fd *f, const char *buffer, __size_t count, int flags) { if(flags & ~MSG_DONTWAIT) { return -EINVAL; } return unix_write(s, f, buffer, count); } int unix_recv(struct socket *s, struct fd *f, char *buffer, __size_t count, int flags) { if(flags & ~MSG_DONTWAIT) { return -EINVAL; } return unix_read(s, f, buffer, count); } int unix_sendto(struct socket *s, struct fd *f, const char *buffer, __size_t count, int flags, const struct sockaddr *addr, int addrlen) { struct inode *i; struct unix_info *u; struct sockaddr_un *su; struct packet *p; char *tmp_name; int errno; su = (struct sockaddr_un *)addr; if(su->sun_family != AF_UNIX) { return -EINVAL; } if(addrlen < 0 || addrlen > sizeof(struct sockaddr_un)) { return -EINVAL; } if((errno = malloc_name(su->sun_path, &tmp_name)) < 0) { return errno; } if((errno = namei(tmp_name, &i, NULL, FOLLOW_LINKS))) { free_name(tmp_name); return errno; } if(!(u = lookup_unix_socket(tmp_name, i))) { iput(i); free_name(tmp_name); return -ECONNREFUSED; } iput(i); free_name(tmp_name); if(!(p = (struct packet *)kmalloc(sizeof(struct packet)))) { return -ENOMEM; } memset_b(p, 0, sizeof(struct packet)); if(!(p->data = (char *)kmalloc(count + 1))) { kfree((unsigned int)p); return -ENOMEM; } memset_b(p->data, 0, count + 1); memcpy_b(p->data, buffer, count); p->len = count; p->socket = s; lock_resource(&packet_resource); append_packet_to_queue(p, &u->packet_queue); unlock_resource(&packet_resource); wakeup(u); return count; } int unix_recvfrom(struct socket *s, struct fd *f, char *buffer, __size_t count, int flags, struct sockaddr *addr, int *addrlen) { struct unix_info *u, *up; struct sockaddr_un *sun; struct packet *p; int size; u = &s->u.unix_info; lock_resource(&packet_resource); while(!(p = peek_packet(u->packet_queue))) { unlock_resource(&packet_resource); if(!(f->flags & O_NONBLOCK)) { if(sleep(u, PROC_INTERRUPTIBLE)) { return -EINTR; } lock_resource(&packet_resource); } else { unlock_resource(&packet_resource); return -EAGAIN; } } size = MIN(p->len - p->offset, count); memcpy_b(buffer, p->data + p->offset, size); p->offset += size; if(!(flags & MSG_PEEK)) { p = remove_packet_from_queue(&u->packet_queue); kfree((unsigned int)p->data); kfree((unsigned int)p); } unlock_resource(&packet_resource); up = &p->socket->u.unix_info; sun = (struct sockaddr_un *)addr; sun->sun_family = AF_UNIX; memcpy_b(sun->sun_path, up->sun->sun_path, up->sun_len); *addrlen = up->sun_len; return size; } int unix_read(struct socket *s, struct fd *f, char *buffer, __size_t count) { struct unix_info *u; int bytes_read; int n, limit; u = &s->u.unix_info; bytes_read = 0; while(count) { if(u->writeoff) { if(u->readoff >= u->writeoff) { limit = PIPE_BUF - u->readoff; } else { limit = u->writeoff - u->readoff; } } else { limit = PIPE_BUF - u->readoff; } n = MIN(limit, count); if(u->size && n) { memcpy_b(buffer + bytes_read, u->data + u->readoff, n); bytes_read += n; u->readoff += n; u->size -= n; count -= n; if(u->writeoff == PIPE_BUF) { u->writeoff = 0; } wakeup(u->peer); wakeup(&do_select); } else { if(s->state != SS_CONNECTED) { if(s->state == SS_DISCONNECTING) { if(u->size) { if(u->readoff == PIPE_BUF) { u->readoff = 0; } continue; } return bytes_read; } return -EINVAL; } if(u->writeoff) { break; } if(f->flags & O_NONBLOCK) { return -EAGAIN; } if(sleep(u, PROC_INTERRUPTIBLE)) { return -EINTR; } } } if(!u->size) { u->readoff = u->writeoff = 0; } return bytes_read; } int unix_write(struct socket *s, struct fd *f, const char *buffer, __size_t count) { struct unix_info *u, *up; int bytes_written; int n, limit; u = &s->u.unix_info; up = s->u.unix_info.peer; bytes_written = 0; while(bytes_written < count) { if(s->state != SS_CONNECTED) { if(s->state == SS_DISCONNECTING) { send_sig(current, SIGPIPE); return -EPIPE; } return -EINVAL; } if(up->readoff) { if(up->writeoff <= up->readoff) { limit = up->readoff; } else { limit = PIPE_BUF; } } else { limit = PIPE_BUF; } n = MIN((count - bytes_written), (limit - up->writeoff)); if(n && n <= PIPE_BUF) { memcpy_b(up->data + up->writeoff, buffer + bytes_written, n); bytes_written += n; up->writeoff += n; up->size += n; if(up->readoff == PIPE_BUF) { up->readoff = 0; } wakeup(u->peer); wakeup(&do_select); continue; } wakeup(u->peer); wakeup(&do_select); if(!(f->flags & O_NONBLOCK)) { if(sleep(u, PROC_INTERRUPTIBLE)) { return -EINTR; } } else { return -EAGAIN; } } return bytes_written; } int unix_ioctl(struct socket *s, struct fd *f, int cmd, unsigned int arg) { int errno; switch(cmd) { default: errno = dev_ioctl(cmd, (void *)arg); break; } return errno; } int unix_select(struct socket *s, int flag) { struct unix_info *u, *up; if(s->flags & SO_ACCEPTCONN) { if (flag == SEL_R && s->queue_len) { return 1; } return 0; } u = &s->u.unix_info; up = s->u.unix_info.peer; switch(flag) { case SEL_R: if(u->size) { return 1; } if(s->state != SS_CONNECTED) { printk("UNIX: select: socket not connected (read EOF)\n"); return 1; } break; case SEL_W: if(s->state != SS_CONNECTED) { printk("UNIX: select: socket not connected (write EOF)\n"); return 1; } if(up->size < PIPE_BUF) { return 1; } break; } return 0; } int unix_shutdown(struct socket *s, int how) { return -EOPNOTSUPP; } int unix_setsockopt(struct socket *s, int level, int optname, const void *optval, socklen_t optlen) { return -EOPNOTSUPP; } int unix_getsockopt(struct socket *s, int level, int optname, void *optval, socklen_t *optlen) { return -EOPNOTSUPP; } int unix_init(void) { unix_socket_head = NULL; return 0; } #endif /* CONFIG_NET */ ================================================ FILE: shell.nix ================================================ with import { }; pkgsCross.i686-embedded.stdenv.mkDerivation { name = "env"; }